test_supervisord.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
  1. import unittest
  2. import time
  3. import signal
  4. import sys
  5. import os
  6. import tempfile
  7. import shutil
  8. from supervisor.tests.base import DummyOptions
  9. from supervisor.tests.base import DummyPConfig
  10. from supervisor.tests.base import DummyPGroupConfig
  11. from supervisor.tests.base import DummyProcess
  12. from supervisor.tests.base import DummyProcessGroup
  13. from supervisor.tests.base import DummyDispatcher
  14. class EntryPointTests(unittest.TestCase):
  15. def test_main_noprofile(self):
  16. from supervisor.supervisord import main
  17. conf = os.path.join(
  18. os.path.abspath(os.path.dirname(__file__)), 'fixtures',
  19. 'donothing.conf')
  20. import StringIO
  21. new_stdout = StringIO.StringIO()
  22. old_stdout = sys.stdout
  23. try:
  24. tempdir = tempfile.mkdtemp()
  25. log = os.path.join(tempdir, 'log')
  26. pid = os.path.join(tempdir, 'pid')
  27. sys.stdout = new_stdout
  28. main(args=['-c', conf, '-l', log, '-j', pid, '-n'],
  29. test=True)
  30. finally:
  31. sys.stdout = old_stdout
  32. shutil.rmtree(tempdir)
  33. output = new_stdout.getvalue()
  34. self.assertTrue(output.find('supervisord started') != 1, output)
  35. if sys.version_info[:2] >= (2, 4):
  36. def test_main_profile(self):
  37. from supervisor.supervisord import main
  38. conf = os.path.join(
  39. os.path.abspath(os.path.dirname(__file__)), 'fixtures',
  40. 'donothing.conf')
  41. import StringIO
  42. new_stdout = StringIO.StringIO()
  43. old_stdout = sys.stdout
  44. try:
  45. tempdir = tempfile.mkdtemp()
  46. log = os.path.join(tempdir, 'log')
  47. pid = os.path.join(tempdir, 'pid')
  48. sys.stdout = new_stdout
  49. main(args=['-c', conf, '-l', log, '-j', pid, '-n',
  50. '--profile_options=cumulative,calls'], test=True)
  51. finally:
  52. sys.stdout = old_stdout
  53. shutil.rmtree(tempdir)
  54. output = new_stdout.getvalue()
  55. self.assertTrue(output.find('cumulative time, call count') != -1,
  56. output)
  57. class SupervisordTests(unittest.TestCase):
  58. def tearDown(self):
  59. from supervisor.events import clear
  60. clear()
  61. def _getTargetClass(self):
  62. from supervisor.supervisord import Supervisor
  63. return Supervisor
  64. def _makeOne(self, options):
  65. return self._getTargetClass()(options)
  66. def test_main_first(self):
  67. options = DummyOptions()
  68. pconfig = DummyPConfig(options, 'foo', 'foo', '/bin/foo')
  69. gconfigs = [DummyPGroupConfig(options,'foo', pconfigs=[pconfig])]
  70. options.process_group_configs = gconfigs
  71. options.test = True
  72. options.first = True
  73. supervisord = self._makeOne(options)
  74. supervisord.main()
  75. self.assertEqual(options.environment_processed, True)
  76. self.assertEqual(options.fds_cleaned_up, False)
  77. self.assertEqual(options.rlimits_set, True)
  78. self.assertEqual(options.make_logger_messages,
  79. (['setuid_called'], [], ['rlimits_set']))
  80. self.assertEqual(options.autochildlogdir_cleared, True)
  81. self.assertEqual(len(supervisord.process_groups), 1)
  82. self.assertEqual(supervisord.process_groups['foo'].config.options,
  83. options)
  84. self.assertEqual(options.environment_processed, True)
  85. self.assertEqual(options.httpservers_opened, True)
  86. self.assertEqual(options.signals_set, True)
  87. self.assertEqual(options.daemonized, True)
  88. self.assertEqual(options.pidfile_written, True)
  89. self.assertEqual(options.cleaned_up, True)
  90. def test_main_notfirst(self):
  91. options = DummyOptions()
  92. pconfig = DummyPConfig(options, 'foo', 'foo', '/bin/foo')
  93. gconfigs = [DummyPGroupConfig(options,'foo', pconfigs=[pconfig])]
  94. options.process_group_configs = gconfigs
  95. options.test = True
  96. options.first = False
  97. supervisord = self._makeOne(options)
  98. supervisord.main()
  99. self.assertEqual(options.environment_processed, True)
  100. self.assertEqual(options.fds_cleaned_up, True)
  101. self.assertFalse(hasattr(options, 'rlimits_set'))
  102. self.assertEqual(options.make_logger_messages,
  103. (['setuid_called'], [], []))
  104. self.assertEqual(options.autochildlogdir_cleared, True)
  105. self.assertEqual(len(supervisord.process_groups), 1)
  106. self.assertEqual(supervisord.process_groups['foo'].config.options,
  107. options)
  108. self.assertEqual(options.environment_processed, True)
  109. self.assertEqual(options.httpservers_opened, True)
  110. self.assertEqual(options.signals_set, True)
  111. self.assertEqual(options.daemonized, False)
  112. self.assertEqual(options.pidfile_written, True)
  113. self.assertEqual(options.cleaned_up, True)
  114. def test_reap(self):
  115. options = DummyOptions()
  116. options.waitpid_return = 1, 1
  117. pconfig = DummyPConfig(options, 'process', 'process', '/bin/process1')
  118. process = DummyProcess(pconfig)
  119. process.drained = False
  120. process.killing = 1
  121. process.laststop = None
  122. process.waitstatus = None, None
  123. options.pidhistory = {1:process}
  124. supervisord = self._makeOne(options)
  125. supervisord.reap(once=True)
  126. self.assertEqual(process.finished, (1,1))
  127. def test_handle_sigterm(self):
  128. options = DummyOptions()
  129. options._signal = signal.SIGTERM
  130. supervisord = self._makeOne(options)
  131. supervisord.handle_signal()
  132. self.assertEqual(supervisord.options.mood, -1)
  133. self.assertEqual(options.logger.data[0],
  134. 'received SIGTERM indicating exit request')
  135. def test_handle_sigint(self):
  136. options = DummyOptions()
  137. options._signal = signal.SIGINT
  138. supervisord = self._makeOne(options)
  139. supervisord.handle_signal()
  140. self.assertEqual(supervisord.options.mood, -1)
  141. self.assertEqual(options.logger.data[0],
  142. 'received SIGINT indicating exit request')
  143. def test_handle_sigquit(self):
  144. options = DummyOptions()
  145. options._signal = signal.SIGQUIT
  146. supervisord = self._makeOne(options)
  147. supervisord.handle_signal()
  148. self.assertEqual(supervisord.options.mood, -1)
  149. self.assertEqual(options.logger.data[0],
  150. 'received SIGQUIT indicating exit request')
  151. def test_handle_sighup(self):
  152. options = DummyOptions()
  153. options._signal = signal.SIGHUP
  154. supervisord = self._makeOne(options)
  155. supervisord.handle_signal()
  156. self.assertEqual(supervisord.options.mood, 0)
  157. self.assertEqual(options.logger.data[0],
  158. 'received SIGHUP indicating restart request')
  159. def test_handle_sigusr2(self):
  160. options = DummyOptions()
  161. options._signal = signal.SIGUSR2
  162. pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
  163. from supervisor.process import ProcessStates
  164. process1 = DummyProcess(pconfig1, state=ProcessStates.STOPPING)
  165. process1.delay = time.time() - 1
  166. supervisord = self._makeOne(options)
  167. pconfigs = [DummyPConfig(options, 'foo', 'foo', '/bin/foo')]
  168. options.process_group_configs = DummyPGroupConfig(
  169. options, 'foo',
  170. pconfigs=pconfigs)
  171. supervisord.handle_signal()
  172. self.assertEqual(supervisord.options.mood, 1)
  173. self.assertEqual(options.logs_reopened, True)
  174. self.assertEqual(options.logger.data[0],
  175. 'received SIGUSR2 indicating log reopen request')
  176. def test_handle_unknown_signal(self):
  177. options = DummyOptions()
  178. options._signal = signal.SIGUSR1
  179. supervisord = self._makeOne(options)
  180. supervisord.handle_signal()
  181. self.assertEqual(supervisord.options.mood, 1)
  182. self.assertEqual(options.logger.data[0],
  183. 'received SIGUSR1 indicating nothing')
  184. def test_diff_add_remove(self):
  185. options = DummyOptions()
  186. supervisord = self._makeOne(options)
  187. pconfig = DummyPConfig(options, 'process1', 'process1')
  188. group1 = DummyPGroupConfig(options, 'group1', pconfigs=[pconfig])
  189. pconfig = DummyPConfig(options, 'process2', 'process2')
  190. group2 = DummyPGroupConfig(options, 'group2', pconfigs=[pconfig])
  191. new = [group1, group2]
  192. added, changed, removed = supervisord.diff_to_active()
  193. self.assertEqual(added, [])
  194. self.assertEqual(changed, [])
  195. self.assertEqual(removed, [])
  196. added, changed, removed = supervisord.diff_to_active(new)
  197. self.assertEqual(added, new)
  198. self.assertEqual(changed, [])
  199. self.assertEqual(removed, [])
  200. supervisord.options.process_group_configs = new
  201. added, changed, removed = supervisord.diff_to_active()
  202. self.assertEqual(added, new)
  203. supervisord.add_process_group(group1)
  204. supervisord.add_process_group(group2)
  205. pconfig = DummyPConfig(options, 'process3', 'process3')
  206. new_group1 = DummyPGroupConfig(options, pconfigs=[pconfig])
  207. pconfig = DummyPConfig(options, 'process4', 'process4')
  208. new_group2 = DummyPGroupConfig(options, pconfigs=[pconfig])
  209. new = [group2, new_group1, new_group2]
  210. added, changed, removed = supervisord.diff_to_active(new)
  211. self.assertEqual(added, [new_group1, new_group2])
  212. self.assertEqual(changed, [])
  213. self.assertEqual(removed, [group1])
  214. def test_diff_changed(self):
  215. from supervisor.options import ProcessConfig, ProcessGroupConfig
  216. options = DummyOptions()
  217. supervisord = self._makeOne(options)
  218. def make_pconfig(name, command, **params):
  219. result = {
  220. 'name': name, 'command': command,
  221. 'directory': None, 'umask': None, 'priority': 999, 'autostart': True,
  222. 'autorestart': True, 'startsecs': 10, 'startretries': 999,
  223. 'uid': None, 'stdout_logfile': None, 'stdout_capture_maxbytes': 0,
  224. 'stdout_events_enabled': False,
  225. 'stdout_logfile_backups': 0, 'stdout_logfile_maxbytes': 0,
  226. 'stdout_syslog': False,
  227. 'stderr_logfile': None, 'stderr_capture_maxbytes': 0,
  228. 'stderr_events_enabled': False,
  229. 'stderr_logfile_backups': 0, 'stderr_logfile_maxbytes': 0,
  230. 'stderr_syslog': False,
  231. 'redirect_stderr': False,
  232. 'stopsignal': None, 'stopwaitsecs': 10,
  233. 'stopasgroup': False,
  234. 'killasgroup': False,
  235. 'exitcodes': (0,2), 'environment': None, 'serverurl': None,
  236. }
  237. result.update(params)
  238. return ProcessConfig(options, **result)
  239. def make_gconfig(name, pconfigs):
  240. return ProcessGroupConfig(options, name, 25, pconfigs)
  241. pconfig = make_pconfig('process1', 'process1', uid='new')
  242. group1 = make_gconfig('group1', [pconfig])
  243. pconfig = make_pconfig('process2', 'process2')
  244. group2 = make_gconfig('group2', [pconfig])
  245. new = [group1, group2]
  246. pconfig = make_pconfig('process1', 'process1', uid='old')
  247. group3 = make_gconfig('group1', [pconfig])
  248. pconfig = make_pconfig('process2', 'process2')
  249. group4 = make_gconfig('group2', [pconfig])
  250. supervisord.add_process_group(group3)
  251. supervisord.add_process_group(group4)
  252. added, changed, removed = supervisord.diff_to_active(new)
  253. self.assertEqual([added, removed], [[], []])
  254. self.assertEqual(changed, [group1])
  255. options = DummyOptions()
  256. supervisord = self._makeOne(options)
  257. pconfig1 = make_pconfig('process1', 'process1')
  258. pconfig2 = make_pconfig('process2', 'process2')
  259. group1 = make_gconfig('group1', [pconfig1, pconfig2])
  260. new = [group1]
  261. supervisord.add_process_group(make_gconfig('group1', [pconfig1]))
  262. added, changed, removed = supervisord.diff_to_active(new)
  263. self.assertEqual([added, removed], [[], []])
  264. self.assertEqual(changed, [group1])
  265. def test_add_process_group(self):
  266. options = DummyOptions()
  267. pconfig = DummyPConfig(options, 'foo', 'foo', '/bin/foo')
  268. gconfig = DummyPGroupConfig(options,'foo', pconfigs=[pconfig])
  269. options.process_group_configs = [gconfig]
  270. supervisord = self._makeOne(options)
  271. self.assertEqual(supervisord.process_groups, {})
  272. result = supervisord.add_process_group(gconfig)
  273. self.assertEqual(supervisord.process_groups.keys(), ['foo'])
  274. self.assertTrue(result)
  275. group = supervisord.process_groups['foo']
  276. result = supervisord.add_process_group(gconfig)
  277. self.assertEqual(group, supervisord.process_groups['foo'])
  278. self.assertTrue(not result)
  279. def test_add_process_group_event(self):
  280. from supervisor import events
  281. L = []
  282. def callback(event):
  283. L.append(1)
  284. events.subscribe(events.ProcessGroupAddedEvent, callback)
  285. options = DummyOptions()
  286. pconfig = DummyPConfig(options, 'foo', 'foo', '/bin/foo')
  287. gconfig = DummyPGroupConfig(options,'foo', pconfigs=[pconfig])
  288. options.process_group_configs = [gconfig]
  289. supervisord = self._makeOne(options)
  290. supervisord.add_process_group(gconfig)
  291. options.test = True
  292. supervisord.runforever()
  293. self.assertEqual(L, [1])
  294. def test_remove_process_group(self):
  295. options = DummyOptions()
  296. pconfig = DummyPConfig(options, 'foo', 'foo', '/bin/foo')
  297. gconfig = DummyPGroupConfig(options, 'foo', pconfigs=[pconfig])
  298. supervisord = self._makeOne(options)
  299. self.assertRaises(KeyError, supervisord.remove_process_group, 'asdf')
  300. supervisord.add_process_group(gconfig)
  301. result = supervisord.remove_process_group('foo')
  302. self.assertEqual(supervisord.process_groups, {})
  303. self.assertTrue(result)
  304. supervisord.add_process_group(gconfig)
  305. supervisord.process_groups['foo'].unstopped_processes = [DummyProcess(None)]
  306. result = supervisord.remove_process_group('foo')
  307. self.assertEqual(supervisord.process_groups.keys(), ['foo'])
  308. self.assertTrue(not result)
  309. def test_remove_process_group_event(self):
  310. from supervisor import events
  311. L = []
  312. def callback(event):
  313. L.append(1)
  314. events.subscribe(events.ProcessGroupRemovedEvent, callback)
  315. options = DummyOptions()
  316. pconfig = DummyPConfig(options, 'foo', 'foo', '/bin/foo')
  317. gconfig = DummyPGroupConfig(options,'foo', pconfigs=[pconfig])
  318. options.process_group_configs = [gconfig]
  319. supervisord = self._makeOne(options)
  320. supervisord.add_process_group(gconfig)
  321. supervisord.process_groups['foo'].stopped_processes = [DummyProcess(None)]
  322. supervisord.remove_process_group('foo')
  323. options.test = True
  324. supervisord.runforever()
  325. self.assertEqual(L, [1])
  326. def test_runforever_emits_generic_startup_event(self):
  327. from supervisor import events
  328. L = []
  329. def callback(event):
  330. L.append(1)
  331. events.subscribe(events.SupervisorStateChangeEvent, callback)
  332. options = DummyOptions()
  333. supervisord = self._makeOne(options)
  334. options.test = True
  335. supervisord.runforever()
  336. self.assertEqual(L, [1])
  337. def test_runforever_emits_generic_specific_event(self):
  338. from supervisor import events
  339. L = []
  340. def callback(event):
  341. L.append(2)
  342. events.subscribe(events.SupervisorRunningEvent, callback)
  343. options = DummyOptions()
  344. options.test = True
  345. supervisord = self._makeOne(options)
  346. supervisord.runforever()
  347. self.assertEqual(L, [2])
  348. def test_runforever_calls_tick(self):
  349. options = DummyOptions()
  350. options.test = True
  351. supervisord = self._makeOne(options)
  352. self.assertEqual(len(supervisord.ticks), 0)
  353. supervisord.runforever()
  354. self.assertEqual(len(supervisord.ticks), 3)
  355. def test_runforever_select_eintr(self):
  356. options = DummyOptions()
  357. import errno
  358. options.select_error = errno.EINTR
  359. supervisord = self._makeOne(options)
  360. options.test = True
  361. supervisord.runforever()
  362. self.assertEqual(options.logger.data[0], 'EINTR encountered in select')
  363. def test_runforever_select_uncaught_exception(self):
  364. options = DummyOptions()
  365. import errno
  366. options.select_error = errno.EBADF
  367. supervisord = self._makeOne(options)
  368. import select
  369. options.test = True
  370. self.assertRaises(select.error, supervisord.runforever)
  371. def test_runforever_select_dispatchers(self):
  372. options = DummyOptions()
  373. supervisord = self._makeOne(options)
  374. pconfig = DummyPConfig(options, 'foo', '/bin/foo',)
  375. gconfig = DummyPGroupConfig(options, pconfigs=[pconfig])
  376. pgroup = DummyProcessGroup(gconfig)
  377. readable = DummyDispatcher(readable=True)
  378. writable = DummyDispatcher(writable=True)
  379. error = DummyDispatcher(writable=True, error=OSError)
  380. pgroup.dispatchers = {6:readable, 7:writable, 8:error}
  381. supervisord.process_groups = {'foo': pgroup}
  382. options.select_result = [6], [7, 8], []
  383. options.test = True
  384. supervisord.runforever()
  385. self.assertEqual(pgroup.transitioned, True)
  386. self.assertEqual(readable.read_event_handled, True)
  387. self.assertEqual(writable.write_event_handled, True)
  388. self.assertEqual(error.error_handled, True)
  389. def test_runforever_select_dispatcher_exitnow(self):
  390. options = DummyOptions()
  391. supervisord = self._makeOne(options)
  392. pconfig = DummyPConfig(options, 'foo', '/bin/foo',)
  393. gconfig = DummyPGroupConfig(options, pconfigs=[pconfig])
  394. pgroup = DummyProcessGroup(gconfig)
  395. from supervisor.medusa import asyncore_25 as asyncore
  396. exitnow = DummyDispatcher(readable=True, error=asyncore.ExitNow)
  397. pgroup.dispatchers = {6:exitnow}
  398. supervisord.process_groups = {'foo': pgroup}
  399. options.select_result = [6], [], []
  400. options.test = True
  401. self.assertRaises(asyncore.ExitNow, supervisord.runforever)
  402. def test_runforever_stopping_emits_events(self):
  403. options = DummyOptions()
  404. supervisord = self._makeOne(options)
  405. gconfig = DummyPGroupConfig(options)
  406. pgroup = DummyProcessGroup(gconfig)
  407. supervisord.process_groups = {'foo': pgroup}
  408. supervisord.options.mood = -1
  409. L = []
  410. def callback(event):
  411. L.append(event)
  412. from supervisor import events
  413. events.subscribe(events.SupervisorStateChangeEvent, callback)
  414. from supervisor.medusa import asyncore_25 as asyncore
  415. options.test = True
  416. self.assertRaises(asyncore.ExitNow, supervisord.runforever)
  417. self.assertTrue(pgroup.all_stopped)
  418. self.assertTrue(isinstance(L[0], events.SupervisorRunningEvent))
  419. self.assertTrue(isinstance(L[0], events.SupervisorStateChangeEvent))
  420. self.assertTrue(isinstance(L[1], events.SupervisorStoppingEvent))
  421. self.assertTrue(isinstance(L[1], events.SupervisorStateChangeEvent))
  422. def test_exit(self):
  423. options = DummyOptions()
  424. supervisord = self._makeOne(options)
  425. pconfig = DummyPConfig(options, 'foo', '/bin/foo',)
  426. gconfig = DummyPGroupConfig(options, pconfigs=[pconfig])
  427. pgroup = DummyProcessGroup(gconfig)
  428. L = []
  429. def callback():
  430. L.append(1)
  431. supervisord.process_groups = {'foo': pgroup}
  432. supervisord.options.mood = 0
  433. supervisord.options.test = True
  434. from supervisor.medusa import asyncore_25 as asyncore
  435. self.assertRaises(asyncore.ExitNow, supervisord.runforever)
  436. self.assertEqual(pgroup.all_stopped, True)
  437. def test_exit_delayed(self):
  438. options = DummyOptions()
  439. supervisord = self._makeOne(options)
  440. pconfig = DummyPConfig(options, 'foo', '/bin/foo',)
  441. process = DummyProcess(pconfig)
  442. gconfig = DummyPGroupConfig(options, pconfigs=[pconfig])
  443. pgroup = DummyProcessGroup(gconfig)
  444. pgroup.unstopped_processes = [process]
  445. L = []
  446. def callback():
  447. L.append(1)
  448. supervisord.process_groups = {'foo': pgroup}
  449. supervisord.options.mood = 0
  450. supervisord.options.test = True
  451. supervisord.runforever()
  452. self.assertNotEqual(supervisord.lastshutdownreport, 0)
  453. def test_getSupervisorStateDescription(self):
  454. from supervisor.states import getSupervisorStateDescription
  455. from supervisor.states import SupervisorStates
  456. result = getSupervisorStateDescription(SupervisorStates.RUNNING)
  457. self.assertEqual(result, 'RUNNING')
  458. def test_tick(self):
  459. from supervisor import events
  460. L = []
  461. def callback(event):
  462. L.append(event)
  463. events.subscribe(events.TickEvent, callback)
  464. options = DummyOptions()
  465. supervisord = self._makeOne(options)
  466. supervisord.tick(now=0)
  467. self.assertEqual(supervisord.ticks[5], 0)
  468. self.assertEqual(supervisord.ticks[60], 0)
  469. self.assertEqual(supervisord.ticks[3600], 0)
  470. self.assertEqual(len(L), 0)
  471. supervisord.tick(now=6)
  472. self.assertEqual(supervisord.ticks[5], 5)
  473. self.assertEqual(supervisord.ticks[60], 0)
  474. self.assertEqual(supervisord.ticks[3600], 0)
  475. self.assertEqual(len(L), 1)
  476. self.assertEqual(L[-1].__class__, events.Tick5Event)
  477. supervisord.tick(now=61)
  478. self.assertEqual(supervisord.ticks[5], 60)
  479. self.assertEqual(supervisord.ticks[60], 60)
  480. self.assertEqual(supervisord.ticks[3600], 0)
  481. self.assertEqual(len(L), 3)
  482. self.assertEqual(L[-1].__class__, events.Tick60Event)
  483. supervisord.tick(now=3601)
  484. self.assertEqual(supervisord.ticks[5], 3600)
  485. self.assertEqual(supervisord.ticks[60], 3600)
  486. self.assertEqual(supervisord.ticks[3600], 3600)
  487. self.assertEqual(len(L), 6)
  488. self.assertEqual(L[-1].__class__, events.Tick3600Event)
  489. def test_suite():
  490. return unittest.findTestCases(sys.modules[__name__])
  491. if __name__ == '__main__':
  492. unittest.main(defaultTest='test_suite')