# Path: ebu_tt_live/utils.py
# class RingBufferWithCallback(collections.deque):
#     """
#     This class calls a callback when an item is falling out of the buffer due to removal.
#     On manual removal it does not. That is the user's responsibility.
#     """
# 
#     _callback = None
# 
#     def __init__(self, iterable=(), maxlen=None, callback=None):
#         if callback is not None and not callable(callback):
#             raise ValueError('Callback: {} is not callable'.format(callback))
#         self._callback = callback
#         super(RingBufferWithCallback, self).__init__(iterable, maxlen)
# 
#     def append(self, item):
#         if len(self) >= self.maxlen:
#             if self._callback is not None:
#                 self._callback(self.popleft())
#         super(RingBufferWithCallback, self).append(item)
# 
# class RotatingFileBuffer(RingBufferWithCallback):
#     """
#     This class holds the given number of file names and when they are pushed out of the buffer it deletes
#     them asynchronously. Preferably just the names and not open file handles.
#     """
# 
#     _deletion_thread = None
#     _deletion_queue = None
# 
#     def __init__(self, maxlen, async=True):
#         super(RotatingFileBuffer, self).__init__(maxlen=maxlen, callback=self.delete_file)
#         # In this case threads make sense since it is I/O we are going to be waiting for and that is releasing the GIL.
#         # Deletion is the means for us to send down files for deletion to the other thread(maybe process later)....
#         self._deletion_queue = Queue.Queue()
#         if async is True:
#             self._deletion_thread = StoppableThread(
#                 target=self._delete_thread_loop,
#                 kwargs={'q': self._deletion_queue}
#             )
#             # Ensuring the thread will not leave us hanging
#             self._deletion_thread.daemon = True
#             self._deletion_thread.start()
# 
#     @classmethod
#     def _do_delete(cls, files_waiting):
#         failed_files = []
#         # Now we can try to see if we have anything to delete
#         while files_waiting:
#             item = files_waiting.pop()
#             full_path = os.path.abspath(item)
#             if os.path.exists(item):
#                 # File is still there try to delete it
#                 # If not we do nothing. The loop discards the name
#                 try:
#                     os.remove(full_path)
#                 except IOError:
#                     # Horrible! Quick, put it back... NEXT
#                     failed_files.append(item)
# 
#         return failed_files
# 
#     @classmethod
#     def _do_consume(cls, q, files_waiting, default_wait):
#         try:
#             files_waiting.append(q.get(timeout=default_wait))
#         except Queue.Empty:
#             pass
# 
#         failed_files = cls._do_delete(files_waiting)
#         return failed_files
# 
#     @classmethod
#     def _delete_thread_loop(cls, q):
#         files_waiting = []
#         default_wait = 0.2
#         while not threading.current_thread().stopped() or not q.empty():
#             files_waiting = cls._do_consume(q=q, files_waiting=files_waiting, default_wait=default_wait)
#             time.sleep(0.1)
#         while files_waiting:
#             files_waiting = cls._do_delete(files_waiting=files_waiting)
#             time.sleep(0.1)
# 
#     def delete_file(self, item):
#         """
#         This function hands the file down to our worker thread to deal with it.
#         :param item:
#         :return:
#         """
#         self._deletion_queue.put(item)
#         if self._deletion_thread is None:
#             files_waiting = []
#             default_wait = 0.1
#             while files_waiting or not self._deletion_queue.empty():
#                 files_waiting = self._do_consume(
#                     q=self._deletion_queue,
#                     files_waiting=files_waiting,
#                     default_wait=default_wait
#                 )
# 
#     def append(self, item):
#         """
#         This override makes sure that we don't add to an asynchronously managed buffer that is about to be shut down.
#         :param item: The file name
#         :return:
#         """
#         if self._deletion_thread is not None:
#             if self._deletion_thread.stopped():
#                 raise RotatingFileBufferStopped('File deletion thread is stopped!')
#         super(RotatingFileBuffer, self).append(item)
# 
# class RotatingFileBufferStopped(Exception):
#     pass


from unittest import TestCase
from mock import MagicMock
from ebu_tt_live.utils import RingBufferWithCallback, RotatingFileBuffer, RotatingFileBufferStopped
import tempfile
import os
import time
        file1 = self._create_a_file(1)
        file2 = self._create_a_file(2)
        file3 = self._create_a_file(3)
        self.instance.append(file1)
        self.instance.append(file2)
        self.instance.append(file3)
        self._assert_exists(file1)
        self._assert_exists(file2)
        self._assert_exists(file3)

    def test_four_files(self):
        file1 = self._create_a_file(1)
        file2 = self._create_a_file(2)
        file3 = self._create_a_file(3)
        file4 = self._create_a_file(4)
        self.instance.append(file1)
        self.instance.append(file2)
        self.instance.append(file3)
        self.instance.append(file4)

        self._assert_not_exists(file1)
        self._assert_exists(file2)
        self._assert_exists(file3)
        self._assert_exists(file4)


class TestRotatingFileBufferAsync(RFBCommon):

    def setUp(self):
        super(TestRotatingFileBufferAsync, self).setUp()

