test_twist.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. # Copyright (c) Twisted Matrix Laboratories.
  2. # See LICENSE for details.
  3. """
  4. Tests for L{twisted.application.twist._twist}.
  5. """
  6. from sys import stdout
  7. from twisted.logger import LogLevel, jsonFileLogObserver
  8. from twisted.test.proto_helpers import MemoryReactor
  9. from ...service import IService, MultiService
  10. from ...runner._exit import ExitStatus
  11. from ...runner._runner import Runner
  12. from ...runner.test.test_runner import DummyExit
  13. from ...twist import _twist
  14. from .._options import TwistOptions
  15. from .._twist import Twist
  16. import twisted.trial.unittest
  17. class TwistTests(twisted.trial.unittest.TestCase):
  18. """
  19. Tests for L{Twist}.
  20. """
  21. def setUp(self):
  22. self.patchInstallReactor()
  23. def patchExit(self):
  24. """
  25. Patch L{_twist.exit} so we can capture usage and prevent actual exits.
  26. """
  27. self.exit = DummyExit()
  28. self.patch(_twist, "exit", self.exit)
  29. def patchInstallReactor(self):
  30. """
  31. Patch C{_options.installReactor} so we can capture usage and prevent
  32. actual installs.
  33. """
  34. self.installedReactors = {}
  35. def installReactor(_, name):
  36. reactor = MemoryReactor()
  37. self.installedReactors[name] = reactor
  38. return reactor
  39. self.patch(TwistOptions, "installReactor", installReactor)
  40. def patchStartService(self):
  41. """
  42. Patch L{MultiService.startService} so we can capture usage and prevent
  43. actual starts.
  44. """
  45. self.serviceStarts = []
  46. def startService(service):
  47. self.serviceStarts.append(service)
  48. self.patch(MultiService, "startService", startService)
  49. def test_optionsValidArguments(self):
  50. """
  51. L{Twist.options} given valid arguments returns options.
  52. """
  53. options = Twist.options(["twist", "web"])
  54. self.assertIsInstance(options, TwistOptions)
  55. def test_optionsInvalidArguments(self):
  56. """
  57. L{Twist.options} given invalid arguments exits with
  58. L{ExitStatus.EX_USAGE} and an error/usage message.
  59. """
  60. self.patchExit()
  61. Twist.options(["twist", "--bogus-bagels"])
  62. self.assertIdentical(self.exit.status, ExitStatus.EX_USAGE)
  63. self.assertTrue(self.exit.message.startswith("Error: "))
  64. self.assertTrue(self.exit.message.endswith(
  65. "\n\n{}".format(TwistOptions())
  66. ))
  67. def test_service(self):
  68. """
  69. L{Twist.service} returns an L{IService}.
  70. """
  71. options = Twist.options(["twist", "web"]) # web should exist
  72. service = Twist.service(options.plugins["web"], options.subOptions)
  73. self.assertTrue(IService.providedBy(service))
  74. def test_startService(self):
  75. """
  76. L{Twist.startService} starts the service and registers a trigger to
  77. stop the service when the reactor shuts down.
  78. """
  79. options = Twist.options(["twist", "web"])
  80. reactor = options["reactor"]
  81. service = Twist.service(
  82. plugin=options.plugins[options.subCommand],
  83. options=options.subOptions,
  84. )
  85. self.patchStartService()
  86. Twist.startService(reactor, service)
  87. self.assertEqual(self.serviceStarts, [service])
  88. self.assertEqual(
  89. reactor.triggers["before"]["shutdown"],
  90. [(service.stopService, (), {})]
  91. )
  92. def test_runnerArguments(self):
  93. """
  94. L{Twist.runnerArguments} translates L{TwistOptions} to runner
  95. arguments.
  96. """
  97. options = Twist.options([
  98. "twist", "--reactor=default", "--log-format=json", "web"
  99. ])
  100. self.assertEqual(
  101. Twist.runnerArguments(options),
  102. dict(
  103. reactor=self.installedReactors["default"],
  104. defaultLogLevel=LogLevel.info,
  105. logFile=stdout,
  106. fileLogObserverFactory=jsonFileLogObserver,
  107. )
  108. )
  109. def test_run(self):
  110. """
  111. L{Twist.run} runs the runner with the given options.
  112. """
  113. argsSeen = []
  114. self.patch(
  115. Runner, "__init__", lambda self, **args: argsSeen.append(args)
  116. )
  117. self.patch(
  118. Runner, "run", lambda self: None
  119. )
  120. twistOptions = Twist.options(["twist", "web"])
  121. runnerArguments = Twist.runnerArguments(twistOptions)
  122. Twist.run(runnerArguments)
  123. self.assertEqual(len(argsSeen), 1)
  124. self.assertEqual(argsSeen[0], runnerArguments)
  125. def test_main(self):
  126. """
  127. L{Twist.run} runs the runner with options corresponding to the given
  128. arguments.
  129. """
  130. self.patchStartService()
  131. runners = []
  132. class Runner(object):
  133. def __init__(self, **kwargs):
  134. self.args = kwargs
  135. self.runs = 0
  136. runners.append(self)
  137. def run(self):
  138. self.runs += 1
  139. self.patch(_twist, "Runner", Runner)
  140. Twist.main([
  141. "twist", "--reactor=default", "--log-format=json", "web"
  142. ])
  143. self.assertEqual(len(self.serviceStarts), 1)
  144. self.assertEqual(len(runners), 1)
  145. self.assertEqual(
  146. runners[0].args,
  147. dict(
  148. reactor=self.installedReactors["default"],
  149. defaultLogLevel=LogLevel.info,
  150. logFile=stdout,
  151. fileLogObserverFactory=jsonFileLogObserver,
  152. )
  153. )
  154. self.assertEqual(runners[0].runs, 1)