Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(13)

Side by Side Diff: third_party/twisted_8_1/twisted/test/test_twistd.py

Issue 12261012: Remove third_party/twisted_8_1 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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)
OLDNEW
« no previous file with comments | « third_party/twisted_8_1/twisted/test/test_tpfile.py ('k') | third_party/twisted_8_1/twisted/test/test_udp.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698