Commit 0140598b authored by Jérome Perrin's avatar Jérome Perrin

ERP5Type.tests.utils: introduce timeZoneContext

This is a context manager which temporary allow to change the time zone.

It is not thread safe and intended to be used in tests only, to replace
setTimeZoneToUTC and setTimezone methods by something unified which
makes the transition to DateTime 4 easier, because changing timezone
was done by patching private attributes and the DateTime 4 use different
attributes.
parent 61298b26
...@@ -27,12 +27,15 @@ ...@@ -27,12 +27,15 @@
# #
############################################################################## ##############################################################################
import os
import unittest import unittest
from DateTime import DateTime from DateTime import DateTime
from erp5.component.module.DateUtils import addToDate, getIntervalListBetweenDates, \ from erp5.component.module.DateUtils import addToDate, getIntervalListBetweenDates, \
atTheEndOfPeriod, getClosestDate atTheEndOfPeriod, getClosestDate
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
from Products.ERP5Type.tests.utils import timeZoneContext
class TestDateUtils(unittest.TestCase): class TestDateUtils(unittest.TestCase):
""" """
...@@ -199,8 +202,51 @@ class TestPinDateTime(ERP5TypeTestCase): ...@@ -199,8 +202,51 @@ class TestPinDateTime(ERP5TypeTestCase):
self.assertGreaterEqual(DateTime(), actual_begin_date) self.assertGreaterEqual(DateTime(), actual_begin_date)
class TestTimeZoneContext(ERP5TypeTestCase):
def afterSetUp(self):
self.reference_date_in_utc = DateTime('2001/02/03 00:00:00 UTC')
self.actual_timezone = DateTime().timezone()
self.actual_environ_tz = os.environ.get('TZ')
def test_timezone_context_UTC(self):
with timeZoneContext('UTC'):
self.assertEqual(DateTime().timezone(), 'UTC')
self.assertEqual(
DateTime(2001, 2, 3).toZone('UTC'), self.reference_date_in_utc)
self.assertEqual(DateTime().timezone(), self.actual_timezone)
self.assertEqual(os.environ.get('TZ'), self.actual_environ_tz)
def test_timezone_context_with_dst(self):
with timeZoneContext('Europe/Paris'):
self.assertEqual(DateTime(2021, 2, 1).timezone(), 'CET')
self.assertEqual(DateTime(2021, 7, 1).timezone(), 'CEST')
self.assertEqual(
DateTime(2001, 2, 3, 1, 0, 0).toZone('UTC'),
self.reference_date_in_utc)
self.assertEqual(DateTime().timezone(), self.actual_timezone)
self.assertEqual(os.environ.get('TZ'), self.actual_environ_tz)
def test_timezone_context_without_dst(self):
with timeZoneContext('Asia/Tokyo'):
self.assertEqual(DateTime().timezone(), 'JST')
self.assertEqual(
DateTime(2001, 2, 3, 9, 0, 0).toZone('UTC'), self.reference_date_in_utc)
self.assertEqual(DateTime().timezone(), self.actual_timezone)
self.assertEqual(os.environ.get('TZ'), self.actual_environ_tz)
def test_timezone_abbreviation(self):
with timeZoneContext('GMT-7'):
self.assertEqual(DateTime(2021, 2, 1).timezone(), 'GMT-7')
self.assertEqual(DateTime(2021, 7, 1).timezone(), 'GMT-7')
self.assertEqual(
DateTime(2001, 2, 2, 17, 0, 0).toZone('UTC'), self.reference_date_in_utc)
self.assertEqual(DateTime().timezone(), self.actual_timezone)
self.assertEqual(os.environ.get('TZ'), self.actual_environ_tz)
def test_suite(): def test_suite():
suite = unittest.TestSuite() suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestDateUtils)) suite.addTest(unittest.makeSuite(TestDateUtils))
suite.addTest(unittest.makeSuite(TestPinDateTime)) suite.addTest(unittest.makeSuite(TestPinDateTime))
suite.addTest(unittest.makeSuite(TestTimeZoneContext))
return suite return suite
import os, time from Products.ERP5Type.tests.utils import timeZoneContext
from DateTime import DateTime
current_timezone_contexts = []
def setTimezone(timezone): def setTimezone(timezone):
# timezone must be for example GMT-7 """Change the default timezone to `timezone`.
os.environ['TZ'] = timezone """
time.tzset() if current_timezone_contexts:
DateTime._isDST = False resetTimeZone()
DateTime._localzone = DateTime._localzone0 = DateTime._localzone1 = timezone
tzc = timeZoneContext(timezone)
tzc.__enter__()
current_timezone_contexts.append(tzc)
return "Timezone Updated" return "Timezone Updated"
def resetTimeZone():
"""Reset the timezone that might have been set by `setTimezone`
"""
current_timezone_contexts.pop().__exit__(None, None, None)
...@@ -66,6 +66,7 @@ from Products.ERP5Type.Utils import convertToUpperCase, str2bytes ...@@ -66,6 +66,7 @@ from Products.ERP5Type.Utils import convertToUpperCase, str2bytes
from Products.ERP5Type.tests.backportUnittest import SetupSiteError from Products.ERP5Type.tests.backportUnittest import SetupSiteError
from Products.ERP5Type.tests.utils import addUserToDeveloperRole from Products.ERP5Type.tests.utils import addUserToDeveloperRole
from Products.ERP5Type.tests.utils import parseListeningAddress from Products.ERP5Type.tests.utils import parseListeningAddress
from Products.ERP5Type.tests.utils import timeZoneContext
# Quiet messages when installing business templates # Quiet messages when installing business templates
install_bt5_quiet = 0 install_bt5_quiet = 0
...@@ -376,15 +377,10 @@ class ERP5TypeTestCaseMixin(ProcessingNodeTestCase, PortalTestCase): ...@@ -376,15 +377,10 @@ class ERP5TypeTestCaseMixin(ProcessingNodeTestCase, PortalTestCase):
self.pinDateTime(None) self.pinDateTime(None)
def setTimeZoneToUTC(self): def setTimeZoneToUTC(self):
# Make sure tests runs with UTC timezone. Some tests are checking values # Deprecated, prefer using `timeZoneContext` context manager instead.
# based on now, and this could give unexpected results: timezone = timeZoneContext('UTC')
# DateTime("2016/10/31") - DateTime("2016/10/30") = 1.0416666666666667 if timezone.__enter__()
# you are running on a timezone like Europe/Paris, while it return 1.0 for self.addCleanup(timezone.__exit__, None, None, None)
# UTC
os.environ['TZ'] = "UTC"
time.tzset()
DateTime._isDST = False
DateTime._localzone = DateTime._localzone0 = DateTime._localzone1 = "UTC"
def getDefaultSystemPreference(self): def getDefaultSystemPreference(self):
id = 'default_system_preference' id = 'default_system_preference'
......
...@@ -28,12 +28,16 @@ ...@@ -28,12 +28,16 @@
"""Utility functions and classes for unit testing """Utility functions and classes for unit testing
""" """
import contextlib
from datetime import datetime
import errno import errno
import os import os
import logging import logging
import mock
import random import random
import socket import socket
import sys import sys
import time
import unittest import unittest
import ZODB import ZODB
import zLOG import zLOG
...@@ -49,6 +53,7 @@ from email import message_from_string ...@@ -49,6 +53,7 @@ from email import message_from_string
from Products.ERP5Type.Globals import PersistentMapping from Products.ERP5Type.Globals import PersistentMapping
from Products.ERP5Type.Utils import simple_decorator from Products.ERP5Type.Utils import simple_decorator
from Products.ZSQLCatalog.SQLCatalog import Catalog from Products.ZSQLCatalog.SQLCatalog import Catalog
import pytz
import six import six
class FileUpload(file): class FileUpload(file):
...@@ -707,3 +712,35 @@ class SubcontentReindexingWrapper(object): ...@@ -707,3 +712,35 @@ class SubcontentReindexingWrapper(object):
self.assertEqual(expected_path_set, catalogged_object_path_set) self.assertEqual(expected_path_set, catalogged_object_path_set)
finally: finally:
Catalog.catalogObjectList = orig_catalogObjectList Catalog.catalogObjectList = orig_catalogObjectList
@contextlib.contextmanager
def timeZoneContext(timezone):
"""Context manager to change timezone in tests.
"""
saved_TZ = os.environ.get('TZ')
os.environ['TZ'] = timezone
time.tzset()
if timezone in pytz.all_timezones:
_multipleZones = time.daylight
_localzone0 = time.tzname[0]
_localzone1 = time.tzname[1] if time.daylight else time.tzname[0]
else:
_multipleZones = False
_localzone0 = _localzone1 = timezone
if hasattr(sys.modules['DateTime.DateTime'].DateTime, '_localzone0'):
patch_target = sys.modules['DateTime.DateTime'].DateTime
else:
# BBB DateTime 2
patch_target = sys.modules['DateTime.DateTime']
try:
with mock.patch.object(patch_target, '_localzone0', new=_localzone0), \
mock.patch.object(patch_target, '_localzone1', new=_localzone1), \
mock.patch.object(patch_target, '_multipleZones', new=_multipleZones):
yield
finally:
os.environ.pop('TZ')
if saved_TZ:
os.environ['TZ'] = saved_TZ
time.tzset()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment