コード例 #1
0
 def test_del(self):
     with mock.patch('anki.storage.Collection') as anki_storage_Collection:
         col = anki_storage_Collection.return_value
         wrapper = CollectionWrapper(self.collection_path)
         wrapper.open()
         wrapper = None
         col.close.assert_called_with()
コード例 #2
0
    def __init__(self, path, setup_new_collection=None):
        self.path = path
        self.wrapper = CollectionWrapper(path, setup_new_collection)

        self._queue = Queue()
        self._thread = None
        self._running = False
        self.last_timestamp = time.time()

        self.start()
コード例 #3
0
    def test_setup_func(self):
        # Run it when the collection doesn't exist
        with mock.patch('anki.storage.Collection') as anki_storage_Collection:
            col = anki_storage_Collection.return_value
            setup_new_collection = MagicMock()
            self.assertFalse(os.path.exists(self.collection_path))
            wrapper = CollectionWrapper(self.collection_path, setup_new_collection)
            wrapper.open()
            anki_storage_Collection.assert_called_with(self.collection_path)
            setup_new_collection.assert_called_with(col)
            wrapper = None

        # Make sure that no collection was actually created
        self.assertFalse(os.path.exists(self.collection_path))
        
        # Create a faux collection file
        with file(self.collection_path, 'wt') as fd:
            fd.write('Collection!')

        # Run it when the collection does exist
        with mock.patch('anki.storage.Collection'):
            setup_new_collection = lambda col: self.fail("Setup function called when collection already exists!")
            self.assertTrue(os.path.exists(self.collection_path))
            wrapper = CollectionWrapper(self.collection_path, setup_new_collection)
            wrapper.open()
            anki_storage_Collection.assert_called_with(self.collection_path)
            wrapper = None
コード例 #4
0
    def test_execute(self):
        with mock.patch('anki.storage.Collection') as anki_storage_Collection:
            col = anki_storage_Collection.return_value
            func = MagicMock()
            func.return_value = sentinel.some_object

            # check that execute works and auto-creates the collection
            wrapper = CollectionWrapper(self.collection_path)
            ret = wrapper.execute(func, [1, 2, 3], {'key': 'aoeu'})
            self.assertEqual(ret, sentinel.some_object)
            anki_storage_Collection.assert_called_with(self.collection_path)
            func.assert_called_with(col, 1, 2, 3, key='aoeu')

            # check that execute always returns False if waitForReturn=False
            func.reset_mock()
            ret = wrapper.execute(func, [1, 2, 3], {'key': 'aoeu'}, waitForReturn=False)
            self.assertEqual(ret, None)
            func.assert_called_with(col, 1, 2, 3, key='aoeu')
コード例 #5
0
    def test_lifecycle(self):
        with mock.patch('AnkiServer.collection.CollectionManager.collection_wrapper') as CollectionWrapper:
            wrapper = MagicMock()
            CollectionWrapper.return_value = wrapper

            manager = CollectionManager()

            # check getting a new collection
            ret = manager.get_collection('path1')
            CollectionWrapper.assert_called_with(os.path.realpath('path1'), None)
            self.assertEqual(ret, wrapper)

            # change the return value, so that it would return a new object
            new_wrapper = MagicMock()
            CollectionWrapper.return_value = new_wrapper
            CollectionWrapper.reset_mock()

            # get the new wrapper
            ret = manager.get_collection('path2')
            CollectionWrapper.assert_called_with(os.path.realpath('path2'), None)
            self.assertEqual(ret, new_wrapper)

            # make sure the wrapper and new_wrapper are different
            self.assertNotEqual(wrapper, new_wrapper)

            # assert that calling with the first path again, returns the first wrapper
            ret = manager.get_collection('path1')
            self.assertEqual(ret, wrapper)

            manager.shutdown()
            wrapper.close.assert_called_with()
            new_wrapper.close.assert_called_with()
