From 2d39d727c21050fe43d221039cb725367ba2df46 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Mon, 24 Oct 2022 18:56:58 -0300 Subject: [PATCH 1/4] test: avoid local times near midnight for test_past_swap_days_buttons() --- ietf/meeting/tests_js.py | 8 +++++++- ietf/utils/timezone.py | 16 ++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/ietf/meeting/tests_js.py b/ietf/meeting/tests_js.py index 2d221994a8f..974d2749281 100644 --- a/ietf/meeting/tests_js.py +++ b/ietf/meeting/tests_js.py @@ -34,6 +34,7 @@ from ietf.utils.test_utils import assert_ical_response_is_valid from ietf.utils.jstest import ( IetfSeleniumTestCase, ifSeleniumEnabled, selenium_enabled, presence_of_element_child_by_css_selector ) +from ietf.utils.timezone import timezone_not_near_midnight if selenium_enabled(): from selenium.webdriver.common.action_chains import ActionChains @@ -392,7 +393,12 @@ def test_past_flags(self): def test_past_swap_days_buttons(self): """Swap days buttons should be hidden for past items""" wait = WebDriverWait(self.driver, 2) - meeting = MeetingFactory(type_id='ietf', date=datetime.datetime.today() - datetime.timedelta(days=3), days=7) + meeting = MeetingFactory( + type_id='ietf', + date=datetime.datetime.today() - datetime.timedelta(days=3), + days=7, + time_zone=timezone_not_near_midnight(), + ) room = RoomFactory(meeting=meeting) # get current time in meeting time zone diff --git a/ietf/utils/timezone.py b/ietf/utils/timezone.py index 723512efeb6..4c72a9ea25a 100644 --- a/ietf/utils/timezone.py +++ b/ietf/utils/timezone.py @@ -1,8 +1,11 @@ import pytz import email.utils import datetime +import random from django.conf import settings +from django.utils import timezone + def local_timezone_to_utc(d): """Takes a naive datetime in the local timezone and returns a @@ -37,3 +40,16 @@ def email_time_to_local_timezone(date_string): def date2datetime(date, tz=pytz.utc): return datetime.datetime(*(date.timetuple()[:6]), tzinfo=tz) + +def timezone_not_near_midnight(): + """Get the name of a random timezone where it's not close to midnight + + Avoids midnight +/- 1 hour. + """ + timezone_options = pytz.common_timezones + tzname = random.choice(timezone_options) + right_now = timezone.now().astimezone(pytz.timezone(tzname)) + while right_now.hour < 1 or right_now.hour >= 23: + tzname = random.choice(pytz.common_timezones) + right_now = right_now.astimezone(pytz.timezone(tzname)) + return tzname From df67d02ef8440e5ed3d55154799c7ecd0dcbc9db Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Tue, 25 Oct 2022 09:42:53 -0300 Subject: [PATCH 2/4] test: use timezone_options variable consistently --- ietf/utils/timezone.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ietf/utils/timezone.py b/ietf/utils/timezone.py index 4c72a9ea25a..d96ac47af8e 100644 --- a/ietf/utils/timezone.py +++ b/ietf/utils/timezone.py @@ -50,6 +50,6 @@ def timezone_not_near_midnight(): tzname = random.choice(timezone_options) right_now = timezone.now().astimezone(pytz.timezone(tzname)) while right_now.hour < 1 or right_now.hour >= 23: - tzname = random.choice(pytz.common_timezones) + tzname = random.choice(timezone_options) right_now = right_now.astimezone(pytz.timezone(tzname)) return tzname From 28a8b51fd2a8222a115c74d4129adff803492a2d Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Wed, 26 Oct 2022 10:24:43 -0300 Subject: [PATCH 3/4] test: add test of timezone_not_near_midnight() method --- ietf/utils/tests.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/ietf/utils/tests.py b/ietf/utils/tests.py index 41679a48bae..5075a2dcb02 100644 --- a/ietf/utils/tests.py +++ b/ietf/utils/tests.py @@ -2,12 +2,15 @@ # -*- coding: utf-8 -*- +import datetime import io import json import os.path +import pytz import shutil import types +from mock import patch from pyquery import PyQuery from typing import Dict, List # pyflakes:ignore @@ -39,6 +42,7 @@ from ietf.utils.test_runner import get_template_paths, set_coverage_checking from ietf.utils.test_utils import TestCase, unicontent from ietf.utils.text import parse_unicode +from ietf.utils.timezone import timezone_not_near_midnight from ietf.utils.xmldraft import XMLDraft class SendingMail(TestCase): @@ -476,3 +480,28 @@ def test_manifest(self): manifest = json.loads(unicontent(r)) self.assertTrue('name' in manifest) self.assertTrue('theme_color' in manifest) + + +class TimezoneTests(TestCase): + """Tests of the timezone utilities""" + @patch( + 'ietf.utils.timezone.timezone.now', + return_value=pytz.timezone('America/Chicago').localize(datetime.datetime(2022, 7, 1, 23, 15, 0)), # 23:15:00 + ) + def test_timezone_not_near_midnight(self, mock): + # give it several choices that should be rejected and one that should be accepted + with patch( + 'ietf.utils.timezone.pytz.common_timezones', + [ + 'America/Chicago', # time is 23:15, should be rejected + 'America/Lima', # time is 23:15, should be rejected + 'America/New_York', # time is 00:15, should be rejected + 'Europe/Riga', # time is 07:15, acceptable + ], + ): + # check a few times (will pass by chance < 0.1% of the time) + self.assertEqual(timezone_not_near_midnight(), 'Europe/Riga') + self.assertEqual(timezone_not_near_midnight(), 'Europe/Riga') + self.assertEqual(timezone_not_near_midnight(), 'Europe/Riga') + self.assertEqual(timezone_not_near_midnight(), 'Europe/Riga') + self.assertEqual(timezone_not_near_midnight(), 'Europe/Riga') From 85c3ad089b0b713642fb4e417cd91948a59a41c1 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Wed, 26 Oct 2022 10:39:34 -0300 Subject: [PATCH 4/4] fix: ensure that timezone_not_near_midnight() always exits --- ietf/utils/tests.py | 12 ++++++++++++ ietf/utils/timezone.py | 9 ++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/ietf/utils/tests.py b/ietf/utils/tests.py index 5075a2dcb02..4814235f6d5 100644 --- a/ietf/utils/tests.py +++ b/ietf/utils/tests.py @@ -505,3 +505,15 @@ def test_timezone_not_near_midnight(self, mock): self.assertEqual(timezone_not_near_midnight(), 'Europe/Riga') self.assertEqual(timezone_not_near_midnight(), 'Europe/Riga') self.assertEqual(timezone_not_near_midnight(), 'Europe/Riga') + + # now give it no valid choice + with patch( + 'ietf.utils.timezone.pytz.common_timezones', + [ + 'America/Chicago', # time is 23:15, should be rejected + 'America/Lima', # time is 23:15, should be rejected + 'America/New_York', # time is 00:15, should be rejected + ], + ): + with self.assertRaises(RuntimeError): + timezone_not_near_midnight() diff --git a/ietf/utils/timezone.py b/ietf/utils/timezone.py index d96ac47af8e..9106c3ae310 100644 --- a/ietf/utils/timezone.py +++ b/ietf/utils/timezone.py @@ -44,12 +44,19 @@ def date2datetime(date, tz=pytz.utc): def timezone_not_near_midnight(): """Get the name of a random timezone where it's not close to midnight - Avoids midnight +/- 1 hour. + Avoids midnight +/- 1 hour. Raises RuntimeError if it is unable to find + a time zone satisfying this constraint. """ timezone_options = pytz.common_timezones tzname = random.choice(timezone_options) right_now = timezone.now().astimezone(pytz.timezone(tzname)) + # Avoid the remote possibility of an infinite loop (might come up + # if there is a problem with the time zone library) + tries_left = 20 while right_now.hour < 1 or right_now.hour >= 23: tzname = random.choice(timezone_options) right_now = right_now.astimezone(pytz.timezone(tzname)) + tries_left -= 1 + if tries_left <= 0: + raise RuntimeError('Unable to find a time zone not near midnight') return tzname