test_time.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. from __future__ import absolute_import, unicode_literals
  2. import pytest
  3. import pytz
  4. from datetime import datetime, timedelta, tzinfo
  5. from pytz import AmbiguousTimeError
  6. from case import Mock, patch
  7. from celery.utils.time import (
  8. delta_resolution,
  9. humanize_seconds,
  10. maybe_iso8601,
  11. maybe_timedelta,
  12. timezone,
  13. rate,
  14. remaining,
  15. make_aware,
  16. maybe_make_aware,
  17. localize,
  18. LocalTimezone,
  19. ffwd,
  20. utcoffset,
  21. get_exponential_backoff_interval,
  22. )
  23. from celery.utils.iso8601 import parse_iso8601
  24. class test_LocalTimezone:
  25. def test_daylight(self, patching):
  26. time = patching('celery.utils.time._time')
  27. time.timezone = 3600
  28. time.daylight = False
  29. x = LocalTimezone()
  30. assert x.STDOFFSET == timedelta(seconds=-3600)
  31. assert x.DSTOFFSET == x.STDOFFSET
  32. time.daylight = True
  33. time.altzone = 3600
  34. y = LocalTimezone()
  35. assert y.STDOFFSET == timedelta(seconds=-3600)
  36. assert y.DSTOFFSET == timedelta(seconds=-3600)
  37. assert repr(y)
  38. y._isdst = Mock()
  39. y._isdst.return_value = True
  40. assert y.utcoffset(datetime.now())
  41. assert not y.dst(datetime.now())
  42. y._isdst.return_value = False
  43. assert y.utcoffset(datetime.now())
  44. assert not y.dst(datetime.now())
  45. assert y.tzname(datetime.now())
  46. class test_iso8601:
  47. def test_parse_with_timezone(self):
  48. d = datetime.utcnow().replace(tzinfo=pytz.utc)
  49. assert parse_iso8601(d.isoformat()) == d
  50. # 2013-06-07T20:12:51.775877+00:00
  51. iso = d.isoformat()
  52. iso1 = iso.replace('+00:00', '-01:00')
  53. d1 = parse_iso8601(iso1)
  54. assert d1.tzinfo._minutes == -60
  55. iso2 = iso.replace('+00:00', '+01:00')
  56. d2 = parse_iso8601(iso2)
  57. assert d2.tzinfo._minutes == +60
  58. iso3 = iso.replace('+00:00', 'Z')
  59. d3 = parse_iso8601(iso3)
  60. assert d3.tzinfo == pytz.UTC
  61. @pytest.mark.parametrize('delta,expected', [
  62. (timedelta(days=2), datetime(2010, 3, 30, 0, 0)),
  63. (timedelta(hours=2), datetime(2010, 3, 30, 11, 0)),
  64. (timedelta(minutes=2), datetime(2010, 3, 30, 11, 50)),
  65. (timedelta(seconds=2), None),
  66. ])
  67. def test_delta_resolution(delta, expected):
  68. dt = datetime(2010, 3, 30, 11, 50, 58, 41065)
  69. assert delta_resolution(dt, delta) == expected or dt
  70. @pytest.mark.parametrize('seconds,expected', [
  71. (4 * 60 * 60 * 24, '4.00 days'),
  72. (1 * 60 * 60 * 24, '1.00 day'),
  73. (4 * 60 * 60, '4.00 hours'),
  74. (1 * 60 * 60, '1.00 hour'),
  75. (4 * 60, '4.00 minutes'),
  76. (1 * 60, '1.00 minute'),
  77. (4, '4.00 seconds'),
  78. (1, '1.00 second'),
  79. (4.3567631221, '4.36 seconds'),
  80. (0, 'now'),
  81. ])
  82. def test_humanize_seconds(seconds, expected):
  83. assert humanize_seconds(seconds) == expected
  84. def test_humanize_seconds__prefix():
  85. assert humanize_seconds(4, prefix='about ') == 'about 4.00 seconds'
  86. def test_maybe_iso8601_datetime():
  87. now = datetime.now()
  88. assert maybe_iso8601(now) is now
  89. @pytest.mark.parametrize('arg,expected', [
  90. (30, timedelta(seconds=30)),
  91. (30.6, timedelta(seconds=30.6)),
  92. (timedelta(days=2), timedelta(days=2)),
  93. ])
  94. def test_maybe_timedelta(arg, expected):
  95. assert maybe_timedelta(arg) == expected
  96. def test_remaining_relative():
  97. remaining(datetime.utcnow(), timedelta(hours=1), relative=True)
  98. class test_timezone:
  99. def test_get_timezone_with_pytz(self):
  100. assert timezone.get_timezone('UTC')
  101. def test_tz_or_local(self):
  102. assert timezone.tz_or_local() == timezone.local
  103. assert timezone.tz_or_local(timezone.utc)
  104. def test_to_local(self):
  105. assert timezone.to_local(make_aware(datetime.utcnow(), timezone.utc))
  106. assert timezone.to_local(datetime.utcnow())
  107. def test_to_local_fallback(self):
  108. assert timezone.to_local_fallback(
  109. make_aware(datetime.utcnow(), timezone.utc))
  110. assert timezone.to_local_fallback(datetime.utcnow())
  111. class test_make_aware:
  112. def test_tz_without_localize(self):
  113. tz = tzinfo()
  114. assert not hasattr(tz, 'localize')
  115. wtz = make_aware(datetime.utcnow(), tz)
  116. assert wtz.tzinfo == tz
  117. def test_when_has_localize(self):
  118. class tzz(tzinfo):
  119. raises = False
  120. def localize(self, dt, is_dst=None):
  121. self.localized = True
  122. if self.raises and is_dst is None:
  123. self.raised = True
  124. raise AmbiguousTimeError()
  125. return 1 # needed by min() in Python 3 (None not hashable)
  126. tz = tzz()
  127. make_aware(datetime.utcnow(), tz)
  128. assert tz.localized
  129. tz2 = tzz()
  130. tz2.raises = True
  131. make_aware(datetime.utcnow(), tz2)
  132. assert tz2.localized
  133. assert tz2.raised
  134. def test_maybe_make_aware(self):
  135. aware = datetime.utcnow().replace(tzinfo=timezone.utc)
  136. assert maybe_make_aware(aware)
  137. naive = datetime.utcnow()
  138. assert maybe_make_aware(naive)
  139. assert maybe_make_aware(naive).tzinfo is pytz.utc
  140. tz = pytz.timezone('US/Eastern')
  141. eastern = datetime.utcnow().replace(tzinfo=tz)
  142. assert maybe_make_aware(eastern).tzinfo is tz
  143. utcnow = datetime.utcnow()
  144. assert maybe_make_aware(utcnow, 'UTC').tzinfo is pytz.utc
  145. class test_localize:
  146. def test_tz_without_normalize(self):
  147. tz = tzinfo()
  148. assert not hasattr(tz, 'normalize')
  149. assert localize(make_aware(datetime.utcnow(), tz), tz)
  150. def test_when_has_normalize(self):
  151. class tzz(tzinfo):
  152. raises = None
  153. def normalize(self, dt, **kwargs):
  154. self.normalized = True
  155. if self.raises and kwargs and kwargs.get('is_dst') is None:
  156. self.raised = True
  157. raise self.raises
  158. return 1 # needed by min() in Python 3 (None not hashable)
  159. tz = tzz()
  160. localize(make_aware(datetime.utcnow(), tz), tz)
  161. assert tz.normalized
  162. tz2 = tzz()
  163. tz2.raises = AmbiguousTimeError()
  164. localize(make_aware(datetime.utcnow(), tz2), tz2)
  165. assert tz2.normalized
  166. assert tz2.raised
  167. tz3 = tzz()
  168. tz3.raises = TypeError()
  169. localize(make_aware(datetime.utcnow(), tz3), tz3)
  170. assert tz3.normalized
  171. assert tz3.raised
  172. @pytest.mark.parametrize('s,expected', [
  173. (999, 999),
  174. (7.5, 7.5),
  175. ('2.5/s', 2.5),
  176. ('1456/s', 1456),
  177. ('100/m', 100 / 60.0),
  178. ('10/h', 10 / 60.0 / 60.0),
  179. (0, 0),
  180. (None, 0),
  181. ('0/m', 0),
  182. ('0/h', 0),
  183. ('0/s', 0),
  184. ('0.0/s', 0),
  185. ])
  186. def test_rate_limit_string(s, expected):
  187. assert rate(s) == expected
  188. class test_ffwd:
  189. def test_repr(self):
  190. x = ffwd(year=2012)
  191. assert repr(x)
  192. def test_radd_with_unknown_gives_NotImplemented(self):
  193. x = ffwd(year=2012)
  194. assert x.__radd__(object()) == NotImplemented
  195. class test_utcoffset:
  196. def test_utcoffset(self, patching):
  197. _time = patching('celery.utils.time._time')
  198. _time.daylight = True
  199. assert utcoffset(time=_time) is not None
  200. _time.daylight = False
  201. assert utcoffset(time=_time) is not None
  202. class test_get_exponential_backoff_interval:
  203. @patch('random.randrange', lambda n: n - 2)
  204. def test_with_jitter(self):
  205. assert get_exponential_backoff_interval(
  206. factor=4,
  207. retries=3,
  208. maximum=100,
  209. full_jitter=True
  210. ) == 4 * (2 ** 3) - 1
  211. def test_without_jitter(self):
  212. assert get_exponential_backoff_interval(
  213. factor=4,
  214. retries=3,
  215. maximum=100,
  216. full_jitter=False
  217. ) == 4 * (2 ** 3)
  218. def test_bound_by_maximum(self):
  219. maximum_boundary = 100
  220. assert get_exponential_backoff_interval(
  221. factor=40,
  222. retries=3,
  223. maximum=maximum_boundary
  224. ) == maximum_boundary
  225. @patch('random.randrange', lambda n: n - 1)
  226. def test_negative_values(self):
  227. assert get_exponential_backoff_interval(
  228. factor=-40,
  229. retries=3,
  230. maximum=100
  231. ) == 0