def setUp(self):
        """I'm called at the beginning of each test"""
        self.q = DeviceQueue()
        self.finished = False
        self.failed = None
        self.pending_deferreds = []

        # safety timeout
        self.timeout = reactor.callLater(10, self.timedOut)
class TestDeviceQueue(unittest.TestCase):

    def setUp(self):
        """I'm called at the beginning of each test"""
        self.q = DeviceQueue()
        self.finished = False
        self.failed = None
        self.pending_deferreds = []

        # safety timeout
        self.timeout = reactor.callLater(10, self.timedOut)


    def tearDown(self):
        """I'm called at the end of each test"""

        # spin here until all pending deferreds have fired (might
        # be overridden by time out of safety deferred)
        while len(self.pending_deferreds):
            if debug:
                print "test tearDown, waiting on %d deferreds" % len(self.pending_deferreds)
            reactor.iterate(0.2)


    def timedOut(self):
        """I'm called when the safety timer expires indicating test probably won't complete"""

        # FIXME: how do we cancel this test and cleanup remaining deferreds?
        self.pending_deferreds = []

        if debug:
            "**** timedOut callback, test did not complete"
        self.fail("Safety timeout callback ... test did not complete")
        reactor.crash()


    def successfulTest(self, returnvalue, d):
        """I'm a 'success' callback that allows a reactor.iterate loop to finish"""
        if debug:
            print "**** successfulTest callback: ", returnvalue, d
        assert d in self.pending_deferreds
        self.finished = True  # important
        self.failed = False  # important
        self.pending_deferreds.remove(d)
        if len(self.pending_deferreds) == 0:
            self.timeout.cancel()


    def failTest(self, returnvalue, d):
        """I'm a 'failure' callback that allows a reactor.iterate loop to finish"""
        if debug:
            print "**** failTest callback: ", returnvalue, d
        self.finished = True  # important
        self.failed = True  # important
        self.pending_deferreds.remove(d)
        if len(self.pending_deferreds) == 0:
            self.timeout.cancel()


    def test_single_submission(self):
        """Submit single command to an empty queue"""

        # core API to test, the DeviceQueue
        d = self.q.submit(delay, 2)
        assert isinstance(d, defer.Deferred)
        d.addCallback(self.successfulTest, d)
        d.addErrback(self.failTest,d)

        self.pending_deferreds.append(d)

        while not self.finished:  # waiting for a deferred to fire ...
            reactor.iterate(0.2)
            if debug:
                print "test_queued_deferred, waiting on: ", self.failed, self.finished, len(self.pending_deferreds)
        
        # possibly set in failTest callback
        if self.failed:
            self.fail("Single submission test failed")



    def test_queued_commands(self):
        """Queued commands block and finish in order"""

        # core API to test, the DeviceQueue
        d1 = self.q.submit(delay, 4)
        d1.addCallback(self.successfulTest, d1)
        d1.addErrback(self.failTest,d1)

        d2 = self.q.submit(delay, 2)
        assert isinstance(d2, defer.Deferred)
        d2.addCallback(self.failTest, d2)
        d2.addErrback(self.failTest,d2)

        self.pending_deferreds.extend([d1,d2])

        while not self.finished:  # waiting for a deferred to fire ...
            reactor.iterate(0.2)
            if debug:
                print "test_queued_deferred, waiting on: ", self.failed, self.finished, len(self.pending_deferreds)
        
        # possibly set in failTest callback
        if self.failed:
            self.fail("Second submission finished first")



    def test_exception_wont_stall_queue(self):
        """Device exception shouldn't stall the queue"""

        d1 = self.q.submit(raise_exception, 1)
        d1.addCallback(self.successfulTest, d1)
        d1.addErrback(self.failTest,d1)
        
        d2 = self.q.submit(delay, 1)
        d2.addCallback(self.successfulTest, d2)
        d2.addErrback(self.failTest,d2)

        self.pending_deferreds.extend([d1,d2])

        while not self.finished:  # waiting for a deferred to fire ...
            reactor.iterate(0.2)
            if debug:
                print "test_queued_deferred, waiting on: ", self.failed, self.finished, len(self.pending_deferreds)



    if False:
        test_single_submission.skip = True
        test_queued_commands.skip = True
        test_exception_wont_stall_queue.skip = True