def test_done(self):
        limiter = TaskLimiter(2)
        w = ManualWritable()

        limiter.add(w.write, 1)
        limiter.add(w.write, 2)
        limiter.add(w.write, 3)
        limiter.add(w.write, 4)

        d = limiter.done()
        self.assertFalse(d.called)
        self.assertEqual(w.writing, [1, 2])
        self.assertEqual(w.written, [])

        yield w.next()
        self.assertFalse(d.called)
        self.assertEqual(w.writing, [2, 3])
        self.assertEqual(w.written, [1])

        yield w.next()
        self.assertFalse(d.called)
        self.assertEqual(w.writing, [3, 4])
        self.assertEqual(w.written, [1, 2])

        yield w.next()
        self.assertFalse(d.called)
        self.assertEqual(w.writing, [4])
        self.assertEqual(w.written, [1, 2, 3])

        yield w.next()
        self.assertTrue(d.called)
        self.assertEqual(w.writing, [])
        self.assertEqual(w.written, [1, 2, 3, 4])
 def test_err(self):
     w = ManualWritable()
     e = Exception()
     errs = []
     d = w.write(23)
     d.addErrback(lambda f: errs.append(f.value))
     yield w.err(e)
     self.assertEqual(errs, [e])
    def test_loop_retry_err(self):
        e1 = Exception()
        e3 = Exception()
        errors = self.patch_on_error()

        r = ManualReadable([1, 2, 3])
        w = ManualWritable()
        self.patch(RetrySenderWorker, 'next_req', staticmethod(r.read))
        self.patch(RetrySenderWorker, 'retry', staticmethod(w.write))

        yield self.mk_worker({
            'frequency': 5,
            'concurrency_limit': 2,
        })

        # We've read all three requests from redis and are busy retrying the
        # first two
        yield r.next()
        yield r.next()
        yield r.next()
        self.assertEqual(errors, [])
        self.assertEqual(r.unread, [])
        self.assertEqual(r.reading, [])
        self.assertEqual(w.writing, [1, 2])
        self.assertEqual(w.written, [])

        # Retry 1 throws an error, we catch it. We now have space for
        # request 3.
        yield w.err(e1)
        self.assertEqual(errors, [e1])
        self.assertEqual(r.unread, [])
        self.assertEqual(r.reading, [])
        self.assertEqual(w.writing, [2, 3])
        self.assertEqual(w.written, [])

        # Retry 2 succeeds.
        yield w.next()
        self.assertEqual(errors, [e1])
        self.assertEqual(r.unread, [])
        self.assertEqual(r.reading, [])
        self.assertEqual(w.writing, [3])
        self.assertEqual(w.written, [2])

        # Retry 3 throws an error, we catch it.
        yield w.err(e3)
        self.assertEqual(errors, [e1, e3])
        self.assertEqual(r.unread, [])
        self.assertEqual(r.reading, [])
        self.assertEqual(w.writing, [])
        self.assertEqual(w.written, [2])
    def test_add(self):
        limiter = TaskLimiter(2)
        w = ManualWritable()

        self.assertEqual(w.writing, [])
        self.assertEqual(w.written, [])

        yield limiter.add(w.write, 1)
        self.assertEqual(w.writing, [1])
        self.assertEqual(w.written, [])

        yield limiter.add(w.write, 2)
        self.assertEqual(w.writing, [1, 2])
        self.assertEqual(w.written, [])

        d3 = limiter.add(w.write, 3)
        self.assertEqual(w.writing, [1, 2])
        self.assertEqual(w.written, [])

        d4 = limiter.add(w.write, 4)
        self.assertEqual(w.writing, [1, 2])
        self.assertEqual(w.written, [])

        self.assertFalse(d3.called)
        yield w.next()
        yield d3
        self.assertEqual(w.writing, [2, 3])
        self.assertEqual(w.written, [1])

        self.assertFalse(d4.called)
        yield w.next()
        yield d4
        self.assertEqual(w.writing, [3, 4])
        self.assertEqual(w.written, [1, 2])

        yield w.next()
        self.assertEqual(w.writing, [4])
        self.assertEqual(w.written, [1, 2, 3])

        yield w.next()
        self.assertEqual(w.writing, [])
        self.assertEqual(w.written, [1, 2, 3, 4])
    def test_add_err(self):
        errs = []
        w = ManualWritable()
        limiter = TaskLimiter(2, lambda f: errs.append(f.value))

        e1, e2, e4 = [Exception() for i in range(3)]
        yield limiter.add(w.write, 1)
        yield limiter.add(w.write, 2)
        d3 = limiter.add(w.write, 3)
        d4 = limiter.add(w.write, 4)

        self.assertFalse(d3.called)
        self.assertFalse(d4.called)
        self.assertEqual(errs, [])
        self.assertEqual(w.writing, [1, 2])
        self.assertEqual(w.written, [])

        yield w.err(e1)
        yield d3
        self.assertEqual(errs, [e1])
        self.assertEqual(w.writing, [2, 3])
        self.assertEqual(w.written, [])

        yield w.err(e2)
        yield d4
        self.assertEqual(errs, [e1, e2])
        self.assertEqual(w.writing, [3, 4])
        self.assertEqual(w.written, [])

        yield w.next()
        self.assertEqual(w.writing, [4])
        self.assertEqual(w.written, [3])

        yield w.err(e4)
        self.assertEqual(errs, [e1, e2, e4])
        self.assertEqual(w.writing, [])
        self.assertEqual(w.written, [3])
    def test_writing(self):
        w = ManualWritable()
        self.assertEqual(w.writing, [])
        self.assertEqual(w.written, [])

        d1 = w.write(1)
        self.assertEqual(w.writing, [1])
        self.assertEqual(w.written, [])

        d2 = w.write(2)
        self.assertEqual(w.writing, [1, 2])
        self.assertEqual(w.written, [])

        self.assertEqual(d1, w.next())
        yield d1
        self.assertEqual(w.writing, [2])
        self.assertEqual(w.written, [1])

        self.assertEqual(d2, w.next())
        yield d2
        self.assertEqual(w.writing, [])
        self.assertEqual(w.written, [1, 2])

        d = w.write(3)
        self.assertEqual(w.writing, [3])
        self.assertEqual(w.written, [1, 2])
        self.assertEqual(d, w.next())
        yield d
        self.assertEqual(w.writing, [])
        self.assertEqual(w.written, [1, 2, 3])

        d = w.write(4)
        self.assertEqual(w.writing, [4])
        self.assertEqual(w.written, [1, 2, 3])
        self.assertEqual(d, w.next())
        yield d
        self.assertEqual(w.writing, [])
        self.assertEqual(w.written, [1, 2, 3, 4])
    def test_loop_concurrency_limit(self):
        r = ManualReadable([1, 2, 3, 4, 5])
        w = ManualWritable()
        self.patch(RetrySenderWorker, 'next_req', staticmethod(r.read))
        self.patch(RetrySenderWorker, 'retry', staticmethod(w.write))

        yield self.mk_worker({
            'frequency': 5,
            'concurrency_limit': 2,
        })

        # We haven't yet started any retries
        self.assertEqual(r.unread, [2, 3, 4, 5])
        self.assertEqual(r.reading, [1])
        self.assertEqual(w.writing, [])
        self.assertEqual(w.written, [])

        # We've started retrying request 1 and still have space
        yield r.next()
        self.assertEqual(r.unread, [3, 4, 5])
        self.assertEqual(r.reading, [2])
        self.assertEqual(w.writing, [1])
        self.assertEqual(w.written, [])

        # We've started retrying request 2 and are at capacity
        yield r.next()
        self.assertEqual(r.unread, [4, 5])
        self.assertEqual(r.reading, [3])
        self.assertEqual(w.writing, [1, 2])
        self.assertEqual(w.written, [])

        # We've read request 3 from redis but haven't retried it yet, since we
        # are waiting for request 1 and 2 to complete
        yield r.next()
        self.assertEqual(r.unread, [4, 5])
        self.assertEqual(r.reading, [])
        self.assertEqual(w.writing, [1, 2])
        self.assertEqual(w.written, [])

        # Request 1 has completed, so we have space to start retrying
        # request 3 and ask redis for request 4.
        yield w.next()
        self.assertEqual(r.unread, [5])
        self.assertEqual(r.reading, [4])
        self.assertEqual(w.writing, [2, 3])
        self.assertEqual(w.written, [1])

        # We've read request 4 from redis but haven't retried it yet, since we
        # are waiting for request 2 and 3 to complete
        yield r.next()
        self.assertEqual(r.unread, [5])
        self.assertEqual(r.reading, [])
        self.assertEqual(w.writing, [2, 3])
        self.assertEqual(w.written, [1])

        # Request 2 has completed, so we have space to start retrying
        # request 3 and ask redis for request 5.
        yield w.next()
        self.assertEqual(r.unread, [])
        self.assertEqual(r.reading, [5])
        self.assertEqual(w.writing, [3, 4])
        self.assertEqual(w.written, [1, 2])

        # Request 3 and 4 complete while we are waiting for request 5
        # from redis
        yield w.next()
        yield w.next()
        self.assertEqual(r.unread, [])
        self.assertEqual(r.reading, [5])
        self.assertEqual(w.writing, [])
        self.assertEqual(w.written, [1, 2, 3, 4])

        # We've read request 5 from redis and started retrying it
        yield r.next()
        self.assertEqual(r.unread, [])
        self.assertEqual(r.reading, [])
        self.assertEqual(w.writing, [5])
        self.assertEqual(w.written, [1, 2, 3, 4])

        # We've retried request 5. Redis says we have nothing more to read, so
        # we are done.
        yield w.next()
        self.assertEqual(r.unread, [])
        self.assertEqual(r.reading, [])
        self.assertEqual(w.writing, [])
        self.assertEqual(w.written, [1, 2, 3, 4, 5])