# Path: ebu_tt_live/utils.py
# class RingBufferWithCallback(collections.deque):
#     """
#     This class calls a callback when an item is falling out of the buffer due to removal.
#     On manual removal it does not. That is the user's responsibility.
#     """
# 
#     _callback = None
# 
#     def __init__(self, iterable=(), maxlen=None, callback=None):
#         if callback is not None and not callable(callback):
#             raise ValueError('Callback: {} is not callable'.format(callback))
#         self._callback = callback
#         super(RingBufferWithCallback, self).__init__(iterable, maxlen)
# 
#     def append(self, item):
#         if len(self) >= self.maxlen:
#             if self._callback is not None:
#                 self._callback(self.popleft())
#         super(RingBufferWithCallback, self).append(item)
# 
# class RotatingFileBuffer(RingBufferWithCallback):
#     """
#     This class holds the given number of file names and when they are pushed out of the buffer it deletes
#     them asynchronously. Preferably just the names and not open file handles.
#     """
# 
#     _deletion_thread = None
#     _deletion_queue = None
# 
#     def __init__(self, maxlen, async=True):
#         super(RotatingFileBuffer, self).__init__(maxlen=maxlen, callback=self.delete_file)
#         # In this case threads make sense since it is I/O we are going to be waiting for and that is releasing the GIL.
#         # Deletion is the means for us to send down files for deletion to the other thread(maybe process later)....
#         self._deletion_queue = Queue.Queue()
#         if async is True:
#             self._deletion_thread = StoppableThread(
#                 target=self._delete_thread_loop,
#                 kwargs={'q': self._deletion_queue}
#             )
#             # Ensuring the thread will not leave us hanging
#             self._deletion_thread.daemon = True
#             self._deletion_thread.start()
# 
#     @classmethod
#     def _do_delete(cls, files_waiting):
#         failed_files = []
#         # Now we can try to see if we have anything to delete
#         while files_waiting:
#             item = files_waiting.pop()
#             full_path = os.path.abspath(item)
#             if os.path.exists(item):
#                 # File is still there try to delete it
#                 # If not we do nothing. The loop discards the name
#                 try:
#                     os.remove(full_path)
#                 except IOError:
#                     # Horrible! Quick, put it back... NEXT
#                     failed_files.append(item)
# 
#         return failed_files
# 
#     @classmethod
#     def _do_consume(cls, q, files_waiting, default_wait):
#         try:
#             files_waiting.append(q.get(timeout=default_wait))
#         except Queue.Empty:
#             pass
# 
#         failed_files = cls._do_delete(files_waiting)
#         return failed_files
# 
#     @classmethod
#     def _delete_thread_loop(cls, q):
#         files_waiting = []
#         default_wait = 0.2
#         while not threading.current_thread().stopped() or not q.empty():
#             files_waiting = cls._do_consume(q=q, files_waiting=files_waiting, default_wait=default_wait)
#             time.sleep(0.1)
#         while files_waiting:
#             files_waiting = cls._do_delete(files_waiting=files_waiting)
#             time.sleep(0.1)
# 
#     def delete_file(self, item):
#         """
#         This function hands the file down to our worker thread to deal with it.
#         :param item:
#         :return:
#         """
#         self._deletion_queue.put(item)
#         if self._deletion_thread is None:
#             files_waiting = []
#             default_wait = 0.1
#             while files_waiting or not self._deletion_queue.empty():
#                 files_waiting = self._do_consume(
#                     q=self._deletion_queue,
#                     files_waiting=files_waiting,
#                     default_wait=default_wait
#                 )
# 
#     def append(self, item):
#         """
#         This override makes sure that we don't add to an asynchronously managed buffer that is about to be shut down.
#         :param item: The file name
#         :return:
#         """
#         if self._deletion_thread is not None:
#             if self._deletion_thread.stopped():
#                 raise RotatingFileBufferStopped('File deletion thread is stopped!')
#         super(RotatingFileBuffer, self).append(item)
# 
# class RotatingFileBufferStopped(Exception):
#     pass


# Path: ebu_tt_live/test/test_utils.py
from unittest import TestCase
from mock import MagicMock
from ebu_tt_live.utils import RingBufferWithCallback, RotatingFileBuffer, RotatingFileBufferStopped
import tempfile
import os
import time

        file1 = self._create_a_file(1)
        file2 = self._create_a_file(2)
        file3 = self._create_a_file(3)
        self.instance.append(file1)
        self.instance.append(file2)
        self.instance.append(file3)
        self._assert_exists(file1)
        self._assert_exists(file2)
        self._assert_exists(file3)

    def test_four_files(self):
        file1 = self._create_a_file(1)
        file2 = self._create_a_file(2)
        file3 = self._create_a_file(3)
        file4 = self._create_a_file(4)
        self.instance.append(file1)
        self.instance.append(file2)
        self.instance.append(file3)
        self.instance.append(file4)

        self._assert_not_exists(file1)
        self._assert_exists(file2)
        self._assert_exists(file3)
        self._assert_exists(file4)


class TestRotatingFileBufferAsync(RFBCommon):

    def setUp(self):
        super(TestRotatingFileBufferAsync, self).setUp()

        self.instance = RotatingFileBuffer(maxlen=3)