1 import os, signal, time
3 from twisted.internet import reactor, defer, task
4 from twisted.python import failure
7 def flip_bit(good, which):
8 # flip the low-order bit of good[which]
10 pieces = good[:which], good[-1:], ""
12 pieces = good[:which], good[which:which+1], good[which+1:]
13 return pieces[0] + chr(ord(pieces[1]) ^ 0x01) + pieces[2]
16 # This class is necessary for any code which wants to use Processes
17 # outside the usual reactor.run() environment. It is copied from
18 # Twisted's twisted.test.test_process
22 # make sure SIGCHLD handler is installed, as it should be on
23 # reactor.run(). problem is reactor may not have been run when this
25 if hasattr(reactor, "_handleSigchld") and hasattr(signal, "SIGCHLD"):
26 self.sigchldHandler = signal.signal(signal.SIGCHLD,
27 reactor._handleSigchld)
29 def tearDownClass(self):
30 if self.sigchldHandler:
31 signal.signal(signal.SIGCHLD, self.sigchldHandler)
33 class TimeoutError(Exception):
38 def poll(self, check_f, pollinterval=0.01, timeout=None):
39 # Return a Deferred, then call check_f periodically until it returns
40 # True, at which point the Deferred will fire.. If check_f raises an
41 # exception, the Deferred will errback. If the check_f does not
42 # indicate success within timeout= seconds, the Deferred will
43 # errback. If timeout=None, no timeout will be enforced.
45 if timeout is not None:
46 cutoff = time.time() + timeout
47 stash = [] # ick. We have to pass the LoopingCall into itself
48 lc = task.LoopingCall(self._poll, check_f, stash, cutoff)
50 d = lc.start(pollinterval)
53 def _poll(self, check_f, stash, cutoff):
54 if cutoff is not None and time.time() > cutoff:
60 class ShouldFailMixin:
62 def shouldFail(self, expected_failure, which, substring, callable, *args, **kwargs):
63 assert substring is None or isinstance(substring, str)
64 d = defer.maybeDeferred(callable, *args, **kwargs)
66 if isinstance(res, failure.Failure):
67 res.trap(expected_failure)
69 self.failUnless(substring in str(res),
70 "substring '%s' not in '%s'"
71 % (substring, str(res)))
73 self.fail("%s was supposed to raise %s, not get '%s'" %
74 (which, expected_failure, res))
79 class TestMixin(SignalMixin):
80 def setUp(self, repeatable=False):
82 @param repeatable: install the repeatable_randomness hacks to attempt
83 to without access to real randomness and real time.time from the
86 self.repeatable = repeatable
88 import repeatable_random
89 repeatable_random.force_repeatability()
90 if hasattr(time, 'realtime'):
91 self.teststarttime = time.realtime()
93 self.teststarttime = time.time()
97 import repeatable_random
98 repeatable_random.restore_non_repeatability()
99 self.clean_pending(required_to_quiesce=True)
101 def clean_pending(self, dummy=None, required_to_quiesce=True):
103 This handy method cleans all pending tasks from the reactor.
105 When writing a unit test, consider the following question:
107 Is the code that you are testing required to release control once it
108 has done its job, so that it is impossible for it to later come around
109 (with a delayed reactor task) and do anything further?
111 If so, then trial will usefully test that for you -- if the code under
112 test leaves any pending tasks on the reactor then trial will fail it.
114 On the other hand, some code is *not* required to release control -- some
115 code is allowed to continuously maintain control by rescheduling reactor
116 tasks in order to do ongoing work. Trial will incorrectly require that
117 code to clean up all its tasks from the reactor.
119 Most people think that such code should be amended to have an optional
120 "shutdown" operation that releases all control, but on the contrary it is
121 good design for some code to *not* have a shutdown operation, but instead
122 to have a "crash-only" design in which it recovers from crash on startup.
124 If the code under test is of the "long-running" kind, which is *not*
125 required to shutdown cleanly in order to pass tests, then you can simply
126 call testutil.clean_pending() at the end of the unit test, and trial will
129 pending = reactor.getDelayedCalls()
130 active = bool(pending)
135 print "WEIRNESS! pending timed call not active+!"
136 if required_to_quiesce and active:
137 self.fail("Reactor was still active when it was required to be quiescent.")
142 def make_readonly(path):
143 win32file.SetFileAttributes(path, win32con.FILE_ATTRIBUTE_READONLY)
144 def make_accessible(path):
145 win32file.SetFileAttributes(path, win32con.FILE_ATTRIBUTE_NORMAL)
148 def _make_readonly(path):
149 os.chmod(path, stat.S_IREAD)
150 os.chmod(os.path.dirname(path), stat.S_IREAD)
151 def _make_accessible(path):
152 os.chmod(os.path.dirname(path), stat.S_IWRITE | stat.S_IEXEC | stat.S_IREAD)
153 os.chmod(path, stat.S_IWRITE | stat.S_IEXEC | stat.S_IREAD)
154 make_readonly = _make_readonly
155 make_accessible = _make_accessible