test_task.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722
  1. import unittest2 as unittest
  2. from StringIO import StringIO
  3. from datetime import datetime, timedelta
  4. from pyparsing import ParseException
  5. from celery import conf
  6. from celery import task
  7. from celery import messaging
  8. from celery.task.schedules import crontab, crontab_parser
  9. from celery.utils import timeutils
  10. from celery.utils import gen_unique_id
  11. from celery.utils.functional import wraps
  12. from celery.result import EagerResult
  13. from celery.execute import send_task
  14. from celery.backends import default_backend
  15. from celery.decorators import task as task_dec
  16. from celery.exceptions import RetryTaskError
  17. from celery.worker.listener import parse_iso8601
  18. def return_True(*args, **kwargs):
  19. # Task run functions can't be closures/lambdas, as they're pickled.
  20. return True
  21. return_True_task = task_dec()(return_True)
  22. def raise_exception(self, **kwargs):
  23. raise Exception("%s error" % self.__class__)
  24. class MockApplyTask(task.Task):
  25. def run(self, x, y):
  26. return x * y
  27. @classmethod
  28. def apply_async(self, *args, **kwargs):
  29. pass
  30. class IncrementCounterTask(task.Task):
  31. name = "c.unittest.increment_counter_task"
  32. count = 0
  33. def run(self, increment_by=1, **kwargs):
  34. increment_by = increment_by or 1
  35. self.__class__.count += increment_by
  36. return self.__class__.count
  37. class RaisingTask(task.Task):
  38. name = "c.unittest.raising_task"
  39. def run(self, **kwargs):
  40. raise KeyError("foo")
  41. class RetryTask(task.Task):
  42. max_retries = 3
  43. iterations = 0
  44. def run(self, arg1, arg2, kwarg=1, **kwargs):
  45. self.__class__.iterations += 1
  46. retries = kwargs["task_retries"]
  47. if retries >= 3:
  48. return arg1
  49. else:
  50. kwargs.update({"kwarg": kwarg})
  51. return self.retry(args=[arg1, arg2], kwargs=kwargs, countdown=0)
  52. class RetryTaskNoArgs(task.Task):
  53. max_retries = 3
  54. iterations = 0
  55. def run(self, **kwargs):
  56. self.__class__.iterations += 1
  57. retries = kwargs["task_retries"]
  58. if retries >= 3:
  59. return 42
  60. else:
  61. return self.retry(kwargs=kwargs, countdown=0)
  62. class RetryTaskMockApply(task.Task):
  63. max_retries = 3
  64. iterations = 0
  65. applied = 0
  66. def run(self, arg1, arg2, kwarg=1, **kwargs):
  67. self.__class__.iterations += 1
  68. retries = kwargs["task_retries"]
  69. if retries >= 3:
  70. return arg1
  71. else:
  72. kwargs.update({"kwarg": kwarg})
  73. return self.retry(args=[arg1, arg2], kwargs=kwargs, countdown=0)
  74. @classmethod
  75. def apply_async(self, *args, **kwargs):
  76. self.applied = 1
  77. class MyCustomException(Exception):
  78. """Random custom exception."""
  79. class RetryTaskCustomExc(task.Task):
  80. max_retries = 3
  81. iterations = 0
  82. def run(self, arg1, arg2, kwarg=1, **kwargs):
  83. self.__class__.iterations += 1
  84. retries = kwargs["task_retries"]
  85. if retries >= 3:
  86. return arg1 + kwarg
  87. else:
  88. try:
  89. raise MyCustomException("Elaine Marie Benes")
  90. except MyCustomException, exc:
  91. kwargs.update({"kwarg": kwarg})
  92. return self.retry(args=[arg1, arg2], kwargs=kwargs,
  93. countdown=0, exc=exc)
  94. class TestTaskRetries(unittest.TestCase):
  95. def test_retry(self):
  96. RetryTask.max_retries = 3
  97. RetryTask.iterations = 0
  98. result = RetryTask.apply([0xFF, 0xFFFF])
  99. self.assertEqual(result.get(), 0xFF)
  100. self.assertEqual(RetryTask.iterations, 4)
  101. def test_retry_no_args(self):
  102. RetryTaskNoArgs.max_retries = 3
  103. RetryTaskNoArgs.iterations = 0
  104. result = RetryTaskNoArgs.apply()
  105. self.assertEqual(result.get(), 42)
  106. self.assertEqual(RetryTaskNoArgs.iterations, 4)
  107. def test_retry_not_eager(self):
  108. exc = Exception("baz")
  109. try:
  110. RetryTaskMockApply.retry(args=[4, 4], kwargs={},
  111. exc=exc, throw=False)
  112. self.assertTrue(RetryTaskMockApply.applied)
  113. finally:
  114. RetryTaskMockApply.applied = 0
  115. try:
  116. self.assertRaises(RetryTaskError, RetryTaskMockApply.retry,
  117. args=[4, 4], kwargs={}, exc=exc, throw=True)
  118. self.assertTrue(RetryTaskMockApply.applied)
  119. finally:
  120. RetryTaskMockApply.applied = 0
  121. def test_retry_with_kwargs(self):
  122. RetryTaskCustomExc.max_retries = 3
  123. RetryTaskCustomExc.iterations = 0
  124. result = RetryTaskCustomExc.apply([0xFF, 0xFFFF], {"kwarg": 0xF})
  125. self.assertEqual(result.get(), 0xFF + 0xF)
  126. self.assertEqual(RetryTaskCustomExc.iterations, 4)
  127. def test_retry_with_custom_exception(self):
  128. RetryTaskCustomExc.max_retries = 2
  129. RetryTaskCustomExc.iterations = 0
  130. result = RetryTaskCustomExc.apply([0xFF, 0xFFFF], {"kwarg": 0xF})
  131. self.assertRaises(MyCustomException,
  132. result.get)
  133. self.assertEqual(RetryTaskCustomExc.iterations, 3)
  134. def test_max_retries_exceeded(self):
  135. RetryTask.max_retries = 2
  136. RetryTask.iterations = 0
  137. result = RetryTask.apply([0xFF, 0xFFFF])
  138. self.assertRaises(RetryTask.MaxRetriesExceededError,
  139. result.get)
  140. self.assertEqual(RetryTask.iterations, 3)
  141. RetryTask.max_retries = 1
  142. RetryTask.iterations = 0
  143. result = RetryTask.apply([0xFF, 0xFFFF])
  144. self.assertRaises(RetryTask.MaxRetriesExceededError,
  145. result.get)
  146. self.assertEqual(RetryTask.iterations, 2)
  147. class MockPublisher(object):
  148. def __init__(self, *args, **kwargs):
  149. self.kwargs = kwargs
  150. class TestCeleryTasks(unittest.TestCase):
  151. def createTaskCls(self, cls_name, task_name=None):
  152. attrs = {"__module__": self.__module__}
  153. if task_name:
  154. attrs["name"] = task_name
  155. cls = type(cls_name, (task.Task, ), attrs)
  156. cls.run = return_True
  157. return cls
  158. def test_AsyncResult(self):
  159. task_id = gen_unique_id()
  160. result = RetryTask.AsyncResult(task_id)
  161. self.assertEqual(result.backend, RetryTask.backend)
  162. self.assertEqual(result.task_id, task_id)
  163. def test_ping(self):
  164. from celery import conf
  165. conf.ALWAYS_EAGER = True
  166. self.assertEqual(task.ping(), 'pong')
  167. conf.ALWAYS_EAGER = False
  168. def test_execute_remote(self):
  169. from celery import conf
  170. conf.ALWAYS_EAGER = True
  171. self.assertEqual(task.execute_remote(return_True, ["foo"]).get(),
  172. True)
  173. conf.ALWAYS_EAGER = False
  174. def test_dmap(self):
  175. from celery import conf
  176. import operator
  177. conf.ALWAYS_EAGER = True
  178. res = task.dmap(operator.add, zip(xrange(10), xrange(10)))
  179. self.assertEqual(sum(res), sum(operator.add(x, x)
  180. for x in xrange(10)))
  181. conf.ALWAYS_EAGER = False
  182. def test_dmap_async(self):
  183. from celery import conf
  184. import operator
  185. conf.ALWAYS_EAGER = True
  186. res = task.dmap_async(operator.add, zip(xrange(10), xrange(10)))
  187. self.assertEqual(sum(res.get()), sum(operator.add(x, x)
  188. for x in xrange(10)))
  189. conf.ALWAYS_EAGER = False
  190. def assertNextTaskDataEqual(self, consumer, presult, task_name,
  191. test_eta=False, **kwargs):
  192. next_task = consumer.fetch()
  193. task_data = next_task.decode()
  194. self.assertEqual(task_data["id"], presult.task_id)
  195. self.assertEqual(task_data["task"], task_name)
  196. task_kwargs = task_data.get("kwargs", {})
  197. if test_eta:
  198. self.assertIsInstance(task_data.get("eta"), basestring)
  199. to_datetime = parse_iso8601(task_data.get("eta"))
  200. self.assertIsInstance(to_datetime, datetime)
  201. for arg_name, arg_value in kwargs.items():
  202. self.assertEqual(task_kwargs.get(arg_name), arg_value)
  203. def test_incomplete_task_cls(self):
  204. class IncompleteTask(task.Task):
  205. name = "c.unittest.t.itask"
  206. self.assertRaises(NotImplementedError, IncompleteTask().run)
  207. def test_task_kwargs_must_be_dictionary(self):
  208. self.assertRaises(ValueError, IncrementCounterTask.apply_async,
  209. [], "str")
  210. def test_task_args_must_be_list(self):
  211. self.assertRaises(ValueError, IncrementCounterTask.apply_async,
  212. "str", {})
  213. def test_regular_task(self):
  214. T1 = self.createTaskCls("T1", "c.unittest.t.t1")
  215. self.assertIsInstance(T1(), T1)
  216. self.assertTrue(T1().run())
  217. self.assertTrue(callable(T1()),
  218. "Task class is callable()")
  219. self.assertTrue(T1()(),
  220. "Task class runs run() when called")
  221. # task name generated out of class module + name.
  222. T2 = self.createTaskCls("T2")
  223. self.assertTrue(T2().name.endswith("test_task.T2"))
  224. t1 = T1()
  225. consumer = t1.get_consumer()
  226. self.assertRaises(NotImplementedError, consumer.receive, "foo", "foo")
  227. consumer.discard_all()
  228. self.assertIsNone(consumer.fetch())
  229. # Without arguments.
  230. presult = t1.delay()
  231. self.assertNextTaskDataEqual(consumer, presult, t1.name)
  232. # With arguments.
  233. presult2 = t1.apply_async(kwargs=dict(name="George Constanza"))
  234. self.assertNextTaskDataEqual(consumer, presult2, t1.name,
  235. name="George Constanza")
  236. # send_task
  237. sresult = send_task(t1.name, kwargs=dict(name="Elaine M. Benes"))
  238. self.assertNextTaskDataEqual(consumer, sresult, t1.name,
  239. name="Elaine M. Benes")
  240. # With eta.
  241. presult2 = task.apply_async(t1, kwargs=dict(name="George Constanza"),
  242. eta=datetime.now() + timedelta(days=1))
  243. self.assertNextTaskDataEqual(consumer, presult2, t1.name,
  244. name="George Constanza", test_eta=True)
  245. # With countdown.
  246. presult2 = task.apply_async(t1, kwargs=dict(name="George Constanza"),
  247. countdown=10)
  248. self.assertNextTaskDataEqual(consumer, presult2, t1.name,
  249. name="George Constanza", test_eta=True)
  250. # Discarding all tasks.
  251. consumer.discard_all()
  252. task.apply_async(t1)
  253. self.assertEqual(consumer.discard_all(), 1)
  254. self.assertIsNone(consumer.fetch())
  255. self.assertFalse(presult.successful())
  256. default_backend.mark_as_done(presult.task_id, result=None)
  257. self.assertTrue(presult.successful())
  258. publisher = t1.get_publisher()
  259. self.assertIsInstance(publisher, messaging.TaskPublisher)
  260. def test_get_publisher(self):
  261. from celery.task import base
  262. old_pub = base.TaskPublisher
  263. base.TaskPublisher = MockPublisher
  264. try:
  265. p = IncrementCounterTask.get_publisher(exchange="foo",
  266. connection="bar")
  267. self.assertEqual(p.kwargs["exchange"], "foo")
  268. finally:
  269. base.TaskPublisher = old_pub
  270. def test_get_logger(self):
  271. T1 = self.createTaskCls("T1", "c.unittest.t.t1")
  272. t1 = T1()
  273. logfh = StringIO()
  274. logger = t1.get_logger(logfile=logfh, loglevel=0)
  275. self.assertTrue(logger)
  276. class TestTaskSet(unittest.TestCase):
  277. def test_function_taskset(self):
  278. from celery import conf
  279. conf.ALWAYS_EAGER = True
  280. ts = task.TaskSet(return_True_task.name, [
  281. ([1], {}), [[2], {}], [[3], {}], [[4], {}], [[5], {}]])
  282. res = ts.apply_async()
  283. self.assertListEqual(res.join(), [True, True, True, True, True])
  284. conf.ALWAYS_EAGER = False
  285. def test_counter_taskset(self):
  286. IncrementCounterTask.count = 0
  287. ts = task.TaskSet(IncrementCounterTask, [
  288. ([], {}),
  289. ([], {"increment_by": 2}),
  290. ([], {"increment_by": 3}),
  291. ([], {"increment_by": 4}),
  292. ([], {"increment_by": 5}),
  293. ([], {"increment_by": 6}),
  294. ([], {"increment_by": 7}),
  295. ([], {"increment_by": 8}),
  296. ([], {"increment_by": 9}),
  297. ])
  298. self.assertEqual(ts.task_name, IncrementCounterTask.name)
  299. self.assertEqual(ts.total, 9)
  300. consumer = IncrementCounterTask().get_consumer()
  301. consumer.discard_all()
  302. taskset_res = ts.apply_async()
  303. subtasks = taskset_res.subtasks
  304. taskset_id = taskset_res.taskset_id
  305. for subtask in subtasks:
  306. m = consumer.fetch().payload
  307. self.assertDictContainsSubset({"taskset": taskset_id,
  308. "task": IncrementCounterTask.name,
  309. "id": subtask.task_id}, m)
  310. IncrementCounterTask().run(
  311. increment_by=m.get("kwargs", {}).get("increment_by"))
  312. self.assertEqual(IncrementCounterTask.count, sum(xrange(1, 10)))
  313. class TestTaskApply(unittest.TestCase):
  314. def test_apply_throw(self):
  315. self.assertRaises(KeyError, RaisingTask.apply, throw=True)
  316. def test_apply_with_CELERY_EAGER_PROPAGATES_EXCEPTIONS(self):
  317. conf.EAGER_PROPAGATES_EXCEPTIONS = True
  318. try:
  319. self.assertRaises(KeyError, RaisingTask.apply)
  320. finally:
  321. conf.EAGER_PROPAGATES_EXCEPTIONS = False
  322. def test_apply(self):
  323. IncrementCounterTask.count = 0
  324. e = IncrementCounterTask.apply()
  325. self.assertIsInstance(e, EagerResult)
  326. self.assertEqual(e.get(), 1)
  327. e = IncrementCounterTask.apply(args=[1])
  328. self.assertEqual(e.get(), 2)
  329. e = IncrementCounterTask.apply(kwargs={"increment_by": 4})
  330. self.assertEqual(e.get(), 6)
  331. self.assertTrue(e.successful())
  332. self.assertTrue(e.ready())
  333. self.assertTrue(repr(e).startswith("<EagerResult:"))
  334. f = RaisingTask.apply()
  335. self.assertTrue(f.ready())
  336. self.assertFalse(f.successful())
  337. self.assertTrue(f.traceback)
  338. self.assertRaises(KeyError, f.get)
  339. class MyPeriodic(task.PeriodicTask):
  340. run_every = timedelta(hours=1)
  341. class TestPeriodicTask(unittest.TestCase):
  342. def test_must_have_run_every(self):
  343. self.assertRaises(NotImplementedError, type, "Foo",
  344. (task.PeriodicTask, ), {"__module__": __name__})
  345. def test_remaining_estimate(self):
  346. self.assertIsInstance(
  347. MyPeriodic().remaining_estimate(datetime.now()),
  348. timedelta)
  349. def test_timedelta_seconds_returns_0_on_negative_time(self):
  350. delta = timedelta(days=-2)
  351. self.assertEqual(MyPeriodic().timedelta_seconds(delta), 0)
  352. def test_timedelta_seconds(self):
  353. deltamap = ((timedelta(seconds=1), 1),
  354. (timedelta(seconds=27), 27),
  355. (timedelta(minutes=3), 3 * 60),
  356. (timedelta(hours=4), 4 * 60 * 60),
  357. (timedelta(days=3), 3 * 86400))
  358. for delta, seconds in deltamap:
  359. self.assertEqual(MyPeriodic().timedelta_seconds(delta), seconds)
  360. def test_delta_resolution(self):
  361. D = timeutils.delta_resolution
  362. dt = datetime(2010, 3, 30, 11, 50, 58, 41065)
  363. deltamap = ((timedelta(days=2), datetime(2010, 3, 30, 0, 0)),
  364. (timedelta(hours=2), datetime(2010, 3, 30, 11, 0)),
  365. (timedelta(minutes=2), datetime(2010, 3, 30, 11, 50)),
  366. (timedelta(seconds=2), dt))
  367. for delta, shoulda in deltamap:
  368. self.assertEqual(D(dt, delta), shoulda)
  369. def test_is_due_not_due(self):
  370. due, remaining = MyPeriodic().is_due(datetime.now())
  371. self.assertFalse(due)
  372. # TODO: This assertion may fail if executed in the first minute of an hour
  373. self.assertGreater(remaining, 60)
  374. def test_is_due(self):
  375. p = MyPeriodic()
  376. due, remaining = p.is_due(datetime.now() - p.run_every.run_every)
  377. self.assertTrue(due)
  378. self.assertEqual(remaining,
  379. p.timedelta_seconds(p.run_every.run_every))
  380. class EveryMinutePeriodic(task.PeriodicTask):
  381. run_every = crontab()
  382. class QuarterlyPeriodic(task.PeriodicTask):
  383. run_every = crontab(minute="*/15")
  384. class HourlyPeriodic(task.PeriodicTask):
  385. run_every = crontab(minute=30)
  386. class DailyPeriodic(task.PeriodicTask):
  387. run_every = crontab(hour=7, minute=30)
  388. class WeeklyPeriodic(task.PeriodicTask):
  389. run_every = crontab(hour=7, minute=30, day_of_week="thursday")
  390. def patch_crontab_nowfun(cls, retval):
  391. def create_patcher(fun):
  392. @wraps(fun)
  393. def __inner(*args, **kwargs):
  394. prev_nowfun = cls.run_every.nowfun
  395. cls.run_every.nowfun = lambda: retval
  396. try:
  397. return fun(*args, **kwargs)
  398. finally:
  399. cls.run_every.nowfun = prev_nowfun
  400. return __inner
  401. return create_patcher
  402. class test_crontab_parser(unittest.TestCase):
  403. def test_parse_star(self):
  404. self.assertEquals(crontab_parser(24).parse('*'), set(range(24)))
  405. self.assertEquals(crontab_parser(60).parse('*'), set(range(60)))
  406. self.assertEquals(crontab_parser(7).parse('*'), set(range(7)))
  407. def test_parse_range(self):
  408. self.assertEquals(crontab_parser(60).parse('1-10'), set(range(1,10+1)))
  409. self.assertEquals(crontab_parser(24).parse('0-20'), set(range(0,20+1)))
  410. self.assertEquals(crontab_parser().parse('2-10'), set(range(2,10+1)))
  411. def test_parse_groups(self):
  412. self.assertEquals(crontab_parser().parse('1,2,3,4'), set([1,2,3,4]))
  413. self.assertEquals(crontab_parser().parse('0,15,30,45'), set([0,15,30,45]))
  414. def test_parse_steps(self):
  415. self.assertEquals(crontab_parser(8).parse('*/2'), set([0,2,4,6]))
  416. self.assertEquals(crontab_parser().parse('*/2'), set([ i*2 for i in xrange(30) ]))
  417. self.assertEquals(crontab_parser().parse('*/3'), set([ i*3 for i in xrange(20) ]))
  418. def test_parse_composite(self):
  419. self.assertEquals(crontab_parser(8).parse('*/2'), set([0,2,4,6]))
  420. self.assertEquals(crontab_parser().parse('2-9/5'), set([5]))
  421. self.assertEquals(crontab_parser().parse('2-10/5'), set([5,10]))
  422. self.assertEquals(crontab_parser().parse('2-11/5,3'), set([3,5,10]))
  423. self.assertEquals(crontab_parser().parse('2-4/3,*/5,0-21/4'), set([0,3,4,5,8,10,12,15,16,20,25,30,35,40,45,50,55]))
  424. def test_parse_errors_on_empty_string(self):
  425. self.assertRaises(ParseException, crontab_parser(60).parse, '')
  426. def test_parse_errors_on_empty_group(self):
  427. self.assertRaises(ParseException, crontab_parser(60).parse, '1,,2')
  428. def test_parse_errors_on_empty_steps(self):
  429. self.assertRaises(ParseException, crontab_parser(60).parse, '*/')
  430. def test_parse_errors_on_negative_number(self):
  431. self.assertRaises(ParseException, crontab_parser(60).parse, '-20')
  432. class test_crontab_is_due(unittest.TestCase):
  433. def test_default_crontab_spec(self):
  434. c = crontab()
  435. self.assertEquals(c.minute, set(range(60)))
  436. self.assertEquals(c.hour, set(range(24)))
  437. self.assertEquals(c.day_of_week, set(range(7)))
  438. def test_simple_crontab_spec(self):
  439. c = crontab(minute=30)
  440. self.assertEquals(c.minute, set([30]))
  441. self.assertEquals(c.hour, set(range(24)))
  442. self.assertEquals(c.day_of_week, set(range(7)))
  443. def test_crontab_spec_minute_formats(self):
  444. c = crontab(minute=30)
  445. self.assertEquals(c.minute, set([30]))
  446. c = crontab(minute='30')
  447. self.assertEquals(c.minute, set([30]))
  448. c = crontab(minute=(30,40,50))
  449. self.assertEquals(c.minute, set([30,40,50]))
  450. c = crontab(minute=set([30,40,50]))
  451. self.assertEquals(c.minute, set([30,40,50]))
  452. def test_crontab_spec_invalid_minute(self):
  453. self.assertRaises(ValueError, crontab, minute=60)
  454. self.assertRaises(ValueError, crontab, minute='0-100')
  455. def test_crontab_spec_hour_formats(self):
  456. c = crontab(hour=6)
  457. self.assertEquals(c.hour, set([6]))
  458. c = crontab(hour='5')
  459. self.assertEquals(c.hour, set([5]))
  460. c = crontab(hour=(4,8,12))
  461. self.assertEquals(c.hour, set([4,8,12]))
  462. def test_crontab_spec_invalid_hour(self):
  463. self.assertRaises(ValueError, crontab, hour=24)
  464. self.assertRaises(ValueError, crontab, hour='0-30')
  465. def test_crontab_spec_dow_formats(self):
  466. c = crontab(day_of_week=5)
  467. self.assertEquals(c.day_of_week, set([5]))
  468. c = crontab(day_of_week='5')
  469. self.assertEquals(c.day_of_week, set([5]))
  470. c = crontab(day_of_week='fri')
  471. self.assertEquals(c.day_of_week, set([5]))
  472. c = crontab(day_of_week='tuesday,sunday,fri')
  473. self.assertEquals(c.day_of_week, set([0,2,5]))
  474. c = crontab(day_of_week='mon-fri')
  475. self.assertEquals(c.day_of_week, set([1,2,3,4,5]))
  476. c = crontab(day_of_week='*/2')
  477. self.assertEquals(c.day_of_week, set([0,2,4,6]))
  478. def test_crontab_spec_invalid_dow(self):
  479. self.assertRaises(ValueError, crontab, day_of_week='fooday-barday')
  480. self.assertRaises(ValueError, crontab, day_of_week='1,4,foo')
  481. self.assertRaises(ValueError, crontab, day_of_week='7')
  482. self.assertRaises(ValueError, crontab, day_of_week='12')
  483. def test_every_minute_execution_is_due(self):
  484. last_ran = datetime.now() - timedelta(seconds=61)
  485. due, remaining = EveryMinutePeriodic().is_due(last_ran)
  486. self.assertTrue(due)
  487. self.assertEquals(remaining, 1)
  488. def test_every_minute_execution_is_not_due(self):
  489. last_ran = datetime.now() - timedelta(seconds=30)
  490. due, remaining = EveryMinutePeriodic().is_due(last_ran)
  491. self.assertFalse(due)
  492. self.assertEquals(remaining, 1)
  493. # 29th of May 2010 is a saturday
  494. @patch_crontab_nowfun(HourlyPeriodic, datetime(2010, 5, 29, 10, 30))
  495. def test_execution_is_due_on_saturday(self):
  496. last_ran = datetime.now() - timedelta(seconds=61)
  497. due, remaining = EveryMinutePeriodic().is_due(last_ran)
  498. self.assertTrue(due)
  499. self.assertEquals(remaining, 1)
  500. # 30th of May 2010 is a sunday
  501. @patch_crontab_nowfun(HourlyPeriodic, datetime(2010, 5, 30, 10, 30))
  502. def test_execution_is_due_on_sunday(self):
  503. last_ran = datetime.now() - timedelta(seconds=61)
  504. due, remaining = EveryMinutePeriodic().is_due(last_ran)
  505. self.assertTrue(due)
  506. self.assertEquals(remaining, 1)
  507. # 31st of May 2010 is a monday
  508. @patch_crontab_nowfun(HourlyPeriodic, datetime(2010, 5, 31, 10, 30))
  509. def test_execution_is_due_on_monday(self):
  510. last_ran = datetime.now() - timedelta(seconds=61)
  511. due, remaining = EveryMinutePeriodic().is_due(last_ran)
  512. self.assertTrue(due)
  513. self.assertEquals(remaining, 1)
  514. @patch_crontab_nowfun(HourlyPeriodic, datetime(2010, 5, 10, 10, 30))
  515. def test_every_hour_execution_is_due(self):
  516. due, remaining = HourlyPeriodic().is_due(datetime(2010, 5, 10, 6, 30))
  517. self.assertTrue(due)
  518. self.assertEquals(remaining, 1)
  519. @patch_crontab_nowfun(HourlyPeriodic, datetime(2010, 5, 10, 10, 29))
  520. def test_every_hour_execution_is_not_due(self):
  521. due, remaining = HourlyPeriodic().is_due(datetime(2010, 5, 10, 6, 30))
  522. self.assertFalse(due)
  523. self.assertEquals(remaining, 1)
  524. @patch_crontab_nowfun(QuarterlyPeriodic, datetime(2010, 5, 10, 10, 15))
  525. def test_first_quarter_execution_is_due(self):
  526. due, remaining = QuarterlyPeriodic().is_due(datetime(2010, 5, 10, 6, 30))
  527. self.assertTrue(due)
  528. self.assertEquals(remaining, 1)
  529. @patch_crontab_nowfun(QuarterlyPeriodic, datetime(2010, 5, 10, 10, 30))
  530. def test_second_quarter_execution_is_due(self):
  531. due, remaining = QuarterlyPeriodic().is_due(datetime(2010, 5, 10, 6, 30))
  532. self.assertTrue(due)
  533. self.assertEquals(remaining, 1)
  534. @patch_crontab_nowfun(QuarterlyPeriodic, datetime(2010, 5, 10, 10, 14))
  535. def test_first_quarter_execution_is_not_due(self):
  536. due, remaining = QuarterlyPeriodic().is_due(datetime(2010, 5, 10, 6, 30))
  537. self.assertFalse(due)
  538. self.assertEquals(remaining, 1)
  539. @patch_crontab_nowfun(QuarterlyPeriodic, datetime(2010, 5, 10, 10, 29))
  540. def test_second_quarter_execution_is_not_due(self):
  541. due, remaining = QuarterlyPeriodic().is_due(datetime(2010, 5, 10, 6, 30))
  542. self.assertFalse(due)
  543. self.assertEquals(remaining, 1)
  544. @patch_crontab_nowfun(DailyPeriodic, datetime(2010, 5, 10, 7, 30))
  545. def test_daily_execution_is_due(self):
  546. due, remaining = DailyPeriodic().is_due(datetime(2010, 5, 9, 7, 30))
  547. self.assertTrue(due)
  548. self.assertEquals(remaining, 1)
  549. @patch_crontab_nowfun(DailyPeriodic, datetime(2010, 5, 10, 10, 30))
  550. def test_daily_execution_is_not_due(self):
  551. due, remaining = DailyPeriodic().is_due(datetime(2010, 5, 10, 6, 29))
  552. self.assertFalse(due)
  553. self.assertEquals(remaining, 1)
  554. @patch_crontab_nowfun(WeeklyPeriodic, datetime(2010, 5, 6, 7, 30))
  555. def test_weekly_execution_is_due(self):
  556. due, remaining = WeeklyPeriodic().is_due(datetime(2010, 4, 30, 7, 30))
  557. self.assertTrue(due)
  558. self.assertEquals(remaining, 1)
  559. @patch_crontab_nowfun(WeeklyPeriodic, datetime(2010, 5, 7, 10, 30))
  560. def test_weekly_execution_is_not_due(self):
  561. due, remaining = WeeklyPeriodic().is_due(datetime(2010, 4, 30, 6, 29))
  562. self.assertFalse(due)
  563. self.assertEquals(remaining, 1)