コード例 #6
0
class ThreadingCollectionWrapper(object):
    """Provides the same interface as CollectionWrapper, but it creates a new Thread to 
    interact with the collection."""
    def __init__(self, path, setup_new_collection=None):
        self.path = path
        self.wrapper = CollectionWrapper(path, setup_new_collection)

        self._queue = Queue()
        self._thread = None
        self._running = False
        self.last_timestamp = time.time()

        self.start()

    @property
    def running(self):
        return self._running

    def qempty(self):
        return self._queue.empty()

    def current(self):
        from threading import current_thread
        return current_thread() == self._thread

    def execute(self, func, args=[], kw={}, waitForReturn=True):
        """ Executes a given function on this thread with the *args and **kw.

        If 'waitForReturn' is True, then it will block until the function has
        executed and return its return value.  If False, it will return None
        immediately and the function will be executed sometime later.
        """

        if waitForReturn:
            return_queue = Queue()
        else:
            return_queue = None

        self._queue.put((func, args, kw, return_queue))

        if return_queue is not None:
            ret = return_queue.get(True)
            if isinstance(ret, Exception):
                raise ret
            return ret

    def _run(self):
        logging.info('CollectionThread[%s]: Starting...', self.path)

        try:
            while self._running:
                func, args, kw, return_queue = self._queue.get(True)

                if hasattr(func, 'func_name'):
                    func_name = func.func_name
                else:
                    func_name = func.__class__.__name__

                logging.info('CollectionThread[%s]: Running %s(*%s, **%s)',
                             self.path, func_name, repr(args), repr(kw))
                self.last_timestamp = time.time()

                try:
                    ret = self.wrapper.execute(func, args, kw, return_queue)
                except Exception as e:
                    logging.error(
                        'CollectionThread[%s]: Unable to %s(*%s, **%s): %s',
                        self.path,
                        func_name,
                        repr(args),
                        repr(kw),
                        e,
                        exc_info=True)
                    # we return the Exception which will be raise'd on the other end
                    ret = e

                if return_queue is not None:
                    return_queue.put(ret)
        except Exception as e:
            logging.error(
                'CollectionThread[%s]: Thread crashed! Exception: %s',
                self.path,
                e,
                exc_info=True)
        finally:
            self.wrapper.close()
            # clean out old thread object
            self._thread = None
            # in case we got here via an exception
            self._running = False

            logging.info('CollectionThread[%s]: Stopped!', self.path)

    def start(self):
        if not self._running:
            self._running = True
            assert self._thread is None
            self._thread = Thread(target=self._run)
            self._thread.start()

    def stop(self):
        def _stop(col):
            self._running = False

        self.execute(_stop, waitForReturn=False)

    def stop_and_wait(self):
        """ Tell the thread to stop and wait for it to happen. """
        self.stop()
        if self._thread is not None:
            self._thread.join()

    #
    # Mimic the CollectionWrapper interface
    #

    def open(self):
        """Non-op. The collection will be opened on demand."""
        pass

    def close(self):
        """Closes the underlying collection without stopping the thread."""
        def _close(col):
            self.wrapper.close()

        self.execute(_close, waitForReturn=False)

    def opened(self):
        return self.wrapper.opened()
コード例 #7
0
    def test_lifecycle_real(self):
        """Testing common life-cycle with existing and non-existant collections. This
        test uses the real Anki objects and actually creates a new collection on disk."""

        wrapper = CollectionWrapper(self.collection_path)
        self.assertFalse(os.path.exists(self.collection_path))
        self.assertFalse(wrapper.opened())

        wrapper.open()
        self.assertTrue(os.path.exists(self.collection_path))
        self.assertTrue(wrapper.opened())

        # calling open twice shouldn't break anything
        wrapper.open()

        wrapper.close()
        self.assertTrue(os.path.exists(self.collection_path))
        self.assertFalse(wrapper.opened())

        # open the same collection again (not a creation)
        wrapper = CollectionWrapper(self.collection_path)
        self.assertFalse(wrapper.opened())
        wrapper.open()
        self.assertTrue(wrapper.opened())
        wrapper.close()
        self.assertFalse(wrapper.opened())
        self.assertTrue(os.path.exists(self.collection_path))