test_tasks.py 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219
  1. from __future__ import absolute_import
  2. from datetime import datetime, timedelta
  3. from functools import wraps
  4. from mock import patch
  5. from pickle import loads, dumps
  6. from celery.task import (
  7. current,
  8. task,
  9. Task,
  10. BaseTask,
  11. TaskSet,
  12. periodic_task,
  13. PeriodicTask
  14. )
  15. from celery import current_app
  16. from celery.app import app_or_default
  17. from celery.exceptions import RetryTaskError
  18. from celery.execute import send_task
  19. from celery.result import EagerResult
  20. from celery.schedules import crontab, crontab_parser, ParseException
  21. from celery.utils import uuid
  22. from celery.utils.timeutils import parse_iso8601, timedelta_seconds
  23. from celery.tests.utils import Case, with_eager_tasks, WhateverIO
  24. def now():
  25. return current_app.now()
  26. def return_True(*args, **kwargs):
  27. # Task run functions can't be closures/lambdas, as they're pickled.
  28. return True
  29. return_True_task = task()(return_True)
  30. def raise_exception(self, **kwargs):
  31. raise Exception('%s error' % self.__class__)
  32. class MockApplyTask(Task):
  33. applied = 0
  34. def run(self, x, y):
  35. return x * y
  36. @classmethod
  37. def apply_async(self, *args, **kwargs):
  38. self.applied += 1
  39. @task(name='c.unittest.increment_counter_task', count=0)
  40. def increment_counter(increment_by=1):
  41. increment_counter.count += increment_by or 1
  42. return increment_counter.count
  43. @task(name='c.unittest.raising_task')
  44. def raising():
  45. raise KeyError('foo')
  46. @task(max_retries=3, iterations=0)
  47. def retry_task(arg1, arg2, kwarg=1, max_retries=None, care=True):
  48. current.iterations += 1
  49. rmax = current.max_retries if max_retries is None else max_retries
  50. assert repr(current.request)
  51. retries = current.request.retries
  52. if care and retries >= rmax:
  53. return arg1
  54. else:
  55. raise current.retry(countdown=0, max_retries=rmax)
  56. @task(max_retries=3, iterations=0, accept_magic_kwargs=True)
  57. def retry_task_noargs(**kwargs):
  58. current.iterations += 1
  59. retries = kwargs['task_retries']
  60. if retries >= 3:
  61. return 42
  62. else:
  63. raise current.retry(countdown=0)
  64. @task(max_retries=3, iterations=0, base=MockApplyTask,
  65. accept_magic_kwargs=True)
  66. def retry_task_mockapply(arg1, arg2, kwarg=1, **kwargs):
  67. current.iterations += 1
  68. retries = kwargs['task_retries']
  69. if retries >= 3:
  70. return arg1
  71. else:
  72. kwargs.update(kwarg=kwarg)
  73. raise current.retry(countdown=0)
  74. class MyCustomException(Exception):
  75. """Random custom exception."""
  76. @task(max_retries=3, iterations=0, accept_magic_kwargs=True)
  77. def retry_task_customexc(arg1, arg2, kwarg=1, **kwargs):
  78. current.iterations += 1
  79. retries = kwargs['task_retries']
  80. if retries >= 3:
  81. return arg1 + kwarg
  82. else:
  83. try:
  84. raise MyCustomException('Elaine Marie Benes')
  85. except MyCustomException as exc:
  86. kwargs.update(kwarg=kwarg)
  87. raise current.retry(countdown=0, exc=exc)
  88. class test_task_retries(Case):
  89. def test_retry(self):
  90. retry_task.__class__.max_retries = 3
  91. retry_task.iterations = 0
  92. retry_task.apply([0xFF, 0xFFFF])
  93. self.assertEqual(retry_task.iterations, 4)
  94. retry_task.__class__.max_retries = 3
  95. retry_task.iterations = 0
  96. retry_task.apply([0xFF, 0xFFFF], {'max_retries': 10})
  97. self.assertEqual(retry_task.iterations, 11)
  98. def test_retry_no_args(self):
  99. assert retry_task_noargs.accept_magic_kwargs
  100. retry_task_noargs.__class__.max_retries = 3
  101. retry_task_noargs.iterations = 0
  102. retry_task_noargs.apply()
  103. self.assertEqual(retry_task_noargs.iterations, 4)
  104. def test_retry_kwargs_can_be_empty(self):
  105. retry_task_mockapply.push_request()
  106. try:
  107. with self.assertRaises(RetryTaskError):
  108. retry_task_mockapply.retry(args=[4, 4], kwargs=None)
  109. finally:
  110. retry_task_mockapply.pop_request()
  111. def test_retry_not_eager(self):
  112. retry_task_mockapply.push_request()
  113. try:
  114. retry_task_mockapply.request.called_directly = False
  115. exc = Exception('baz')
  116. try:
  117. retry_task_mockapply.retry(
  118. args=[4, 4], kwargs={'task_retries': 0},
  119. exc=exc, throw=False,
  120. )
  121. self.assertTrue(retry_task_mockapply.__class__.applied)
  122. finally:
  123. retry_task_mockapply.__class__.applied = 0
  124. try:
  125. with self.assertRaises(RetryTaskError):
  126. retry_task_mockapply.retry(
  127. args=[4, 4], kwargs={'task_retries': 0},
  128. exc=exc, throw=True)
  129. self.assertTrue(retry_task_mockapply.__class__.applied)
  130. finally:
  131. retry_task_mockapply.__class__.applied = 0
  132. finally:
  133. retry_task_mockapply.pop_request()
  134. def test_retry_with_kwargs(self):
  135. retry_task_customexc.__class__.max_retries = 3
  136. retry_task_customexc.iterations = 0
  137. retry_task_customexc.apply([0xFF, 0xFFFF], {'kwarg': 0xF})
  138. self.assertEqual(retry_task_customexc.iterations, 4)
  139. def test_retry_with_custom_exception(self):
  140. retry_task_customexc.__class__.max_retries = 2
  141. retry_task_customexc.iterations = 0
  142. result = retry_task_customexc.apply([0xFF, 0xFFFF], {'kwarg': 0xF})
  143. with self.assertRaises(MyCustomException):
  144. result.get()
  145. self.assertEqual(retry_task_customexc.iterations, 3)
  146. def test_max_retries_exceeded(self):
  147. retry_task.__class__.max_retries = 2
  148. retry_task.iterations = 0
  149. result = retry_task.apply([0xFF, 0xFFFF], {'care': False})
  150. with self.assertRaises(retry_task.MaxRetriesExceededError):
  151. result.get()
  152. self.assertEqual(retry_task.iterations, 3)
  153. retry_task.__class__.max_retries = 1
  154. retry_task.iterations = 0
  155. result = retry_task.apply([0xFF, 0xFFFF], {'care': False})
  156. with self.assertRaises(retry_task.MaxRetriesExceededError):
  157. result.get()
  158. self.assertEqual(retry_task.iterations, 2)
  159. class test_canvas_utils(Case):
  160. def test_si(self):
  161. self.assertTrue(retry_task.si())
  162. self.assertTrue(retry_task.si().immutable)
  163. def test_chunks(self):
  164. self.assertTrue(retry_task.chunks(range(100), 10))
  165. def test_map(self):
  166. self.assertTrue(retry_task.map(range(100)))
  167. def test_starmap(self):
  168. self.assertTrue(retry_task.starmap(range(100)))
  169. def test_on_success(self):
  170. retry_task.on_success(1, 1, (), {})
  171. class test_tasks(Case):
  172. def test_unpickle_task(self):
  173. import pickle
  174. @task
  175. def xxx():
  176. pass
  177. self.assertIs(pickle.loads(pickle.dumps(xxx)), xxx.app.tasks[xxx.name])
  178. def createTask(self, name):
  179. return task(__module__=self.__module__, name=name)(return_True)
  180. def test_AsyncResult(self):
  181. task_id = uuid()
  182. result = retry_task.AsyncResult(task_id)
  183. self.assertEqual(result.backend, retry_task.backend)
  184. self.assertEqual(result.id, task_id)
  185. def assertNextTaskDataEqual(self, consumer, presult, task_name,
  186. test_eta=False, test_expires=False, **kwargs):
  187. next_task = consumer.queues[0].get()
  188. task_data = next_task.decode()
  189. self.assertEqual(task_data['id'], presult.id)
  190. self.assertEqual(task_data['task'], task_name)
  191. task_kwargs = task_data.get('kwargs', {})
  192. if test_eta:
  193. self.assertIsInstance(task_data.get('eta'), basestring)
  194. to_datetime = parse_iso8601(task_data.get('eta'))
  195. self.assertIsInstance(to_datetime, datetime)
  196. if test_expires:
  197. self.assertIsInstance(task_data.get('expires'), basestring)
  198. to_datetime = parse_iso8601(task_data.get('expires'))
  199. self.assertIsInstance(to_datetime, datetime)
  200. for arg_name, arg_value in kwargs.items():
  201. self.assertEqual(task_kwargs.get(arg_name), arg_value)
  202. def test_incomplete_task_cls(self):
  203. class IncompleteTask(Task):
  204. name = 'c.unittest.t.itask'
  205. with self.assertRaises(NotImplementedError):
  206. IncompleteTask().run()
  207. def test_task_kwargs_must_be_dictionary(self):
  208. with self.assertRaises(ValueError):
  209. increment_counter.apply_async([], 'str')
  210. def test_task_args_must_be_list(self):
  211. with self.assertRaises(ValueError):
  212. increment_counter.apply_async('str', {})
  213. def test_regular_task(self):
  214. T1 = self.createTask('c.unittest.t.t1')
  215. self.assertIsInstance(T1, BaseTask)
  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. consumer = T1.get_consumer()
  222. with self.assertRaises(NotImplementedError):
  223. consumer.receive('foo', 'foo')
  224. consumer.purge()
  225. self.assertIsNone(consumer.queues[0].get())
  226. # Without arguments.
  227. presult = T1.delay()
  228. self.assertNextTaskDataEqual(consumer, presult, T1.name)
  229. # With arguments.
  230. presult2 = T1.apply_async(kwargs=dict(name='George Costanza'))
  231. self.assertNextTaskDataEqual(consumer, presult2, T1.name,
  232. name='George Costanza')
  233. # send_task
  234. sresult = send_task(T1.name, kwargs=dict(name='Elaine M. Benes'))
  235. self.assertNextTaskDataEqual(consumer, sresult, T1.name,
  236. name='Elaine M. Benes')
  237. # With eta.
  238. presult2 = T1.apply_async(kwargs=dict(name='George Costanza'),
  239. eta=now() + timedelta(days=1),
  240. expires=now() + timedelta(days=2))
  241. self.assertNextTaskDataEqual(consumer, presult2, T1.name,
  242. name='George Costanza', test_eta=True, test_expires=True)
  243. # With countdown.
  244. presult2 = T1.apply_async(kwargs=dict(name='George Costanza'),
  245. countdown=10, expires=12)
  246. self.assertNextTaskDataEqual(consumer, presult2, T1.name,
  247. name='George Costanza', test_eta=True, test_expires=True)
  248. # Discarding all tasks.
  249. consumer.purge()
  250. T1.apply_async()
  251. self.assertEqual(consumer.purge(), 1)
  252. self.assertIsNone(consumer.queues[0].get())
  253. self.assertFalse(presult.successful())
  254. T1.backend.mark_as_done(presult.id, result=None)
  255. self.assertTrue(presult.successful())
  256. publisher = T1.get_publisher()
  257. self.assertTrue(publisher.exchange)
  258. def test_context_get(self):
  259. task = self.createTask('c.unittest.t.c.g')
  260. task.push_request()
  261. try:
  262. request = task.request
  263. request.foo = 32
  264. self.assertEqual(request.get('foo'), 32)
  265. self.assertEqual(request.get('bar', 36), 36)
  266. request.clear()
  267. finally:
  268. task.pop_request()
  269. def test_task_class_repr(self):
  270. task = self.createTask('c.unittest.t.repr')
  271. self.assertIn('class Task of', repr(task.app.Task))
  272. prev, task.app.Task._app = task.app.Task._app, None
  273. try:
  274. self.assertIn('unbound', repr(task.app.Task, ))
  275. finally:
  276. task.app.Task._app = prev
  277. def test_bind_no_magic_kwargs(self):
  278. task = self.createTask('c.unittest.t.magic_kwargs')
  279. task.__class__.accept_magic_kwargs = None
  280. task.bind(task.app)
  281. def test_annotate(self):
  282. with patch('celery.app.task.resolve_all_annotations') as anno:
  283. anno.return_value = [{'FOO': 'BAR'}]
  284. Task.annotate()
  285. self.assertEqual(Task.FOO, 'BAR')
  286. def test_after_return(self):
  287. task = self.createTask('c.unittest.t.after_return')
  288. task.push_request()
  289. try:
  290. task.request.chord = return_True_task.s()
  291. task.after_return('SUCCESS', 1.0, 'foobar', (), {}, None)
  292. task.request.clear()
  293. finally:
  294. task.pop_request()
  295. def test_send_task_sent_event(self):
  296. T1 = self.createTask('c.unittest.t.t1')
  297. app = T1.app
  298. conn = app.connection()
  299. chan = conn.channel()
  300. app.conf.CELERY_SEND_TASK_SENT_EVENT = True
  301. dispatcher = [None]
  302. class Prod(object):
  303. channel = chan
  304. def publish_task(self, *args, **kwargs):
  305. dispatcher[0] = kwargs.get('event_dispatcher')
  306. try:
  307. T1.apply_async(producer=Prod())
  308. finally:
  309. app.conf.CELERY_SEND_TASK_SENT_EVENT = False
  310. chan.close()
  311. conn.close()
  312. self.assertTrue(dispatcher[0])
  313. def test_get_publisher(self):
  314. connection = app_or_default().connection()
  315. p = increment_counter.get_publisher(connection, auto_declare=False,
  316. exchange='foo')
  317. self.assertEqual(p.exchange.name, 'foo')
  318. p = increment_counter.get_publisher(connection, auto_declare=False,
  319. exchange='foo',
  320. exchange_type='fanout')
  321. self.assertEqual(p.exchange.type, 'fanout')
  322. def test_update_state(self):
  323. @task
  324. def yyy():
  325. pass
  326. yyy.push_request()
  327. try:
  328. tid = uuid()
  329. yyy.update_state(tid, 'FROBULATING', {'fooz': 'baaz'})
  330. self.assertEqual(yyy.AsyncResult(tid).status, 'FROBULATING')
  331. self.assertDictEqual(yyy.AsyncResult(tid).result, {'fooz': 'baaz'})
  332. yyy.request.id = tid
  333. yyy.update_state(state='FROBUZATING', meta={'fooz': 'baaz'})
  334. self.assertEqual(yyy.AsyncResult(tid).status, 'FROBUZATING')
  335. self.assertDictEqual(yyy.AsyncResult(tid).result, {'fooz': 'baaz'})
  336. finally:
  337. yyy.pop_request()
  338. def test_repr(self):
  339. @task
  340. def task_test_repr():
  341. pass
  342. self.assertIn('task_test_repr', repr(task_test_repr))
  343. def test_has___name__(self):
  344. @task
  345. def yyy2():
  346. pass
  347. self.assertTrue(yyy2.__name__)
  348. def test_get_logger(self):
  349. t1 = self.createTask('c.unittest.t.t1')
  350. t1.push_request()
  351. try:
  352. logfh = WhateverIO()
  353. logger = t1.get_logger(logfile=logfh, loglevel=0)
  354. self.assertTrue(logger)
  355. t1.request.loglevel = 3
  356. logger = t1.get_logger(logfile=logfh, loglevel=None)
  357. self.assertTrue(logger)
  358. finally:
  359. t1.pop_request()
  360. class test_TaskSet(Case):
  361. @with_eager_tasks
  362. def test_function_taskset(self):
  363. subtasks = [return_True_task.s(i) for i in range(1, 6)]
  364. ts = TaskSet(subtasks)
  365. res = ts.apply_async()
  366. self.assertListEqual(res.join(), [True, True, True, True, True])
  367. def test_counter_taskset(self):
  368. increment_counter.count = 0
  369. ts = TaskSet(tasks=[
  370. increment_counter.s(),
  371. increment_counter.s(increment_by=2),
  372. increment_counter.s(increment_by=3),
  373. increment_counter.s(increment_by=4),
  374. increment_counter.s(increment_by=5),
  375. increment_counter.s(increment_by=6),
  376. increment_counter.s(increment_by=7),
  377. increment_counter.s(increment_by=8),
  378. increment_counter.s(increment_by=9),
  379. ])
  380. self.assertEqual(ts.total, 9)
  381. consumer = increment_counter.get_consumer()
  382. consumer.purge()
  383. consumer.close()
  384. taskset_res = ts.apply_async()
  385. subtasks = taskset_res.subtasks
  386. taskset_id = taskset_res.taskset_id
  387. consumer = increment_counter.get_consumer()
  388. for subtask in subtasks:
  389. m = consumer.queues[0].get().payload
  390. self.assertDictContainsSubset({'taskset': taskset_id,
  391. 'task': increment_counter.name,
  392. 'id': subtask.id}, m)
  393. increment_counter(
  394. increment_by=m.get('kwargs', {}).get('increment_by'))
  395. self.assertEqual(increment_counter.count, sum(xrange(1, 10)))
  396. def test_named_taskset(self):
  397. prefix = 'test_named_taskset-'
  398. ts = TaskSet([return_True_task.subtask([1])])
  399. res = ts.apply(taskset_id=prefix + uuid())
  400. self.assertTrue(res.taskset_id.startswith(prefix))
  401. class test_apply_task(Case):
  402. def test_apply_throw(self):
  403. with self.assertRaises(KeyError):
  404. raising.apply(throw=True)
  405. def test_apply_no_magic_kwargs(self):
  406. increment_counter.accept_magic_kwargs = False
  407. try:
  408. increment_counter.apply()
  409. finally:
  410. increment_counter.accept_magic_kwargs = True
  411. def test_apply_with_CELERY_EAGER_PROPAGATES_EXCEPTIONS(self):
  412. raising.app.conf.CELERY_EAGER_PROPAGATES_EXCEPTIONS = True
  413. try:
  414. with self.assertRaises(KeyError):
  415. raising.apply()
  416. finally:
  417. raising.app.conf.CELERY_EAGER_PROPAGATES_EXCEPTIONS = False
  418. def test_apply(self):
  419. increment_counter.count = 0
  420. e = increment_counter.apply()
  421. self.assertIsInstance(e, EagerResult)
  422. self.assertEqual(e.get(), 1)
  423. e = increment_counter.apply(args=[1])
  424. self.assertEqual(e.get(), 2)
  425. e = increment_counter.apply(kwargs={'increment_by': 4})
  426. self.assertEqual(e.get(), 6)
  427. self.assertTrue(e.successful())
  428. self.assertTrue(e.ready())
  429. self.assertTrue(repr(e).startswith('<EagerResult:'))
  430. f = raising.apply()
  431. self.assertTrue(f.ready())
  432. self.assertFalse(f.successful())
  433. self.assertTrue(f.traceback)
  434. with self.assertRaises(KeyError):
  435. f.get()
  436. @periodic_task(run_every=timedelta(hours=1))
  437. def my_periodic():
  438. pass
  439. class test_periodic_tasks(Case):
  440. def test_must_have_run_every(self):
  441. with self.assertRaises(NotImplementedError):
  442. type('Foo', (PeriodicTask, ), {'__module__': __name__})
  443. def test_remaining_estimate(self):
  444. s = my_periodic.run_every
  445. self.assertIsInstance(
  446. s.remaining_estimate(s.maybe_make_aware(now())),
  447. timedelta)
  448. def test_is_due_not_due(self):
  449. due, remaining = my_periodic.run_every.is_due(now())
  450. self.assertFalse(due)
  451. # This assertion may fail if executed in the
  452. # first minute of an hour, thus 59 instead of 60
  453. self.assertGreater(remaining, 59)
  454. def test_is_due(self):
  455. p = my_periodic
  456. due, remaining = p.run_every.is_due(
  457. now() - p.run_every.run_every)
  458. self.assertTrue(due)
  459. self.assertEqual(remaining,
  460. timedelta_seconds(p.run_every.run_every))
  461. def test_schedule_repr(self):
  462. p = my_periodic
  463. self.assertTrue(repr(p.run_every))
  464. @periodic_task(run_every=crontab())
  465. def every_minute():
  466. pass
  467. @periodic_task(run_every=crontab(minute='*/15'))
  468. def quarterly():
  469. pass
  470. @periodic_task(run_every=crontab(minute=30))
  471. def hourly():
  472. pass
  473. @periodic_task(run_every=crontab(hour=7, minute=30))
  474. def daily():
  475. pass
  476. @periodic_task(run_every=crontab(hour=7, minute=30,
  477. day_of_week='thursday'))
  478. def weekly():
  479. pass
  480. @periodic_task(run_every=crontab(hour=7, minute=30,
  481. day_of_week='thursday',
  482. day_of_month='8-14'))
  483. def monthly():
  484. pass
  485. @periodic_task(run_every=crontab(hour=7, minute=30,
  486. day_of_week='thursday',
  487. day_of_month='8-14',
  488. month_of_year=3))
  489. def yearly():
  490. pass
  491. def patch_crontab_nowfun(cls, retval):
  492. def create_patcher(fun):
  493. @wraps(fun)
  494. def __inner(*args, **kwargs):
  495. prev_nowfun = cls.run_every.nowfun
  496. cls.run_every.nowfun = lambda: retval
  497. try:
  498. return fun(*args, **kwargs)
  499. finally:
  500. cls.run_every.nowfun = prev_nowfun
  501. return __inner
  502. return create_patcher
  503. class test_crontab_parser(Case):
  504. def test_crontab_reduce(self):
  505. self.assertTrue(loads(dumps(crontab('*'))))
  506. def test_range_steps_not_enough(self):
  507. with self.assertRaises(crontab_parser.ParseException):
  508. crontab_parser(24)._range_steps([1])
  509. def test_parse_star(self):
  510. self.assertEqual(crontab_parser(24).parse('*'), set(range(24)))
  511. self.assertEqual(crontab_parser(60).parse('*'), set(range(60)))
  512. self.assertEqual(crontab_parser(7).parse('*'), set(range(7)))
  513. self.assertEqual(crontab_parser(31, 1).parse('*'),
  514. set(range(1, 31 + 1)))
  515. self.assertEqual(crontab_parser(12, 1).parse('*'),
  516. set(range(1, 12 + 1)))
  517. def test_parse_range(self):
  518. self.assertEqual(crontab_parser(60).parse('1-10'),
  519. set(range(1, 10 + 1)))
  520. self.assertEqual(crontab_parser(24).parse('0-20'),
  521. set(range(0, 20 + 1)))
  522. self.assertEqual(crontab_parser().parse('2-10'),
  523. set(range(2, 10 + 1)))
  524. self.assertEqual(crontab_parser(60, 1).parse('1-10'),
  525. set(range(1, 10 + 1)))
  526. def test_parse_groups(self):
  527. self.assertEqual(crontab_parser().parse('1,2,3,4'),
  528. set([1, 2, 3, 4]))
  529. self.assertEqual(crontab_parser().parse('0,15,30,45'),
  530. set([0, 15, 30, 45]))
  531. self.assertEqual(crontab_parser(min_=1).parse('1,2,3,4'),
  532. set([1, 2, 3, 4]))
  533. def test_parse_steps(self):
  534. self.assertEqual(crontab_parser(8).parse('*/2'),
  535. set([0, 2, 4, 6]))
  536. self.assertEqual(crontab_parser().parse('*/2'),
  537. set(i * 2 for i in xrange(30)))
  538. self.assertEqual(crontab_parser().parse('*/3'),
  539. set(i * 3 for i in xrange(20)))
  540. self.assertEqual(crontab_parser(8, 1).parse('*/2'),
  541. set([1, 3, 5, 7]))
  542. self.assertEqual(crontab_parser(min_=1).parse('*/2'),
  543. set(i * 2 + 1 for i in xrange(30)))
  544. self.assertEqual(crontab_parser(min_=1).parse('*/3'),
  545. set(i * 3 + 1 for i in xrange(20)))
  546. def test_parse_composite(self):
  547. self.assertEqual(crontab_parser(8).parse('*/2'), set([0, 2, 4, 6]))
  548. self.assertEqual(crontab_parser().parse('2-9/5'), set([2, 7]))
  549. self.assertEqual(crontab_parser().parse('2-10/5'), set([2, 7]))
  550. self.assertEqual(crontab_parser().parse('2-11/5,3'), set([2, 3, 7]))
  551. self.assertEqual(crontab_parser().parse('2-4/3,*/5,0-21/4'),
  552. set([0, 2, 4, 5, 8, 10, 12, 15, 16,
  553. 20, 25, 30, 35, 40, 45, 50, 55]))
  554. self.assertEqual(crontab_parser().parse('1-9/2'),
  555. set([1, 3, 5, 7, 9]))
  556. self.assertEqual(crontab_parser(8, 1).parse('*/2'), set([1, 3, 5, 7]))
  557. self.assertEqual(crontab_parser(min_=1).parse('2-9/5'), set([2, 7]))
  558. self.assertEqual(crontab_parser(min_=1).parse('2-10/5'), set([2, 7]))
  559. self.assertEqual(crontab_parser(min_=1).parse('2-11/5,3'),
  560. set([2, 3, 7]))
  561. self.assertEqual(crontab_parser(min_=1).parse('2-4/3,*/5,1-21/4'),
  562. set([1, 2, 5, 6, 9, 11, 13, 16, 17,
  563. 21, 26, 31, 36, 41, 46, 51, 56]))
  564. self.assertEqual(crontab_parser(min_=1).parse('1-9/2'),
  565. set([1, 3, 5, 7, 9]))
  566. def test_parse_errors_on_empty_string(self):
  567. with self.assertRaises(ParseException):
  568. crontab_parser(60).parse('')
  569. def test_parse_errors_on_empty_group(self):
  570. with self.assertRaises(ParseException):
  571. crontab_parser(60).parse('1,,2')
  572. def test_parse_errors_on_empty_steps(self):
  573. with self.assertRaises(ParseException):
  574. crontab_parser(60).parse('*/')
  575. def test_parse_errors_on_negative_number(self):
  576. with self.assertRaises(ParseException):
  577. crontab_parser(60).parse('-20')
  578. def test_expand_cronspec_eats_iterables(self):
  579. self.assertEqual(crontab._expand_cronspec(iter([1, 2, 3]), 100),
  580. set([1, 2, 3]))
  581. self.assertEqual(crontab._expand_cronspec(iter([1, 2, 3]), 100, 1),
  582. set([1, 2, 3]))
  583. def test_expand_cronspec_invalid_type(self):
  584. with self.assertRaises(TypeError):
  585. crontab._expand_cronspec(object(), 100)
  586. def test_repr(self):
  587. self.assertIn('*', repr(crontab('*')))
  588. def test_eq(self):
  589. self.assertEqual(crontab(day_of_week='1, 2'),
  590. crontab(day_of_week='1-2'))
  591. self.assertEqual(crontab(day_of_month='1, 16, 31'),
  592. crontab(day_of_month='*/15'))
  593. self.assertEqual(crontab(minute='1', hour='2', day_of_week='5',
  594. day_of_month='10', month_of_year='5'),
  595. crontab(minute='1', hour='2', day_of_week='5',
  596. day_of_month='10', month_of_year='5'))
  597. self.assertNotEqual(crontab(minute='1'), crontab(minute='2'))
  598. self.assertNotEqual(crontab(month_of_year='1'),
  599. crontab(month_of_year='2'))
  600. self.assertFalse(object() == crontab(minute='1'))
  601. self.assertFalse(crontab(minute='1') == object())
  602. class test_crontab_remaining_estimate(Case):
  603. def next_ocurrance(self, crontab, now):
  604. crontab.nowfun = lambda: now
  605. return now + crontab.remaining_estimate(now)
  606. def test_next_minute(self):
  607. next = self.next_ocurrance(crontab(),
  608. datetime(2010, 9, 11, 14, 30, 15))
  609. self.assertEqual(next, datetime(2010, 9, 11, 14, 31))
  610. def test_not_next_minute(self):
  611. next = self.next_ocurrance(crontab(),
  612. datetime(2010, 9, 11, 14, 59, 15))
  613. self.assertEqual(next, datetime(2010, 9, 11, 15, 0))
  614. def test_this_hour(self):
  615. next = self.next_ocurrance(crontab(minute=[5, 42]),
  616. datetime(2010, 9, 11, 14, 30, 15))
  617. self.assertEqual(next, datetime(2010, 9, 11, 14, 42))
  618. def test_not_this_hour(self):
  619. next = self.next_ocurrance(crontab(minute=[5, 10, 15]),
  620. datetime(2010, 9, 11, 14, 30, 15))
  621. self.assertEqual(next, datetime(2010, 9, 11, 15, 5))
  622. def test_today(self):
  623. next = self.next_ocurrance(crontab(minute=[5, 42], hour=[12, 17]),
  624. datetime(2010, 9, 11, 14, 30, 15))
  625. self.assertEqual(next, datetime(2010, 9, 11, 17, 5))
  626. def test_not_today(self):
  627. next = self.next_ocurrance(crontab(minute=[5, 42], hour=[12]),
  628. datetime(2010, 9, 11, 14, 30, 15))
  629. self.assertEqual(next, datetime(2010, 9, 12, 12, 5))
  630. def test_weekday(self):
  631. next = self.next_ocurrance(crontab(minute=30,
  632. hour=14,
  633. day_of_week='sat'),
  634. datetime(2010, 9, 11, 14, 30, 15))
  635. self.assertEqual(next, datetime(2010, 9, 18, 14, 30))
  636. def test_not_weekday(self):
  637. next = self.next_ocurrance(crontab(minute=[5, 42],
  638. day_of_week='mon-fri'),
  639. datetime(2010, 9, 11, 14, 30, 15))
  640. self.assertEqual(next, datetime(2010, 9, 13, 0, 5))
  641. def test_monthday(self):
  642. next = self.next_ocurrance(crontab(minute=30,
  643. hour=14,
  644. day_of_month=18),
  645. datetime(2010, 9, 11, 14, 30, 15))
  646. self.assertEqual(next, datetime(2010, 9, 18, 14, 30))
  647. def test_not_monthday(self):
  648. next = self.next_ocurrance(crontab(minute=[5, 42],
  649. day_of_month=29),
  650. datetime(2010, 1, 22, 14, 30, 15))
  651. self.assertEqual(next, datetime(2010, 1, 29, 0, 5))
  652. def test_weekday_monthday(self):
  653. next = self.next_ocurrance(crontab(minute=30,
  654. hour=14,
  655. day_of_week='mon',
  656. day_of_month=18),
  657. datetime(2010, 1, 18, 14, 30, 15))
  658. self.assertEqual(next, datetime(2010, 10, 18, 14, 30))
  659. def test_monthday_not_weekday(self):
  660. next = self.next_ocurrance(crontab(minute=[5, 42],
  661. day_of_week='sat',
  662. day_of_month=29),
  663. datetime(2010, 1, 29, 0, 5, 15))
  664. self.assertEqual(next, datetime(2010, 5, 29, 0, 5))
  665. def test_weekday_not_monthday(self):
  666. next = self.next_ocurrance(crontab(minute=[5, 42],
  667. day_of_week='mon',
  668. day_of_month=18),
  669. datetime(2010, 1, 11, 0, 5, 15))
  670. self.assertEqual(next, datetime(2010, 1, 18, 0, 5))
  671. def test_not_weekday_not_monthday(self):
  672. next = self.next_ocurrance(crontab(minute=[5, 42],
  673. day_of_week='mon',
  674. day_of_month=18),
  675. datetime(2010, 1, 10, 0, 5, 15))
  676. self.assertEqual(next, datetime(2010, 1, 18, 0, 5))
  677. def test_leapday(self):
  678. next = self.next_ocurrance(crontab(minute=30,
  679. hour=14,
  680. day_of_month=29),
  681. datetime(2012, 1, 29, 14, 30, 15))
  682. self.assertEqual(next, datetime(2012, 2, 29, 14, 30))
  683. def test_not_leapday(self):
  684. next = self.next_ocurrance(crontab(minute=30,
  685. hour=14,
  686. day_of_month=29),
  687. datetime(2010, 1, 29, 14, 30, 15))
  688. self.assertEqual(next, datetime(2010, 3, 29, 14, 30))
  689. def test_weekmonthdayyear(self):
  690. next = self.next_ocurrance(crontab(minute=30,
  691. hour=14,
  692. day_of_week='fri',
  693. day_of_month=29,
  694. month_of_year=1),
  695. datetime(2010, 1, 22, 14, 30, 15))
  696. self.assertEqual(next, datetime(2010, 1, 29, 14, 30))
  697. def test_monthdayyear_not_week(self):
  698. next = self.next_ocurrance(crontab(minute=[5, 42],
  699. day_of_week='wed,thu',
  700. day_of_month=29,
  701. month_of_year='1,4,7'),
  702. datetime(2010, 1, 29, 14, 30, 15))
  703. self.assertEqual(next, datetime(2010, 4, 29, 0, 5))
  704. def test_weekdaymonthyear_not_monthday(self):
  705. next = self.next_ocurrance(crontab(minute=30,
  706. hour=14,
  707. day_of_week='fri',
  708. day_of_month=29,
  709. month_of_year='1-10'),
  710. datetime(2010, 1, 29, 14, 30, 15))
  711. self.assertEqual(next, datetime(2010, 10, 29, 14, 30))
  712. def test_weekmonthday_not_monthyear(self):
  713. next = self.next_ocurrance(crontab(minute=[5, 42],
  714. day_of_week='fri',
  715. day_of_month=29,
  716. month_of_year='2-10'),
  717. datetime(2010, 1, 29, 14, 30, 15))
  718. self.assertEqual(next, datetime(2010, 10, 29, 0, 5))
  719. def test_weekday_not_monthdayyear(self):
  720. next = self.next_ocurrance(crontab(minute=[5, 42],
  721. day_of_week='mon',
  722. day_of_month=18,
  723. month_of_year='2-10'),
  724. datetime(2010, 1, 11, 0, 5, 15))
  725. self.assertEqual(next, datetime(2010, 10, 18, 0, 5))
  726. def test_monthday_not_weekdaymonthyear(self):
  727. next = self.next_ocurrance(crontab(minute=[5, 42],
  728. day_of_week='mon',
  729. day_of_month=29,
  730. month_of_year='2-4'),
  731. datetime(2010, 1, 29, 0, 5, 15))
  732. self.assertEqual(next, datetime(2010, 3, 29, 0, 5))
  733. def test_monthyear_not_weekmonthday(self):
  734. next = self.next_ocurrance(crontab(minute=[5, 42],
  735. day_of_week='mon',
  736. day_of_month=29,
  737. month_of_year='2-4'),
  738. datetime(2010, 2, 28, 0, 5, 15))
  739. self.assertEqual(next, datetime(2010, 3, 29, 0, 5))
  740. def test_not_weekmonthdayyear(self):
  741. next = self.next_ocurrance(crontab(minute=[5, 42],
  742. day_of_week='fri,sat',
  743. day_of_month=29,
  744. month_of_year='2-10'),
  745. datetime(2010, 1, 28, 14, 30, 15))
  746. self.assertEqual(next, datetime(2010, 5, 29, 0, 5))
  747. class test_crontab_is_due(Case):
  748. def setUp(self):
  749. self.now = now()
  750. self.next_minute = 60 - self.now.second - 1e-6 * self.now.microsecond
  751. def test_default_crontab_spec(self):
  752. c = crontab()
  753. self.assertEqual(c.minute, set(range(60)))
  754. self.assertEqual(c.hour, set(range(24)))
  755. self.assertEqual(c.day_of_week, set(range(7)))
  756. self.assertEqual(c.day_of_month, set(range(1, 32)))
  757. self.assertEqual(c.month_of_year, set(range(1, 13)))
  758. def test_simple_crontab_spec(self):
  759. c = crontab(minute=30)
  760. self.assertEqual(c.minute, set([30]))
  761. self.assertEqual(c.hour, set(range(24)))
  762. self.assertEqual(c.day_of_week, set(range(7)))
  763. self.assertEqual(c.day_of_month, set(range(1, 32)))
  764. self.assertEqual(c.month_of_year, set(range(1, 13)))
  765. def test_crontab_spec_minute_formats(self):
  766. c = crontab(minute=30)
  767. self.assertEqual(c.minute, set([30]))
  768. c = crontab(minute='30')
  769. self.assertEqual(c.minute, set([30]))
  770. c = crontab(minute=(30, 40, 50))
  771. self.assertEqual(c.minute, set([30, 40, 50]))
  772. c = crontab(minute=set([30, 40, 50]))
  773. self.assertEqual(c.minute, set([30, 40, 50]))
  774. def test_crontab_spec_invalid_minute(self):
  775. with self.assertRaises(ValueError):
  776. crontab(minute=60)
  777. with self.assertRaises(ValueError):
  778. crontab(minute='0-100')
  779. def test_crontab_spec_hour_formats(self):
  780. c = crontab(hour=6)
  781. self.assertEqual(c.hour, set([6]))
  782. c = crontab(hour='5')
  783. self.assertEqual(c.hour, set([5]))
  784. c = crontab(hour=(4, 8, 12))
  785. self.assertEqual(c.hour, set([4, 8, 12]))
  786. def test_crontab_spec_invalid_hour(self):
  787. with self.assertRaises(ValueError):
  788. crontab(hour=24)
  789. with self.assertRaises(ValueError):
  790. crontab(hour='0-30')
  791. def test_crontab_spec_dow_formats(self):
  792. c = crontab(day_of_week=5)
  793. self.assertEqual(c.day_of_week, set([5]))
  794. c = crontab(day_of_week='5')
  795. self.assertEqual(c.day_of_week, set([5]))
  796. c = crontab(day_of_week='fri')
  797. self.assertEqual(c.day_of_week, set([5]))
  798. c = crontab(day_of_week='tuesday,sunday,fri')
  799. self.assertEqual(c.day_of_week, set([0, 2, 5]))
  800. c = crontab(day_of_week='mon-fri')
  801. self.assertEqual(c.day_of_week, set([1, 2, 3, 4, 5]))
  802. c = crontab(day_of_week='*/2')
  803. self.assertEqual(c.day_of_week, set([0, 2, 4, 6]))
  804. def test_crontab_spec_invalid_dow(self):
  805. with self.assertRaises(ValueError):
  806. crontab(day_of_week='fooday-barday')
  807. with self.assertRaises(ValueError):
  808. crontab(day_of_week='1,4,foo')
  809. with self.assertRaises(ValueError):
  810. crontab(day_of_week='7')
  811. with self.assertRaises(ValueError):
  812. crontab(day_of_week='12')
  813. def test_crontab_spec_dom_formats(self):
  814. c = crontab(day_of_month=5)
  815. self.assertEqual(c.day_of_month, set([5]))
  816. c = crontab(day_of_month='5')
  817. self.assertEqual(c.day_of_month, set([5]))
  818. c = crontab(day_of_month='2,4,6')
  819. self.assertEqual(c.day_of_month, set([2, 4, 6]))
  820. c = crontab(day_of_month='*/5')
  821. self.assertEqual(c.day_of_month, set([1, 6, 11, 16, 21, 26, 31]))
  822. def test_crontab_spec_invalid_dom(self):
  823. with self.assertRaises(ValueError):
  824. crontab(day_of_month=0)
  825. with self.assertRaises(ValueError):
  826. crontab(day_of_month='0-10')
  827. with self.assertRaises(ValueError):
  828. crontab(day_of_month=32)
  829. with self.assertRaises(ValueError):
  830. crontab(day_of_month='31,32')
  831. def test_crontab_spec_moy_formats(self):
  832. c = crontab(month_of_year=1)
  833. self.assertEqual(c.month_of_year, set([1]))
  834. c = crontab(month_of_year='1')
  835. self.assertEqual(c.month_of_year, set([1]))
  836. c = crontab(month_of_year='2,4,6')
  837. self.assertEqual(c.month_of_year, set([2, 4, 6]))
  838. c = crontab(month_of_year='*/2')
  839. self.assertEqual(c.month_of_year, set([1, 3, 5, 7, 9, 11]))
  840. c = crontab(month_of_year='2-12/2')
  841. self.assertEqual(c.month_of_year, set([2, 4, 6, 8, 10, 12]))
  842. def test_crontab_spec_invalid_moy(self):
  843. with self.assertRaises(ValueError):
  844. crontab(month_of_year=0)
  845. with self.assertRaises(ValueError):
  846. crontab(month_of_year='0-5')
  847. with self.assertRaises(ValueError):
  848. crontab(month_of_year=13)
  849. with self.assertRaises(ValueError):
  850. crontab(month_of_year='12,13')
  851. def seconds_almost_equal(self, a, b, precision):
  852. for index, skew in enumerate((+0.1, 0, -0.1)):
  853. try:
  854. self.assertAlmostEqual(a, b + skew, precision)
  855. except AssertionError:
  856. if index + 1 >= 3:
  857. raise
  858. else:
  859. break
  860. def assertRelativedelta(self, due, last_ran):
  861. try:
  862. from dateutil.relativedelta import relativedelta
  863. except ImportError:
  864. return
  865. l1, d1, n1 = due.run_every.remaining_delta(last_ran)
  866. l2, d2, n2 = due.run_every.remaining_delta(last_ran,
  867. ffwd=relativedelta)
  868. if not isinstance(d1, relativedelta):
  869. self.assertEqual(l1, l2)
  870. for field, value in d1._fields().iteritems():
  871. self.assertEqual(getattr(d1, field), value)
  872. self.assertFalse(d2.years)
  873. self.assertFalse(d2.months)
  874. self.assertFalse(d2.days)
  875. self.assertFalse(d2.leapdays)
  876. self.assertFalse(d2.hours)
  877. self.assertFalse(d2.minutes)
  878. self.assertFalse(d2.seconds)
  879. self.assertFalse(d2.microseconds)
  880. def test_every_minute_execution_is_due(self):
  881. last_ran = self.now - timedelta(seconds=61)
  882. due, remaining = every_minute.run_every.is_due(last_ran)
  883. self.assertRelativedelta(every_minute, last_ran)
  884. self.assertTrue(due)
  885. self.seconds_almost_equal(remaining, self.next_minute, 1)
  886. def test_every_minute_execution_is_not_due(self):
  887. last_ran = self.now - timedelta(seconds=self.now.second)
  888. due, remaining = every_minute.run_every.is_due(last_ran)
  889. self.assertFalse(due)
  890. self.seconds_almost_equal(remaining, self.next_minute, 1)
  891. # 29th of May 2010 is a saturday
  892. @patch_crontab_nowfun(hourly, datetime(2010, 5, 29, 10, 30))
  893. def test_execution_is_due_on_saturday(self):
  894. last_ran = self.now - timedelta(seconds=61)
  895. due, remaining = every_minute.run_every.is_due(last_ran)
  896. self.assertTrue(due)
  897. self.seconds_almost_equal(remaining, self.next_minute, 1)
  898. # 30th of May 2010 is a sunday
  899. @patch_crontab_nowfun(hourly, datetime(2010, 5, 30, 10, 30))
  900. def test_execution_is_due_on_sunday(self):
  901. last_ran = self.now - timedelta(seconds=61)
  902. due, remaining = every_minute.run_every.is_due(last_ran)
  903. self.assertTrue(due)
  904. self.seconds_almost_equal(remaining, self.next_minute, 1)
  905. # 31st of May 2010 is a monday
  906. @patch_crontab_nowfun(hourly, datetime(2010, 5, 31, 10, 30))
  907. def test_execution_is_due_on_monday(self):
  908. last_ran = self.now - timedelta(seconds=61)
  909. due, remaining = every_minute.run_every.is_due(last_ran)
  910. self.assertTrue(due)
  911. self.seconds_almost_equal(remaining, self.next_minute, 1)
  912. @patch_crontab_nowfun(hourly, datetime(2010, 5, 10, 10, 30))
  913. def test_every_hour_execution_is_due(self):
  914. due, remaining = hourly.run_every.is_due(
  915. datetime(2010, 5, 10, 6, 30))
  916. self.assertTrue(due)
  917. self.assertEqual(remaining, 60 * 60)
  918. @patch_crontab_nowfun(hourly, datetime(2010, 5, 10, 10, 29))
  919. def test_every_hour_execution_is_not_due(self):
  920. due, remaining = hourly.run_every.is_due(
  921. datetime(2010, 5, 10, 9, 30))
  922. self.assertFalse(due)
  923. self.assertEqual(remaining, 60)
  924. @patch_crontab_nowfun(quarterly, datetime(2010, 5, 10, 10, 15))
  925. def test_first_quarter_execution_is_due(self):
  926. due, remaining = quarterly.run_every.is_due(
  927. datetime(2010, 5, 10, 6, 30))
  928. self.assertTrue(due)
  929. self.assertEqual(remaining, 15 * 60)
  930. @patch_crontab_nowfun(quarterly, datetime(2010, 5, 10, 10, 30))
  931. def test_second_quarter_execution_is_due(self):
  932. due, remaining = quarterly.run_every.is_due(
  933. datetime(2010, 5, 10, 6, 30))
  934. self.assertTrue(due)
  935. self.assertEqual(remaining, 15 * 60)
  936. @patch_crontab_nowfun(quarterly, datetime(2010, 5, 10, 10, 14))
  937. def test_first_quarter_execution_is_not_due(self):
  938. due, remaining = quarterly.run_every.is_due(
  939. datetime(2010, 5, 10, 10, 0))
  940. self.assertFalse(due)
  941. self.assertEqual(remaining, 60)
  942. @patch_crontab_nowfun(quarterly, datetime(2010, 5, 10, 10, 29))
  943. def test_second_quarter_execution_is_not_due(self):
  944. due, remaining = quarterly.run_every.is_due(
  945. datetime(2010, 5, 10, 10, 15))
  946. self.assertFalse(due)
  947. self.assertEqual(remaining, 60)
  948. @patch_crontab_nowfun(daily, datetime(2010, 5, 10, 7, 30))
  949. def test_daily_execution_is_due(self):
  950. due, remaining = daily.run_every.is_due(
  951. datetime(2010, 5, 9, 7, 30))
  952. self.assertTrue(due)
  953. self.assertEqual(remaining, 24 * 60 * 60)
  954. @patch_crontab_nowfun(daily, datetime(2010, 5, 10, 10, 30))
  955. def test_daily_execution_is_not_due(self):
  956. due, remaining = daily.run_every.is_due(
  957. datetime(2010, 5, 10, 7, 30))
  958. self.assertFalse(due)
  959. self.assertEqual(remaining, 21 * 60 * 60)
  960. @patch_crontab_nowfun(weekly, datetime(2010, 5, 6, 7, 30))
  961. def test_weekly_execution_is_due(self):
  962. due, remaining = weekly.run_every.is_due(
  963. datetime(2010, 4, 30, 7, 30))
  964. self.assertTrue(due)
  965. self.assertEqual(remaining, 7 * 24 * 60 * 60)
  966. @patch_crontab_nowfun(weekly, datetime(2010, 5, 7, 10, 30))
  967. def test_weekly_execution_is_not_due(self):
  968. due, remaining = weekly.run_every.is_due(
  969. datetime(2010, 5, 6, 7, 30))
  970. self.assertFalse(due)
  971. self.assertEqual(remaining, 6 * 24 * 60 * 60 - 3 * 60 * 60)
  972. @patch_crontab_nowfun(monthly, datetime(2010, 5, 13, 7, 30))
  973. def test_monthly_execution_is_due(self):
  974. due, remaining = monthly.run_every.is_due(
  975. datetime(2010, 4, 8, 7, 30))
  976. self.assertTrue(due)
  977. self.assertEqual(remaining, 28 * 24 * 60 * 60)
  978. @patch_crontab_nowfun(monthly, datetime(2010, 5, 9, 10, 30))
  979. def test_monthly_execution_is_not_due(self):
  980. due, remaining = monthly.run_every.is_due(
  981. datetime(2010, 4, 8, 7, 30))
  982. self.assertFalse(due)
  983. self.assertEqual(remaining, 4 * 24 * 60 * 60 - 3 * 60 * 60)
  984. @patch_crontab_nowfun(yearly, datetime(2010, 3, 11, 7, 30))
  985. def test_yearly_execution_is_due(self):
  986. due, remaining = yearly.run_every.is_due(
  987. datetime(2009, 3, 12, 7, 30))
  988. self.assertTrue(due)
  989. self.assertEqual(remaining, 364 * 24 * 60 * 60)
  990. @patch_crontab_nowfun(yearly, datetime(2010, 3, 7, 10, 30))
  991. def test_yearly_execution_is_not_due(self):
  992. due, remaining = yearly.run_every.is_due(
  993. datetime(2009, 3, 12, 7, 30))
  994. self.assertFalse(due)
  995. self.assertEqual(remaining, 4 * 24 * 60 * 60 - 3 * 60 * 60)