| OLD | NEW |
| (Empty) |
| 1 import os | |
| 2 | |
| 3 from zope.interface import implements | |
| 4 | |
| 5 from twisted.internet.interfaces import IProcessTransport | |
| 6 from twisted.internet import defer | |
| 7 from twisted.internet.base import DelayedCall | |
| 8 | |
| 9 from twisted.trial.unittest import TestCase | |
| 10 from twisted.trial import util | |
| 11 from twisted.trial.util import DirtyReactorAggregateError, _Janitor | |
| 12 from twisted.trial.test import packages | |
| 13 | |
| 14 | |
| 15 | |
| 16 class TestMktemp(TestCase): | |
| 17 def test_name(self): | |
| 18 name = self.mktemp() | |
| 19 dirs = os.path.dirname(name).split(os.sep)[:-1] | |
| 20 self.failUnlessEqual( | |
| 21 dirs, ['twisted.trial.test.test_util', 'TestMktemp', 'test_name']) | |
| 22 | |
| 23 def test_unique(self): | |
| 24 name = self.mktemp() | |
| 25 self.failIfEqual(name, self.mktemp()) | |
| 26 | |
| 27 def test_created(self): | |
| 28 name = self.mktemp() | |
| 29 dirname = os.path.dirname(name) | |
| 30 self.failUnless(os.path.exists(dirname)) | |
| 31 self.failIf(os.path.exists(name)) | |
| 32 | |
| 33 def test_location(self): | |
| 34 path = os.path.abspath(self.mktemp()) | |
| 35 self.failUnless(path.startswith(os.getcwd())) | |
| 36 | |
| 37 | |
| 38 class TestIntrospection(TestCase): | |
| 39 def test_containers(self): | |
| 40 import suppression | |
| 41 parents = util.getPythonContainers( | |
| 42 suppression.TestSuppression2.testSuppressModule) | |
| 43 expected = [suppression.TestSuppression2, suppression] | |
| 44 for a, b in zip(parents, expected): | |
| 45 self.failUnlessEqual(a, b) | |
| 46 | |
| 47 | |
| 48 class TestFindObject(packages.SysPathManglingTest): | |
| 49 def test_importPackage(self): | |
| 50 package1 = util.findObject('package') | |
| 51 import package as package2 | |
| 52 self.failUnlessEqual(package1, (True, package2)) | |
| 53 | |
| 54 def test_importModule(self): | |
| 55 test_sample2 = util.findObject('goodpackage.test_sample') | |
| 56 from goodpackage import test_sample | |
| 57 self.failUnlessEqual((True, test_sample), test_sample2) | |
| 58 | |
| 59 def test_importError(self): | |
| 60 self.failUnlessRaises(ZeroDivisionError, | |
| 61 util.findObject, 'package.test_bad_module') | |
| 62 | |
| 63 def test_sophisticatedImportError(self): | |
| 64 self.failUnlessRaises(ImportError, | |
| 65 util.findObject, 'package2.test_module') | |
| 66 | |
| 67 def test_importNonexistentPackage(self): | |
| 68 self.failUnlessEqual(util.findObject('doesntexist')[0], False) | |
| 69 | |
| 70 def test_findNonexistentModule(self): | |
| 71 self.failUnlessEqual(util.findObject('package.doesntexist')[0], False) | |
| 72 | |
| 73 def test_findNonexistentObject(self): | |
| 74 self.failUnlessEqual(util.findObject( | |
| 75 'goodpackage.test_sample.doesnt')[0], False) | |
| 76 self.failUnlessEqual(util.findObject( | |
| 77 'goodpackage.test_sample.AlphabetTest.doesntexist')[0], False) | |
| 78 | |
| 79 def test_findObjectExist(self): | |
| 80 alpha1 = util.findObject('goodpackage.test_sample.AlphabetTest') | |
| 81 from goodpackage import test_sample | |
| 82 self.failUnlessEqual(alpha1, (True, test_sample.AlphabetTest)) | |
| 83 | |
| 84 | |
| 85 | |
| 86 class TestRunSequentially(TestCase): | |
| 87 """ | |
| 88 Sometimes it is useful to be able to run an arbitrary list of callables, | |
| 89 one after the other. | |
| 90 | |
| 91 When some of those callables can return Deferreds, things become complex. | |
| 92 """ | |
| 93 | |
| 94 def test_emptyList(self): | |
| 95 """ | |
| 96 When asked to run an empty list of callables, runSequentially returns a | |
| 97 successful Deferred that fires an empty list. | |
| 98 """ | |
| 99 d = util._runSequentially([]) | |
| 100 d.addCallback(self.assertEqual, []) | |
| 101 return d | |
| 102 | |
| 103 | |
| 104 def test_singleSynchronousSuccess(self): | |
| 105 """ | |
| 106 When given a callable that succeeds without returning a Deferred, | |
| 107 include the return value in the results list, tagged with a SUCCESS | |
| 108 flag. | |
| 109 """ | |
| 110 d = util._runSequentially([lambda: None]) | |
| 111 d.addCallback(self.assertEqual, [(defer.SUCCESS, None)]) | |
| 112 return d | |
| 113 | |
| 114 | |
| 115 def test_singleSynchronousFailure(self): | |
| 116 """ | |
| 117 When given a callable that raises an exception, include a Failure for | |
| 118 that exception in the results list, tagged with a FAILURE flag. | |
| 119 """ | |
| 120 d = util._runSequentially([lambda: self.fail('foo')]) | |
| 121 def check(results): | |
| 122 [(flag, fail)] = results | |
| 123 fail.trap(self.failureException) | |
| 124 self.assertEqual(fail.getErrorMessage(), 'foo') | |
| 125 self.assertEqual(flag, defer.FAILURE) | |
| 126 return d.addCallback(check) | |
| 127 | |
| 128 | |
| 129 def test_singleAsynchronousSuccess(self): | |
| 130 """ | |
| 131 When given a callable that returns a successful Deferred, include the | |
| 132 result of the Deferred in the results list, tagged with a SUCCESS flag. | |
| 133 """ | |
| 134 d = util._runSequentially([lambda: defer.succeed(None)]) | |
| 135 d.addCallback(self.assertEqual, [(defer.SUCCESS, None)]) | |
| 136 return d | |
| 137 | |
| 138 | |
| 139 def test_singleAsynchronousFailure(self): | |
| 140 """ | |
| 141 When given a callable that returns a failing Deferred, include the | |
| 142 failure the results list, tagged with a FAILURE flag. | |
| 143 """ | |
| 144 d = util._runSequentially([lambda: defer.fail(ValueError('foo'))]) | |
| 145 def check(results): | |
| 146 [(flag, fail)] = results | |
| 147 fail.trap(ValueError) | |
| 148 self.assertEqual(fail.getErrorMessage(), 'foo') | |
| 149 self.assertEqual(flag, defer.FAILURE) | |
| 150 return d.addCallback(check) | |
| 151 | |
| 152 | |
| 153 def test_callablesCalledInOrder(self): | |
| 154 """ | |
| 155 Check that the callables are called in the given order, one after the | |
| 156 other. | |
| 157 """ | |
| 158 log = [] | |
| 159 deferreds = [] | |
| 160 | |
| 161 def append(value): | |
| 162 d = defer.Deferred() | |
| 163 log.append(value) | |
| 164 deferreds.append(d) | |
| 165 return d | |
| 166 | |
| 167 d = util._runSequentially([lambda: append('foo'), | |
| 168 lambda: append('bar')]) | |
| 169 | |
| 170 # runSequentially should wait until the Deferred has fired before | |
| 171 # running the second callable. | |
| 172 self.assertEqual(log, ['foo']) | |
| 173 deferreds[-1].callback(None) | |
| 174 self.assertEqual(log, ['foo', 'bar']) | |
| 175 | |
| 176 # Because returning created Deferreds makes jml happy. | |
| 177 deferreds[-1].callback(None) | |
| 178 return d | |
| 179 | |
| 180 | |
| 181 def test_continuesAfterError(self): | |
| 182 """ | |
| 183 If one of the callables raises an error, then runSequentially continues | |
| 184 to run the remaining callables. | |
| 185 """ | |
| 186 d = util._runSequentially([lambda: self.fail('foo'), lambda: 'bar']) | |
| 187 def check(results): | |
| 188 [(flag1, fail), (flag2, result)] = results | |
| 189 fail.trap(self.failureException) | |
| 190 self.assertEqual(flag1, defer.FAILURE) | |
| 191 self.assertEqual(fail.getErrorMessage(), 'foo') | |
| 192 self.assertEqual(flag2, defer.SUCCESS) | |
| 193 self.assertEqual(result, 'bar') | |
| 194 return d.addCallback(check) | |
| 195 | |
| 196 | |
| 197 def test_stopOnFirstError(self): | |
| 198 """ | |
| 199 If the C{stopOnFirstError} option is passed to C{runSequentially}, then | |
| 200 no further callables are called after the first exception is raised. | |
| 201 """ | |
| 202 d = util._runSequentially([lambda: self.fail('foo'), lambda: 'bar'], | |
| 203 stopOnFirstError=True) | |
| 204 def check(results): | |
| 205 [(flag1, fail)] = results | |
| 206 fail.trap(self.failureException) | |
| 207 self.assertEqual(flag1, defer.FAILURE) | |
| 208 self.assertEqual(fail.getErrorMessage(), 'foo') | |
| 209 return d.addCallback(check) | |
| 210 | |
| 211 | |
| 212 def test_stripFlags(self): | |
| 213 """ | |
| 214 If the C{stripFlags} option is passed to C{runSequentially} then the | |
| 215 SUCCESS / FAILURE flags are stripped from the output. Instead, the | |
| 216 Deferred fires a flat list of results containing only the results and | |
| 217 failures. | |
| 218 """ | |
| 219 d = util._runSequentially([lambda: self.fail('foo'), lambda: 'bar'], | |
| 220 stripFlags=True) | |
| 221 def check(results): | |
| 222 [fail, result] = results | |
| 223 fail.trap(self.failureException) | |
| 224 self.assertEqual(fail.getErrorMessage(), 'foo') | |
| 225 self.assertEqual(result, 'bar') | |
| 226 return d.addCallback(check) | |
| 227 test_stripFlags.todo = "YAGNI" | |
| 228 | |
| 229 | |
| 230 | |
| 231 class DirtyReactorAggregateErrorTest(TestCase): | |
| 232 """ | |
| 233 Tests for the L{DirtyReactorAggregateError}. | |
| 234 """ | |
| 235 | |
| 236 def test_formatDelayedCall(self): | |
| 237 """ | |
| 238 Delayed calls are formatted nicely. | |
| 239 """ | |
| 240 error = DirtyReactorAggregateError(["Foo", "bar"]) | |
| 241 self.assertEquals(str(error), | |
| 242 """\ | |
| 243 Reactor was unclean. | |
| 244 DelayedCalls: (set twisted.internet.base.DelayedCall.debug = True to debug) | |
| 245 Foo | |
| 246 bar""") | |
| 247 | |
| 248 | |
| 249 def test_formatSelectables(self): | |
| 250 """ | |
| 251 Selectables are formatted nicely. | |
| 252 """ | |
| 253 error = DirtyReactorAggregateError([], ["selectable 1", "selectable 2"]) | |
| 254 self.assertEquals(str(error), | |
| 255 """\ | |
| 256 Reactor was unclean. | |
| 257 Selectables: | |
| 258 selectable 1 | |
| 259 selectable 2""") | |
| 260 | |
| 261 | |
| 262 def test_formatDelayedCallsAndSelectables(self): | |
| 263 """ | |
| 264 Both delayed calls and selectables can appear in the same error. | |
| 265 """ | |
| 266 error = DirtyReactorAggregateError(["bleck", "Boozo"], | |
| 267 ["Sel1", "Sel2"]) | |
| 268 self.assertEquals(str(error), | |
| 269 """\ | |
| 270 Reactor was unclean. | |
| 271 DelayedCalls: (set twisted.internet.base.DelayedCall.debug = True to debug) | |
| 272 bleck | |
| 273 Boozo | |
| 274 Selectables: | |
| 275 Sel1 | |
| 276 Sel2""") | |
| 277 | |
| 278 | |
| 279 | |
| 280 class StubReactor(object): | |
| 281 """ | |
| 282 A reactor stub which contains enough functionality to be used with the | |
| 283 L{_Janitor}. | |
| 284 | |
| 285 @ivar iterations: A list of the arguments passed to L{iterate}. | |
| 286 @ivar removeAllCalled: Number of times that L{removeAll} was called. | |
| 287 @ivar selectables: The value that will be returned from L{removeAll}. | |
| 288 @ivar delayedCalls: The value to return from L{getDelayedCalls}. | |
| 289 """ | |
| 290 | |
| 291 def __init__(self, delayedCalls, selectables=None): | |
| 292 """ | |
| 293 @param delayedCalls: See L{StubReactor.delayedCalls}. | |
| 294 @param selectables: See L{StubReactor.selectables}. | |
| 295 """ | |
| 296 self.delayedCalls = delayedCalls | |
| 297 self.iterations = [] | |
| 298 self.removeAllCalled = 0 | |
| 299 if not selectables: | |
| 300 selectables = [] | |
| 301 self.selectables = selectables | |
| 302 | |
| 303 | |
| 304 def iterate(self, timeout=None): | |
| 305 """ | |
| 306 Increment C{self.iterations}. | |
| 307 """ | |
| 308 self.iterations.append(timeout) | |
| 309 | |
| 310 | |
| 311 def getDelayedCalls(self): | |
| 312 """ | |
| 313 Return C{self.delayedCalls}. | |
| 314 """ | |
| 315 return self.delayedCalls | |
| 316 | |
| 317 | |
| 318 def removeAll(self): | |
| 319 """ | |
| 320 Increment C{self.removeAllCalled} and return C{self.selectables}. | |
| 321 """ | |
| 322 self.removeAllCalled += 1 | |
| 323 return self.selectables | |
| 324 | |
| 325 | |
| 326 | |
| 327 class StubErrorReporter(object): | |
| 328 """ | |
| 329 A subset of L{twisted.trial.itrial.IReporter} which records L{addError} | |
| 330 calls. | |
| 331 | |
| 332 @ivar errors: List of two-tuples of (test, error) which were passed to | |
| 333 L{addError}. | |
| 334 """ | |
| 335 | |
| 336 def __init__(self): | |
| 337 self.errors = [] | |
| 338 | |
| 339 | |
| 340 def addError(self, test, error): | |
| 341 """ | |
| 342 Record parameters in C{self.errors}. | |
| 343 """ | |
| 344 self.errors.append((test, error)) | |
| 345 | |
| 346 | |
| 347 | |
| 348 class JanitorTests(TestCase): | |
| 349 """ | |
| 350 Tests for L{_Janitor}! | |
| 351 """ | |
| 352 | |
| 353 def test_cleanPendingSpinsReactor(self): | |
| 354 """ | |
| 355 During pending-call cleanup, the reactor will be spun twice with an | |
| 356 instant timeout. This is not a requirement, it is only a test for | |
| 357 current behavior. Hopefully Trial will eventually not do this kind of | |
| 358 reactor stuff. | |
| 359 """ | |
| 360 reactor = StubReactor([]) | |
| 361 jan = _Janitor(None, None, reactor=reactor) | |
| 362 jan._cleanPending() | |
| 363 self.assertEquals(reactor.iterations, [0, 0]) | |
| 364 | |
| 365 | |
| 366 def test_cleanPendingCancelsCalls(self): | |
| 367 """ | |
| 368 During pending-call cleanup, the janitor cancels pending timed calls. | |
| 369 """ | |
| 370 def func(): | |
| 371 return "Lulz" | |
| 372 cancelled = [] | |
| 373 delayedCall = DelayedCall(300, func, (), {}, | |
| 374 cancelled.append, lambda x: None) | |
| 375 reactor = StubReactor([delayedCall]) | |
| 376 jan = _Janitor(None, None, reactor=reactor) | |
| 377 jan._cleanPending() | |
| 378 self.assertEquals(cancelled, [delayedCall]) | |
| 379 | |
| 380 | |
| 381 def test_cleanPendingReturnsDelayedCallStrings(self): | |
| 382 """ | |
| 383 The Janitor produces string representations of delayed calls from the | |
| 384 delayed call cleanup method. It gets the string representations | |
| 385 *before* cancelling the calls; this is important because cancelling the | |
| 386 call removes critical debugging information from the string | |
| 387 representation. | |
| 388 """ | |
| 389 delayedCall = DelayedCall(300, lambda: None, (), {}, | |
| 390 lambda x: None, lambda x: None, | |
| 391 seconds=lambda: 0) | |
| 392 delayedCallString = str(delayedCall) | |
| 393 reactor = StubReactor([delayedCall]) | |
| 394 jan = _Janitor(None, None, reactor=reactor) | |
| 395 strings = jan._cleanPending() | |
| 396 self.assertEquals(strings, [delayedCallString]) | |
| 397 | |
| 398 | |
| 399 def test_cleanReactorRemovesSelectables(self): | |
| 400 """ | |
| 401 The Janitor will remove selectables during reactor cleanup. | |
| 402 """ | |
| 403 reactor = StubReactor([]) | |
| 404 jan = _Janitor(None, None, reactor=reactor) | |
| 405 jan._cleanReactor() | |
| 406 self.assertEquals(reactor.removeAllCalled, 1) | |
| 407 | |
| 408 | |
| 409 def test_cleanReactorKillsProcesses(self): | |
| 410 """ | |
| 411 The Janitor will kill processes during reactor cleanup. | |
| 412 """ | |
| 413 class StubProcessTransport(object): | |
| 414 """ | |
| 415 A stub L{IProcessTransport} provider which records signals. | |
| 416 @ivar signals: The signals passed to L{signalProcess}. | |
| 417 """ | |
| 418 implements(IProcessTransport) | |
| 419 | |
| 420 def __init__(self): | |
| 421 self.signals = [] | |
| 422 | |
| 423 def signalProcess(self, signal): | |
| 424 """ | |
| 425 Append C{signal} to C{self.signals}. | |
| 426 """ | |
| 427 self.signals.append(signal) | |
| 428 | |
| 429 pt = StubProcessTransport() | |
| 430 reactor = StubReactor([], [pt]) | |
| 431 jan = _Janitor(None, None, reactor=reactor) | |
| 432 jan._cleanReactor() | |
| 433 self.assertEquals(pt.signals, ["KILL"]) | |
| 434 | |
| 435 | |
| 436 def test_cleanReactorReturnsSelectableStrings(self): | |
| 437 """ | |
| 438 The Janitor returns string representations of the selectables that it | |
| 439 cleaned up from the reactor cleanup method. | |
| 440 """ | |
| 441 class Selectable(object): | |
| 442 """ | |
| 443 A stub Selectable which only has an interesting string | |
| 444 representation. | |
| 445 """ | |
| 446 def __repr__(self): | |
| 447 return "(SELECTABLE!)" | |
| 448 | |
| 449 reactor = StubReactor([], [Selectable()]) | |
| 450 jan = _Janitor(None, None, reactor=reactor) | |
| 451 self.assertEquals(jan._cleanReactor(), ["(SELECTABLE!)"]) | |
| 452 | |
| 453 | |
| 454 def test_postCaseCleanupNoErrors(self): | |
| 455 """ | |
| 456 The post-case cleanup method will return True and not call C{addError} | |
| 457 on the result if there are no pending calls. | |
| 458 """ | |
| 459 reactor = StubReactor([]) | |
| 460 test = object() | |
| 461 reporter = StubErrorReporter() | |
| 462 jan = _Janitor(test, reporter, reactor=reactor) | |
| 463 self.assertTrue(jan.postCaseCleanup()) | |
| 464 self.assertEquals(reporter.errors, []) | |
| 465 | |
| 466 | |
| 467 def test_postCaseCleanupWithErrors(self): | |
| 468 """ | |
| 469 The post-case cleanup method will return False and call C{addError} on | |
| 470 the result with a L{DirtyReactorAggregateError} Failure if there are | |
| 471 pending calls. | |
| 472 """ | |
| 473 delayedCall = DelayedCall(300, lambda: None, (), {}, | |
| 474 lambda x: None, lambda x: None, | |
| 475 seconds=lambda: 0) | |
| 476 delayedCallString = str(delayedCall) | |
| 477 reactor = StubReactor([delayedCall], []) | |
| 478 test = object() | |
| 479 reporter = StubErrorReporter() | |
| 480 jan = _Janitor(test, reporter, reactor=reactor) | |
| 481 self.assertFalse(jan.postCaseCleanup()) | |
| 482 self.assertEquals(len(reporter.errors), 1) | |
| 483 self.assertEquals(reporter.errors[0][1].value.delayedCalls, | |
| 484 [delayedCallString]) | |
| 485 | |
| 486 | |
| 487 def test_postClassCleanupNoErrors(self): | |
| 488 """ | |
| 489 The post-class cleanup method will not call C{addError} on the result | |
| 490 if there are no pending calls or selectables. | |
| 491 """ | |
| 492 reactor = StubReactor([]) | |
| 493 test = object() | |
| 494 reporter = StubErrorReporter() | |
| 495 jan = _Janitor(test, reporter, reactor=reactor) | |
| 496 jan.postClassCleanup() | |
| 497 self.assertEquals(reporter.errors, []) | |
| 498 | |
| 499 | |
| 500 def test_postClassCleanupWithPendingCallErrors(self): | |
| 501 """ | |
| 502 The post-class cleanup method call C{addError} on the result with a | |
| 503 L{DirtyReactorAggregateError} Failure if there are pending calls. | |
| 504 """ | |
| 505 delayedCall = DelayedCall(300, lambda: None, (), {}, | |
| 506 lambda x: None, lambda x: None, | |
| 507 seconds=lambda: 0) | |
| 508 delayedCallString = str(delayedCall) | |
| 509 reactor = StubReactor([delayedCall], []) | |
| 510 test = object() | |
| 511 reporter = StubErrorReporter() | |
| 512 jan = _Janitor(test, reporter, reactor=reactor) | |
| 513 jan.postClassCleanup() | |
| 514 self.assertEquals(len(reporter.errors), 1) | |
| 515 self.assertEquals(reporter.errors[0][1].value.delayedCalls, | |
| 516 [delayedCallString]) | |
| 517 | |
| 518 | |
| 519 def test_postClassCleanupWithSelectableErrors(self): | |
| 520 """ | |
| 521 The post-class cleanup method call C{addError} on the result with a | |
| 522 L{DirtyReactorAggregateError} Failure if there are selectables. | |
| 523 """ | |
| 524 selectable = "SELECTABLE HERE" | |
| 525 reactor = StubReactor([], [selectable]) | |
| 526 test = object() | |
| 527 reporter = StubErrorReporter() | |
| 528 jan = _Janitor(test, reporter, reactor=reactor) | |
| 529 jan.postClassCleanup() | |
| 530 self.assertEquals(len(reporter.errors), 1) | |
| 531 self.assertEquals(reporter.errors[0][1].value.selectables, | |
| 532 [repr(selectable)]) | |
| 533 | |
| OLD | NEW |