test_tasks.py 45 KB

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