OLD | NEW |
| (Empty) |
1 # Copyright (c) 2007-2008 Twisted Matrix Laboratories. | |
2 # See LICENSE for details. | |
3 | |
4 """ | |
5 Tests for L{twisted.application.app} and L{twisted.scripts.twistd}. | |
6 """ | |
7 | |
8 import os, sys, cPickle | |
9 try: | |
10 import pwd, grp | |
11 except ImportError: | |
12 pwd = grp = None | |
13 | |
14 from zope.interface import implements | |
15 | |
16 from twisted.trial import unittest | |
17 | |
18 from twisted.application import service, app | |
19 from twisted.scripts import twistd | |
20 from twisted.python import log | |
21 | |
22 try: | |
23 import profile | |
24 except ImportError: | |
25 profile = None | |
26 | |
27 try: | |
28 import hotshot | |
29 import hotshot.stats | |
30 except (ImportError, SystemExit): | |
31 # For some reasons, hotshot.stats seems to raise SystemExit on some | |
32 # distributions, probably when considered non-free. See the import of | |
33 # this module in twisted.application.app for more details. | |
34 hotshot = None | |
35 | |
36 try: | |
37 import cProfile | |
38 import pstats | |
39 except ImportError: | |
40 cProfile = None | |
41 | |
42 | |
43 | |
44 def patchUserDatabase(patch, user, uid, group, gid): | |
45 """ | |
46 Patch L{pwd.getpwnam} so that it behaves as though only one user exists | |
47 and patch L{grp.getgrnam} so that it behaves as though only one group | |
48 exists. | |
49 | |
50 @param patch: A function like L{TestCase.patch} which will be used to | |
51 install the fake implementations. | |
52 | |
53 @type user: C{str} | |
54 @param user: The name of the single user which will exist. | |
55 | |
56 @type uid: C{int} | |
57 @param uid: The UID of the single user which will exist. | |
58 | |
59 @type group: C{str} | |
60 @param group: The name of the single user which will exist. | |
61 | |
62 @type gid: C{int} | |
63 @param gid: The GID of the single group which will exist. | |
64 """ | |
65 # Try not to be an unverified fake, but try not to depend on quirks of | |
66 # the system either (eg, run as a process with a uid and gid which | |
67 # equal each other, and so doesn't reliably test that uid is used where | |
68 # uid should be used and gid is used where gid should be used). -exarkun | |
69 pwent = pwd.getpwuid(os.getuid()) | |
70 grent = grp.getgrgid(os.getgid()) | |
71 | |
72 def getpwnam(name): | |
73 result = list(pwent) | |
74 result[result.index(pwent.pw_name)] = user | |
75 result[result.index(pwent.pw_uid)] = uid | |
76 result = tuple(result) | |
77 return {user: result}[name] | |
78 | |
79 def getgrnam(name): | |
80 result = list(grent) | |
81 result[result.index(grent.gr_name)] = group | |
82 result[result.index(grent.gr_gid)] = gid | |
83 result = tuple(result) | |
84 return {group: result}[name] | |
85 | |
86 patch(pwd, "getpwnam", getpwnam) | |
87 patch(grp, "getgrnam", getgrnam) | |
88 | |
89 | |
90 | |
91 class MockServiceMaker(object): | |
92 """ | |
93 A non-implementation of L{twisted.application.service.IServiceMaker}. | |
94 """ | |
95 tapname = 'ueoa' | |
96 | |
97 def makeService(self, options): | |
98 """ | |
99 Take a L{usage.Options} instance and return a | |
100 L{service.IService} provider. | |
101 """ | |
102 self.options = options | |
103 self.service = service.Service() | |
104 return self.service | |
105 | |
106 | |
107 | |
108 class CrippledApplicationRunner(twistd._SomeApplicationRunner): | |
109 """ | |
110 An application runner that cripples the platform-specific runner and | |
111 nasty side-effect-having code so that we can use it without actually | |
112 running any environment-affecting code. | |
113 """ | |
114 def preApplication(self): | |
115 pass | |
116 | |
117 def postApplication(self): | |
118 pass | |
119 | |
120 def startLogging(self, observer): | |
121 pass | |
122 | |
123 | |
124 | |
125 class ServerOptionsTest(unittest.TestCase): | |
126 """ | |
127 Non-platform-specific tests for the pltaform-specific ServerOptions class. | |
128 """ | |
129 | |
130 def test_postOptionsSubCommandCausesNoSave(self): | |
131 """ | |
132 postOptions should set no_save to True when a subcommand is used. | |
133 """ | |
134 config = twistd.ServerOptions() | |
135 config.subCommand = 'ueoa' | |
136 config.postOptions() | |
137 self.assertEquals(config['no_save'], True) | |
138 | |
139 | |
140 def test_postOptionsNoSubCommandSavesAsUsual(self): | |
141 """ | |
142 If no sub command is used, postOptions should not touch no_save. | |
143 """ | |
144 config = twistd.ServerOptions() | |
145 config.postOptions() | |
146 self.assertEquals(config['no_save'], False) | |
147 | |
148 | |
149 def test_reportProfileDeprecation(self): | |
150 """ | |
151 Check that the --report-profile option prints a C{DeprecationWarning}. | |
152 """ | |
153 config = twistd.ServerOptions() | |
154 self.assertWarns( | |
155 DeprecationWarning, "--report-profile option is deprecated and " | |
156 "a no-op since Twisted 8.0.", app.__file__, | |
157 config.parseOptions, ["--report-profile", "foo"]) | |
158 | |
159 | |
160 | |
161 class TapFileTest(unittest.TestCase): | |
162 """ | |
163 Test twistd-related functionality that requires a tap file on disk. | |
164 """ | |
165 | |
166 def setUp(self): | |
167 """ | |
168 Create a trivial Application and put it in a tap file on disk. | |
169 """ | |
170 self.tapfile = self.mktemp() | |
171 cPickle.dump(service.Application("Hi!"), file(self.tapfile, 'wb')) | |
172 | |
173 | |
174 def test_createOrGetApplicationWithTapFile(self): | |
175 """ | |
176 Ensure that the createOrGetApplication call that 'twistd -f foo.tap' | |
177 makes will load the Application out of foo.tap. | |
178 """ | |
179 config = twistd.ServerOptions() | |
180 config.parseOptions(['-f', self.tapfile]) | |
181 application = CrippledApplicationRunner(config).createOrGetApplication() | |
182 self.assertEquals(service.IService(application).name, 'Hi!') | |
183 | |
184 | |
185 | |
186 class TestApplicationRunner(app.ApplicationRunner): | |
187 """ | |
188 An ApplicationRunner which tracks the environment in which its | |
189 methods are called. | |
190 """ | |
191 def preApplication(self): | |
192 self.order = ["pre"] | |
193 self.hadApplicationPreApplication = hasattr(self, 'application') | |
194 | |
195 | |
196 def getLogObserver(self): | |
197 self.order.append("log") | |
198 self.hadApplicationLogObserver = hasattr(self, 'application') | |
199 return lambda events: None | |
200 | |
201 | |
202 def startLogging(self, observer): | |
203 pass | |
204 | |
205 | |
206 def postApplication(self): | |
207 self.order.append("post") | |
208 self.hadApplicationPostApplication = hasattr(self, 'application') | |
209 | |
210 | |
211 | |
212 class ApplicationRunnerTest(unittest.TestCase): | |
213 """ | |
214 Non-platform-specific tests for the platform-specific ApplicationRunner. | |
215 """ | |
216 def setUp(self): | |
217 config = twistd.ServerOptions() | |
218 self.serviceMaker = MockServiceMaker() | |
219 # Set up a config object like it's been parsed with a subcommand | |
220 config.loadedPlugins = {'test_command': self.serviceMaker} | |
221 config.subOptions = object() | |
222 config.subCommand = 'test_command' | |
223 self.config = config | |
224 | |
225 | |
226 def test_applicationRunnerGetsCorrectApplication(self): | |
227 """ | |
228 Ensure that a twistd plugin gets used in appropriate ways: it | |
229 is passed its Options instance, and the service it returns is | |
230 added to the application. | |
231 """ | |
232 arunner = CrippledApplicationRunner(self.config) | |
233 arunner.run() | |
234 | |
235 self.assertIdentical( | |
236 self.serviceMaker.options, self.config.subOptions, | |
237 "ServiceMaker.makeService needs to be passed the correct " | |
238 "sub Command object.") | |
239 self.assertIdentical( | |
240 self.serviceMaker.service, | |
241 service.IService(arunner.application).services[0], | |
242 "ServiceMaker.makeService's result needs to be set as a child " | |
243 "of the Application.") | |
244 | |
245 | |
246 def test_preAndPostApplication(self): | |
247 """ | |
248 Test thet preApplication and postApplication methods are | |
249 called by ApplicationRunner.run() when appropriate. | |
250 """ | |
251 s = TestApplicationRunner(self.config) | |
252 s.run() | |
253 self.failIf(s.hadApplicationPreApplication) | |
254 self.failUnless(s.hadApplicationPostApplication) | |
255 self.failUnless(s.hadApplicationLogObserver) | |
256 self.assertEquals(s.order, ["pre", "log", "post"]) | |
257 | |
258 | |
259 def test_stdoutLogObserver(self): | |
260 """ | |
261 Verify that if C{'-'} is specified as the log file, stdout is used. | |
262 """ | |
263 self.config.parseOptions(["--logfile", "-", "--nodaemon"]) | |
264 runner = CrippledApplicationRunner(self.config) | |
265 observerMethod = runner.getLogObserver() | |
266 observer = observerMethod.im_self | |
267 self.failUnless(isinstance(observer, log.FileLogObserver)) | |
268 writeMethod = observer.write | |
269 fileObj = writeMethod.__self__ | |
270 self.assertIdentical(fileObj, sys.stdout) | |
271 | |
272 | |
273 def test_fileLogObserver(self): | |
274 """ | |
275 Verify that if a string other than C{'-'} is specified as the log file, | |
276 the file with that name is used. | |
277 """ | |
278 logfilename = os.path.abspath(self.mktemp()) | |
279 self.config.parseOptions(["--logfile", logfilename]) | |
280 runner = CrippledApplicationRunner(self.config) | |
281 observerMethod = runner.getLogObserver() | |
282 observer = observerMethod.im_self | |
283 self.failUnless(isinstance(observer, log.FileLogObserver)) | |
284 writeMethod = observer.write | |
285 fileObj = writeMethod.im_self | |
286 self.assertEqual(fileObj.path, logfilename) | |
287 | |
288 | |
289 def _applicationStartsWithConfiguredID(self, argv, uid, gid): | |
290 """ | |
291 Assert that given a particular command line, an application is started | |
292 as a particular UID/GID. | |
293 | |
294 @param argv: A list of strings giving the options to parse. | |
295 @param uid: An integer giving the expected UID. | |
296 @param gid: An integer giving the expected GID. | |
297 """ | |
298 self.config.parseOptions(argv) | |
299 | |
300 events = [] | |
301 class FakeUnixApplicationRunner(twistd._SomeApplicationRunner): | |
302 def setupEnvironment(self, chroot, rundir, nodaemon, pidfile): | |
303 events.append('environment') | |
304 | |
305 def shedPrivileges(self, euid, uid, gid): | |
306 events.append(('privileges', euid, uid, gid)) | |
307 | |
308 def startReactor(self, reactor, oldstdout, oldstderr): | |
309 events.append('reactor') | |
310 | |
311 def removePID(self, pidfile): | |
312 pass | |
313 | |
314 | |
315 class FakeService(object): | |
316 implements(service.IService, service.IProcess) | |
317 | |
318 processName = None | |
319 | |
320 def privilegedStartService(self): | |
321 events.append('privilegedStartService') | |
322 | |
323 def startService(self): | |
324 events.append('startService') | |
325 | |
326 def stopService(self): | |
327 pass | |
328 | |
329 runner = FakeUnixApplicationRunner(self.config) | |
330 runner.preApplication() | |
331 runner.application = FakeService() | |
332 runner.postApplication() | |
333 | |
334 self.assertEqual( | |
335 events, | |
336 ['environment', 'privilegedStartService', | |
337 ('privileges', False, uid, gid), 'startService', 'reactor']) | |
338 | |
339 | |
340 def test_applicationStartsWithConfiguredNumericIDs(self): | |
341 """ | |
342 L{postApplication} should change the UID and GID to the values | |
343 specified as numeric strings by the configuration after running | |
344 L{service.IService.privilegedStartService} and before running | |
345 L{service.IService.startService}. | |
346 """ | |
347 uid = 1234 | |
348 gid = 4321 | |
349 self._applicationStartsWithConfiguredID( | |
350 ["--uid", str(uid), "--gid", str(gid)], uid, gid) | |
351 | |
352 | |
353 def test_applicationStartsWithConfiguredNameIDs(self): | |
354 """ | |
355 L{postApplication} should change the UID and GID to the values | |
356 specified as user and group names by the configuration after running | |
357 L{service.IService.privilegedStartService} and before running | |
358 L{service.IService.startService}. | |
359 """ | |
360 user = "foo" | |
361 uid = 1234 | |
362 group = "bar" | |
363 gid = 4321 | |
364 patchUserDatabase(self.patch, user, uid, group, gid) | |
365 self._applicationStartsWithConfiguredID( | |
366 ["--uid", user, "--gid", group], uid, gid) | |
367 | |
368 if getattr(os, 'setuid', None) is None: | |
369 msg = "Platform does not support --uid/--gid twistd options." | |
370 test_applicationStartsWithConfiguredNameIDs.skip = msg | |
371 test_applicationStartsWithConfiguredNumericIDs.skip = msg | |
372 del msg | |
373 | |
374 | |
375 def test_startReactorRunsTheReactor(self): | |
376 """ | |
377 L{startReactor} calls L{reactor.run}. | |
378 """ | |
379 reactor = DummyReactor() | |
380 runner = app.ApplicationRunner({ | |
381 "profile": False, | |
382 "profiler": "profile", | |
383 "debug": False}) | |
384 runner.startReactor(reactor, None, None) | |
385 self.assertTrue( | |
386 reactor.called, "startReactor did not call reactor.run()") | |
387 | |
388 | |
389 | |
390 class DummyReactor(object): | |
391 """ | |
392 A dummy reactor, only providing a C{run} method and checking that it | |
393 has been called. | |
394 | |
395 @ivar called: if C{run} has been called or not. | |
396 @type called: C{bool} | |
397 """ | |
398 called = False | |
399 | |
400 def run(self): | |
401 """ | |
402 A fake run method, checking that it's been called one and only time. | |
403 """ | |
404 if self.called: | |
405 raise RuntimeError("Already called") | |
406 self.called = True | |
407 | |
408 | |
409 | |
410 class AppProfilingTestCase(unittest.TestCase): | |
411 """ | |
412 Tests for L{app.AppProfiler}. | |
413 """ | |
414 | |
415 def test_profile(self): | |
416 """ | |
417 L{app.ProfileRunner.run} should call the C{run} method of the reactor | |
418 and save profile data in the specified file. | |
419 """ | |
420 config = twistd.ServerOptions() | |
421 config["profile"] = self.mktemp() | |
422 config["profiler"] = "profile" | |
423 profiler = app.AppProfiler(config) | |
424 reactor = DummyReactor() | |
425 | |
426 profiler.run(reactor) | |
427 | |
428 self.assertTrue(reactor.called) | |
429 data = file(config["profile"]).read() | |
430 self.assertIn("DummyReactor.run", data) | |
431 self.assertIn("function calls", data) | |
432 | |
433 if profile is None: | |
434 test_profile.skip = "profile module not available" | |
435 | |
436 | |
437 def test_profileSaveStats(self): | |
438 """ | |
439 With the C{savestats} option specified, L{app.ProfileRunner.run} | |
440 should save the raw stats object instead of a summary output. | |
441 """ | |
442 config = twistd.ServerOptions() | |
443 config["profile"] = self.mktemp() | |
444 config["profiler"] = "profile" | |
445 config["savestats"] = True | |
446 profiler = app.AppProfiler(config) | |
447 reactor = DummyReactor() | |
448 | |
449 profiler.run(reactor) | |
450 | |
451 self.assertTrue(reactor.called) | |
452 data = file(config["profile"]).read() | |
453 self.assertIn("DummyReactor.run", data) | |
454 self.assertNotIn("function calls", data) | |
455 | |
456 if profile is None: | |
457 test_profileSaveStats.skip = "profile module not available" | |
458 | |
459 | |
460 def test_withoutProfile(self): | |
461 """ | |
462 When the C{profile} module is not present, L{app.ProfilerRunner.run} | |
463 should raise a C{SystemExit} exception. | |
464 """ | |
465 savedModules = sys.modules.copy() | |
466 | |
467 config = twistd.ServerOptions() | |
468 config["profiler"] = "profile" | |
469 profiler = app.AppProfiler(config) | |
470 | |
471 sys.modules["profile"] = None | |
472 try: | |
473 self.assertRaises(SystemExit, profiler.run, None) | |
474 finally: | |
475 sys.modules.clear() | |
476 sys.modules.update(savedModules) | |
477 | |
478 | |
479 def test_profilePrintStatsError(self): | |
480 """ | |
481 When an error happens during the print of the stats, C{sys.stdout} | |
482 should be restored to its initial value. | |
483 """ | |
484 class ErroneousProfile(profile.Profile): | |
485 def print_stats(self): | |
486 raise RuntimeError("Boom") | |
487 self.patch(profile, "Profile", ErroneousProfile) | |
488 | |
489 config = twistd.ServerOptions() | |
490 config["profile"] = self.mktemp() | |
491 config["profiler"] = "profile" | |
492 profiler = app.AppProfiler(config) | |
493 reactor = DummyReactor() | |
494 | |
495 oldStdout = sys.stdout | |
496 self.assertRaises(RuntimeError, profiler.run, reactor) | |
497 self.assertIdentical(sys.stdout, oldStdout) | |
498 | |
499 if profile is None: | |
500 test_profilePrintStatsError.skip = "profile module not available" | |
501 | |
502 | |
503 def test_hotshot(self): | |
504 """ | |
505 L{app.HotshotRunner.run} should call the C{run} method of the reactor | |
506 and save profile data in the specified file. | |
507 """ | |
508 config = twistd.ServerOptions() | |
509 config["profile"] = self.mktemp() | |
510 config["profiler"] = "hotshot" | |
511 profiler = app.AppProfiler(config) | |
512 reactor = DummyReactor() | |
513 | |
514 profiler.run(reactor) | |
515 | |
516 self.assertTrue(reactor.called) | |
517 data = file(config["profile"]).read() | |
518 self.assertIn("run", data) | |
519 self.assertIn("function calls", data) | |
520 | |
521 if hotshot is None: | |
522 test_hotshot.skip = "hotshot module not available" | |
523 | |
524 | |
525 def test_hotshotSaveStats(self): | |
526 """ | |
527 With the C{savestats} option specified, L{app.HotshotRunner.run} should | |
528 save the raw stats object instead of a summary output. | |
529 """ | |
530 config = twistd.ServerOptions() | |
531 config["profile"] = self.mktemp() | |
532 config["profiler"] = "hotshot" | |
533 config["savestats"] = True | |
534 profiler = app.AppProfiler(config) | |
535 reactor = DummyReactor() | |
536 | |
537 profiler.run(reactor) | |
538 | |
539 self.assertTrue(reactor.called) | |
540 data = file(config["profile"]).read() | |
541 self.assertIn("hotshot-version", data) | |
542 self.assertIn("run", data) | |
543 self.assertNotIn("function calls", data) | |
544 | |
545 if hotshot is None: | |
546 test_hotshotSaveStats.skip = "hotshot module not available" | |
547 | |
548 | |
549 def test_withoutHotshot(self): | |
550 """ | |
551 When the C{hotshot} module is not present, L{app.HotshotRunner.run} | |
552 should raise a C{SystemExit} exception and log the C{ImportError}. | |
553 """ | |
554 savedModules = sys.modules.copy() | |
555 sys.modules["hotshot"] = None | |
556 | |
557 config = twistd.ServerOptions() | |
558 config["profiler"] = "hotshot" | |
559 profiler = app.AppProfiler(config) | |
560 try: | |
561 self.assertRaises(SystemExit, profiler.run, None) | |
562 finally: | |
563 sys.modules.clear() | |
564 sys.modules.update(savedModules) | |
565 | |
566 | |
567 def test_nothotshotDeprecation(self): | |
568 """ | |
569 Check that switching on the C{nothotshot} option produces a warning and | |
570 sets the profiler to B{profile}. | |
571 """ | |
572 config = twistd.ServerOptions() | |
573 config['nothotshot'] = True | |
574 profiler = self.assertWarns(DeprecationWarning, | |
575 "The --nothotshot option is deprecated. Please specify the " | |
576 "profiler name using the --profiler option", | |
577 app.__file__, app.AppProfiler, config) | |
578 self.assertEquals(profiler.profiler, "profile") | |
579 | |
580 | |
581 def test_hotshotPrintStatsError(self): | |
582 """ | |
583 When an error happens while printing the stats, C{sys.stdout} | |
584 should be restored to its initial value. | |
585 """ | |
586 import pstats | |
587 class ErroneousStats(pstats.Stats): | |
588 def print_stats(self): | |
589 raise RuntimeError("Boom") | |
590 self.patch(pstats, "Stats", ErroneousStats) | |
591 | |
592 config = twistd.ServerOptions() | |
593 config["profile"] = self.mktemp() | |
594 config["profiler"] = "hotshot" | |
595 profiler = app.AppProfiler(config) | |
596 reactor = DummyReactor() | |
597 | |
598 oldStdout = sys.stdout | |
599 self.assertRaises(RuntimeError, profiler.run, reactor) | |
600 self.assertIdentical(sys.stdout, oldStdout) | |
601 | |
602 if hotshot is None: | |
603 test_hotshotPrintStatsError.skip = "hotshot module not available" | |
604 | |
605 | |
606 def test_cProfile(self): | |
607 """ | |
608 L{app.CProfileRunner.run} should call the C{run} method of the | |
609 reactor and save profile data in the specified file. | |
610 """ | |
611 config = twistd.ServerOptions() | |
612 config["profile"] = self.mktemp() | |
613 config["profiler"] = "cProfile" | |
614 profiler = app.AppProfiler(config) | |
615 reactor = DummyReactor() | |
616 | |
617 profiler.run(reactor) | |
618 | |
619 self.assertTrue(reactor.called) | |
620 data = file(config["profile"]).read() | |
621 self.assertIn("run", data) | |
622 self.assertIn("function calls", data) | |
623 | |
624 if cProfile is None: | |
625 test_cProfile.skip = "cProfile module not available" | |
626 | |
627 | |
628 def test_cProfileSaveStats(self): | |
629 """ | |
630 With the C{savestats} option specified, | |
631 L{app.CProfileRunner.run} should save the raw stats object | |
632 instead of a summary output. | |
633 """ | |
634 config = twistd.ServerOptions() | |
635 config["profile"] = self.mktemp() | |
636 config["profiler"] = "cProfile" | |
637 config["savestats"] = True | |
638 profiler = app.AppProfiler(config) | |
639 reactor = DummyReactor() | |
640 | |
641 profiler.run(reactor) | |
642 | |
643 self.assertTrue(reactor.called) | |
644 data = file(config["profile"]).read() | |
645 self.assertIn("run", data) | |
646 | |
647 if cProfile is None: | |
648 test_cProfileSaveStats.skip = "cProfile module not available" | |
649 | |
650 | |
651 def test_withoutCProfile(self): | |
652 """ | |
653 When the C{cProfile} module is not present, | |
654 L{app.CProfileRunner.run} should raise a C{SystemExit} | |
655 exception and log the C{ImportError}. | |
656 """ | |
657 savedModules = sys.modules.copy() | |
658 sys.modules["cProfile"] = None | |
659 | |
660 config = twistd.ServerOptions() | |
661 config["profiler"] = "cProfile" | |
662 profiler = app.AppProfiler(config) | |
663 try: | |
664 self.assertRaises(SystemExit, profiler.run, None) | |
665 finally: | |
666 sys.modules.clear() | |
667 sys.modules.update(savedModules) | |
668 | |
669 | |
670 def test_unknownProfiler(self): | |
671 """ | |
672 Check that L{app.AppProfiler} raises L{SystemExit} when given an | |
673 unknown profiler name. | |
674 """ | |
675 config = twistd.ServerOptions() | |
676 config["profile"] = self.mktemp() | |
677 config["profiler"] = "foobar" | |
678 | |
679 error = self.assertRaises(SystemExit, app.AppProfiler, config) | |
680 self.assertEquals(str(error), "Unsupported profiler name: foobar") | |
681 | |
682 | |
683 def test_oldRunWithProfiler(self): | |
684 """ | |
685 L{app.runWithProfiler} should print a C{DeprecationWarning} pointing | |
686 at L{AppProfiler}. | |
687 """ | |
688 class DummyProfiler(object): | |
689 called = False | |
690 def run(self, reactor): | |
691 self.called = True | |
692 profiler = DummyProfiler() | |
693 self.patch(app, "AppProfiler", lambda conf: profiler) | |
694 | |
695 def runWithProfiler(): | |
696 return app.runWithProfiler(DummyReactor(), {}) | |
697 | |
698 self.assertWarns(DeprecationWarning, | |
699 "runWithProfiler is deprecated since Twisted 8.0. " | |
700 "Use ProfileRunner instead.", __file__, | |
701 runWithProfiler) | |
702 self.assertTrue(profiler.called) | |
703 | |
704 | |
705 def test_oldRunWithHotshot(self): | |
706 """ | |
707 L{app.runWithHotshot} should print a C{DeprecationWarning} pointing | |
708 at L{AppProfiler}. | |
709 """ | |
710 class DummyProfiler(object): | |
711 called = False | |
712 def run(self, reactor): | |
713 self.called = True | |
714 profiler = DummyProfiler() | |
715 self.patch(app, "AppProfiler", lambda conf: profiler) | |
716 | |
717 def runWithHotshot(): | |
718 return app.runWithHotshot(DummyReactor(), {}) | |
719 | |
720 self.assertWarns(DeprecationWarning, | |
721 "runWithHotshot is deprecated since Twisted 8.0. " | |
722 "Use HotshotRunner instead.", __file__, | |
723 runWithHotshot) | |
724 self.assertTrue(profiler.called) | |
OLD | NEW |