test_multi.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. from __future__ import absolute_import, unicode_literals
  2. import signal
  3. import sys
  4. import pytest
  5. from case import Mock, patch
  6. from celery.bin.multi import MultiTool
  7. from celery.bin.multi import __doc__ as doc
  8. from celery.bin.multi import main
  9. from celery.five import WhateverIO
  10. class test_MultiTool:
  11. def setup(self):
  12. self.fh = WhateverIO()
  13. self.env = {}
  14. self.t = MultiTool(env=self.env, fh=self.fh)
  15. self.t.cluster_from_argv = Mock(name='cluster_from_argv')
  16. self.t._cluster_from_argv = Mock(name='cluster_from_argv')
  17. self.t.Cluster = Mock(name='Cluster')
  18. self.t.carp = Mock(name='.carp')
  19. self.t.usage = Mock(name='.usage')
  20. self.t.splash = Mock(name='.splash')
  21. self.t.say = Mock(name='.say')
  22. self.t.ok = Mock(name='.ok')
  23. self.cluster = self.t.Cluster.return_value
  24. def _cluster_from_argv(argv):
  25. p = self.t.OptionParser(argv)
  26. p.parse()
  27. return p, self.cluster
  28. self.t.cluster_from_argv.return_value = self.cluster
  29. self.t._cluster_from_argv.side_effect = _cluster_from_argv
  30. def test_findsig(self):
  31. self.assert_sig_argument(['a', 'b', 'c', '-1'], 1)
  32. self.assert_sig_argument(['--foo=1', '-9'], 9)
  33. self.assert_sig_argument(['-INT'], signal.SIGINT)
  34. self.assert_sig_argument([], signal.SIGTERM)
  35. self.assert_sig_argument(['-s'], signal.SIGTERM)
  36. self.assert_sig_argument(['-log'], signal.SIGTERM)
  37. def assert_sig_argument(self, args, expected):
  38. p = self.t.OptionParser(args)
  39. p.parse()
  40. assert self.t._find_sig_argument(p) == expected
  41. def test_execute_from_commandline(self):
  42. self.t.call_command = Mock(name='call_command')
  43. self.t.execute_from_commandline(
  44. 'multi start --verbose 10 --foo'.split(),
  45. cmd='X',
  46. )
  47. assert self.t.cmd == 'X'
  48. assert self.t.prog_name == 'multi'
  49. self.t.call_command.assert_called_with('start', ['10', '--foo'])
  50. def test_execute_from_commandline__arguments(self):
  51. assert self.t.execute_from_commandline('multi'.split())
  52. assert self.t.execute_from_commandline('multi -bar'.split())
  53. def test_call_command(self):
  54. cmd = self.t.commands['foo'] = Mock(name='foo')
  55. self.t.retcode = 303
  56. assert (self.t.call_command('foo', ['1', '2', '--foo=3']) is
  57. cmd.return_value)
  58. cmd.assert_called_with('1', '2', '--foo=3')
  59. def test_call_command__error(self):
  60. assert self.t.call_command('asdqwewqe', ['1', '2']) == 1
  61. self.t.carp.assert_called()
  62. def test_handle_reserved_options(self):
  63. assert self.t._handle_reserved_options(
  64. ['a', '-q', 'b', '--no-color', 'c']) == ['a', 'b', 'c']
  65. def test_start(self):
  66. self.cluster.start.return_value = [0, 0, 1, 0]
  67. assert self.t.start('10', '-A', 'proj')
  68. self.t.splash.assert_called_with()
  69. self.t.cluster_from_argv.assert_called_with(('10', '-A', 'proj'))
  70. self.cluster.start.assert_called_with()
  71. def test_start__exitcodes(self):
  72. self.cluster.start.return_value = [0, 0, 0]
  73. assert not self.t.start('foo', 'bar', 'baz')
  74. self.cluster.start.assert_called_with()
  75. self.cluster.start.return_value = [0, 1, 0]
  76. assert self.t.start('foo', 'bar', 'baz')
  77. def test_stop(self):
  78. self.t.stop('10', '-A', 'proj', retry=3)
  79. self.t.splash.assert_called_with()
  80. self.t._cluster_from_argv.assert_called_with(('10', '-A', 'proj'))
  81. self.cluster.stop.assert_called_with(retry=3, sig=signal.SIGTERM)
  82. def test_stopwait(self):
  83. self.t.stopwait('10', '-A', 'proj', retry=3)
  84. self.t.splash.assert_called_with()
  85. self.t._cluster_from_argv.assert_called_with(('10', '-A', 'proj'))
  86. self.cluster.stopwait.assert_called_with(retry=3, sig=signal.SIGTERM)
  87. def test_restart(self):
  88. self.cluster.restart.return_value = [0, 0, 1, 0]
  89. self.t.restart('10', '-A', 'proj')
  90. self.t.splash.assert_called_with()
  91. self.t._cluster_from_argv.assert_called_with(('10', '-A', 'proj'))
  92. self.cluster.restart.assert_called_with(sig=signal.SIGTERM)
  93. def test_names(self):
  94. self.t.cluster_from_argv.return_value = [Mock(), Mock()]
  95. self.t.cluster_from_argv.return_value[0].name = 'x'
  96. self.t.cluster_from_argv.return_value[1].name = 'y'
  97. self.t.names('10', '-A', 'proj')
  98. self.t.say.assert_called()
  99. def test_get(self):
  100. node = self.cluster.find.return_value = Mock(name='node')
  101. node.argv = ['A', 'B', 'C']
  102. assert (self.t.get('wanted', '10', '-A', 'proj') is
  103. self.t.ok.return_value)
  104. self.cluster.find.assert_called_with('wanted')
  105. self.t.cluster_from_argv.assert_called_with(('10', '-A', 'proj'))
  106. self.t.ok.assert_called_with(' '.join(node.argv))
  107. def test_get__KeyError(self):
  108. self.cluster.find.side_effect = KeyError()
  109. assert self.t.get('wanted', '10', '-A', 'proj')
  110. def test_show(self):
  111. nodes = self.t.cluster_from_argv.return_value = [
  112. Mock(name='n1'),
  113. Mock(name='n2'),
  114. ]
  115. nodes[0].argv_with_executable = ['python', 'foo', 'bar']
  116. nodes[1].argv_with_executable = ['python', 'xuzzy', 'baz']
  117. assert self.t.show('10', '-A', 'proj') is self.t.ok.return_value
  118. self.t.ok.assert_called_with(
  119. '\n'.join(' '.join(node.argv_with_executable) for node in nodes))
  120. def test_kill(self):
  121. self.t.kill('10', '-A', 'proj')
  122. self.t.splash.assert_called_with()
  123. self.t.cluster_from_argv.assert_called_with(('10', '-A', 'proj'))
  124. self.cluster.kill.assert_called_with()
  125. def test_expand(self):
  126. node1 = Mock(name='n1')
  127. node2 = Mock(name='n2')
  128. node1.expander.return_value = 'A'
  129. node2.expander.return_value = 'B'
  130. nodes = self.t.cluster_from_argv.return_value = [node1, node2]
  131. assert self.t.expand('%p', '10') is self.t.ok.return_value
  132. self.t.cluster_from_argv.assert_called_with(('10',))
  133. for node in nodes:
  134. node.expander.assert_called_with('%p')
  135. self.t.ok.assert_called_with('A\nB')
  136. def test_note(self):
  137. self.t.quiet = True
  138. self.t.note('foo')
  139. self.t.say.assert_not_called()
  140. self.t.quiet = False
  141. self.t.note('foo')
  142. self.t.say.assert_called_with('foo', newline=True)
  143. def test_splash(self):
  144. x = MultiTool()
  145. x.note = Mock()
  146. x.nosplash = True
  147. x.splash()
  148. x.note.assert_not_called()
  149. x.nosplash = False
  150. x.splash()
  151. x.note.assert_called()
  152. def test_Cluster(self):
  153. m = MultiTool()
  154. c = m.cluster_from_argv(['A', 'B', 'C'])
  155. assert c.env is m.env
  156. assert c.cmd == 'celery worker'
  157. assert c.on_stopping_preamble == m.on_stopping_preamble
  158. assert c.on_send_signal == m.on_send_signal
  159. assert c.on_still_waiting_for == m.on_still_waiting_for
  160. assert c.on_still_waiting_progress == m.on_still_waiting_progress
  161. assert c.on_still_waiting_end == m.on_still_waiting_end
  162. assert c.on_node_start == m.on_node_start
  163. assert c.on_node_restart == m.on_node_restart
  164. assert c.on_node_shutdown_ok == m.on_node_shutdown_ok
  165. assert c.on_node_status == m.on_node_status
  166. assert c.on_node_signal_dead == m.on_node_signal_dead
  167. assert c.on_node_signal == m.on_node_signal
  168. assert c.on_node_down == m.on_node_down
  169. assert c.on_child_spawn == m.on_child_spawn
  170. assert c.on_child_signalled == m.on_child_signalled
  171. assert c.on_child_failure == m.on_child_failure
  172. def test_on_stopping_preamble(self):
  173. self.t.on_stopping_preamble([])
  174. def test_on_send_signal(self):
  175. self.t.on_send_signal(Mock(), Mock())
  176. def test_on_still_waiting_for(self):
  177. self.t.on_still_waiting_for([Mock(), Mock()])
  178. def test_on_still_waiting_for__empty(self):
  179. self.t.on_still_waiting_for([])
  180. def test_on_still_waiting_progress(self):
  181. self.t.on_still_waiting_progress([])
  182. def test_on_still_waiting_end(self):
  183. self.t.on_still_waiting_end()
  184. def test_on_node_signal_dead(self):
  185. self.t.on_node_signal_dead(Mock())
  186. def test_on_node_start(self):
  187. self.t.on_node_start(Mock())
  188. def test_on_node_restart(self):
  189. self.t.on_node_restart(Mock())
  190. def test_on_node_down(self):
  191. self.t.on_node_down(Mock())
  192. def test_on_node_shutdown_ok(self):
  193. self.t.on_node_shutdown_ok(Mock())
  194. def test_on_node_status__FAIL(self):
  195. self.t.on_node_status(Mock(), 1)
  196. self.t.say.assert_called_with(self.t.FAILED, newline=True)
  197. def test_on_node_status__OK(self):
  198. self.t.on_node_status(Mock(), 0)
  199. self.t.say.assert_called_with(self.t.OK, newline=True)
  200. def test_on_node_signal(self):
  201. self.t.on_node_signal(Mock(), Mock())
  202. def test_on_child_spawn(self):
  203. self.t.on_child_spawn(Mock(), Mock(), Mock())
  204. def test_on_child_signalled(self):
  205. self.t.on_child_signalled(Mock(), Mock())
  206. def test_on_child_failure(self):
  207. self.t.on_child_failure(Mock(), Mock())
  208. def test_constant_strings(self):
  209. assert self.t.OK
  210. assert self.t.DOWN
  211. assert self.t.FAILED
  212. class test_MultiTool_functional:
  213. def setup(self):
  214. self.fh = WhateverIO()
  215. self.env = {}
  216. self.t = MultiTool(env=self.env, fh=self.fh)
  217. def test_note(self):
  218. self.t.note('hello world')
  219. assert self.fh.getvalue() == 'hello world\n'
  220. def test_note_quiet(self):
  221. self.t.quiet = True
  222. self.t.note('hello world')
  223. assert not self.fh.getvalue()
  224. def test_carp(self):
  225. self.t.say = Mock()
  226. self.t.carp('foo')
  227. self.t.say.assert_called_with('foo', True, self.t.stderr)
  228. def test_info(self):
  229. self.t.verbose = True
  230. self.t.info('hello info')
  231. assert self.fh.getvalue() == 'hello info\n'
  232. def test_info_not_verbose(self):
  233. self.t.verbose = False
  234. self.t.info('hello info')
  235. assert not self.fh.getvalue()
  236. def test_error(self):
  237. self.t.carp = Mock()
  238. self.t.usage = Mock()
  239. assert self.t.error('foo') == 1
  240. self.t.carp.assert_called_with('foo')
  241. self.t.usage.assert_called_with()
  242. self.t.carp = Mock()
  243. assert self.t.error() == 1
  244. self.t.carp.assert_not_called()
  245. def test_nosplash(self):
  246. self.t.nosplash = True
  247. self.t.splash()
  248. assert not self.fh.getvalue()
  249. def test_splash(self):
  250. self.t.nosplash = False
  251. self.t.splash()
  252. assert 'celery multi' in self.fh.getvalue()
  253. def test_usage(self):
  254. self.t.usage()
  255. assert self.fh.getvalue()
  256. def test_help(self):
  257. self.t.help([])
  258. assert doc in self.fh.getvalue()
  259. def test_expand(self):
  260. self.t.expand('foo%n', 'ask', 'klask', 'dask')
  261. assert self.fh.getvalue() == 'fooask\nfooklask\nfoodask\n'
  262. @patch('celery.apps.multi.gethostname')
  263. def test_get(self, gethostname):
  264. gethostname.return_value = 'e.com'
  265. self.t.get('xuzzy@e.com', 'foo', 'bar', 'baz')
  266. assert not self.fh.getvalue()
  267. self.t.get('foo@e.com', 'foo', 'bar', 'baz')
  268. assert self.fh.getvalue()
  269. @patch('celery.apps.multi.gethostname')
  270. def test_names(self, gethostname):
  271. gethostname.return_value = 'e.com'
  272. self.t.names('foo', 'bar', 'baz')
  273. assert 'foo@e.com\nbar@e.com\nbaz@e.com' in self.fh.getvalue()
  274. def test_execute_from_commandline(self):
  275. start = self.t.commands['start'] = Mock()
  276. self.t.error = Mock()
  277. self.t.execute_from_commandline(['multi', 'start', 'foo', 'bar'])
  278. self.t.error.assert_not_called()
  279. start.assert_called_with('foo', 'bar')
  280. self.t.error = Mock()
  281. self.t.execute_from_commandline(['multi', 'frob', 'foo', 'bar'])
  282. self.t.error.assert_called_with('Invalid command: frob')
  283. self.t.error = Mock()
  284. self.t.execute_from_commandline(['multi'])
  285. self.t.error.assert_called_with()
  286. self.t.error = Mock()
  287. self.t.execute_from_commandline(['multi', '-foo'])
  288. self.t.error.assert_called_with()
  289. self.t.execute_from_commandline(
  290. ['multi', 'start', 'foo',
  291. '--nosplash', '--quiet', '-q', '--verbose', '--no-color'],
  292. )
  293. assert self.t.nosplash
  294. assert self.t.quiet
  295. assert self.t.verbose
  296. assert self.t.no_color
  297. @patch('celery.bin.multi.MultiTool')
  298. def test_main(self, MultiTool):
  299. m = MultiTool.return_value = Mock()
  300. with pytest.raises(SystemExit):
  301. main()
  302. m.execute_from_commandline.assert_called_with(sys.argv)