Skip to content

Commit abf0ec9

Browse files
committed
circumvent freezegun in favor of a real clock where possible
1 parent 7d0a0e7 commit abf0ec9

File tree

7 files changed

+68
-11
lines changed

7 files changed

+68
-11
lines changed

tests/builder_test.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44

55
import xml.etree.ElementTree as ET
66
from xml.dom.minidom import Document
7+
from freezegun import freeze_time
78

89
from xmlrunner import builder
10+
import time
911

1012

1113
class TestXMLContextTest(unittest.TestCase):
@@ -88,6 +90,19 @@ def test_add_timestamp_attribute_on_end_context(self):
8890

8991
element.attributes['timestamp'].value
9092

93+
@freeze_time('2012-12-21')
94+
def test_time_and_timestamp_attributes_are_not_affected_by_freezegun(self):
95+
self.root.begin('testsuite', 'name')
96+
time.sleep(.015)
97+
element = self.root.end()
98+
99+
current_year = int(element.attributes['timestamp'].value[:4])
100+
self.assertGreater(current_year, 2012)
101+
self.assertLess(current_year, 2100)
102+
103+
current_time = float(element.attributes['time'].value)
104+
self.assertGreater(current_time, .012)
105+
self.assertLess(current_time, .020)
91106

92107
class TestXMLBuilderTest(unittest.TestCase):
93108
"""TestXMLBuilder test cases.

tests/testsuite.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626
import os
2727
import os.path
2828
from unittest import mock
29+
from time import sleep
30+
from freezegun import freeze_time
31+
import re
2932

3033

3134
def _load_schema(version):
@@ -116,6 +119,9 @@ def test_non_ascii_skip(self):
116119
def test_pass(self):
117120
pass
118121

122+
def test_pass_after_30ms(self):
123+
sleep(.030)
124+
119125
def test_fail(self):
120126
self.assertTrue(False)
121127

@@ -733,6 +739,24 @@ def test_xmlrunner_elapsed_times(self):
733739
suite.addTest(self.DummyTest('test_pass'))
734740
self._test_xmlrunner(suite)
735741

742+
@freeze_time('2012-12-21')
743+
def test_xmlrunner_time_measurement_unaffected_by_freezegun(self):
744+
suite = unittest.TestSuite()
745+
outdir = BytesIO()
746+
suite.addTest(self.DummyTest('test_pass_after_30ms'))
747+
runner = xmlrunner.XMLTestRunner(
748+
stream=self.stream, output=outdir, verbosity=self.verbosity,
749+
**self.runner_kwargs)
750+
runner.run(suite)
751+
outdir.seek(0)
752+
output = outdir.read()
753+
match = re.search(br'time="(.+?)"', output)
754+
self.assertIsNotNone(match)
755+
time_value = float(match.group(1))
756+
# Provide a generous margin for this 30ms test to make flakes unlikely.
757+
self.assertGreater(time_value, 0.025)
758+
self.assertLess(time_value, 0.050)
759+
736760
def test_xmlrunner_resultclass(self):
737761
class Result(_XMLTestResult):
738762
pass

tox.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ deps =
2121
djangocurr: django>=2.2.0
2222
pytest: pytest
2323
lxml>=3.6.0
24+
freezegun
2425
commands =
2526
coverage run --append setup.py test
2627
coverage report --omit='.tox/*'

xmlrunner/builder.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import re
22
import sys
33
import datetime
4-
import time
54

65
from xml.dom.minidom import Document
76

7+
from .time import get_real_time_if_possible
8+
89

910
__all__ = ('TestXMLBuilder', 'TestXMLContext')
1011

@@ -72,13 +73,13 @@ def begin(self, tag, name):
7273
"""
7374
self.element = self.xml_doc.createElement(tag)
7475
self.element.setAttribute('name', replace_nontext(name))
75-
self._start_time = time.time()
76+
self._start_time = get_real_time_if_possible()
7677

7778
def end(self):
7879
"""Closes this context (started with a call to `begin`) and creates an
7980
attribute for each counter and another for the elapsed time.
8081
"""
81-
self._stop_time = time.time()
82+
self._stop_time = get_real_time_if_possible()
8283
self.element.setAttribute('time', self.elapsed_time())
8384
self.element.setAttribute('timestamp', self.timestamp())
8485
self._set_result_counters()

xmlrunner/result.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@
99
from os import path
1010
from io import StringIO
1111

12-
# use direct import to bypass freezegun
13-
from time import time
14-
12+
from .time import get_real_time_if_possible
1513
from .unittest import TestResult, _TextTestResult, failfast
1614

1715

@@ -253,7 +251,7 @@ def startTest(self, test):
253251
"""
254252
Called before execute each test method.
255253
"""
256-
self.start_time = time()
254+
self.start_time = get_real_time_if_possible()
257255
TestResult.startTest(self, test)
258256

259257
try:
@@ -321,7 +319,8 @@ def stopTest(self, test):
321319
# self._stderr_data = sys.stderr.getvalue()
322320

323321
_TextTestResult.stopTest(self, test)
324-
self.stop_time = time()
322+
323+
self.stop_time = get_real_time_if_possible()
325324

326325
if self.callback and callable(self.callback):
327326
self.callback()

xmlrunner/runner.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11

22
import argparse
33
import sys
4+
45
import time
56

7+
from .time import get_real_time_if_possible
68
from .unittest import TextTestRunner, TestProgram
79
from .result import _XMLTestResult
810

@@ -15,7 +17,7 @@ class XMLTestRunner(TextTestRunner):
1517
"""
1618
A test runner class that outputs the results in JUnit like XML files.
1719
"""
18-
def __init__(self, output='.', outsuffix=None,
20+
def __init__(self, output='.', outsuffix=None,
1921
elapsed_times=True, encoding=UTF8,
2022
resultclass=None,
2123
**kwargs):
@@ -62,9 +64,9 @@ def run(self, test):
6264
self.stream.writeln(result.separator2)
6365

6466
# Execute tests
65-
start_time = time.time()
67+
start_time = get_real_time_if_possible()
6668
test(result)
67-
stop_time = time.time()
69+
stop_time = get_real_time_if_possible()
6870
time_taken = stop_time - start_time
6971

7072
# Print results

xmlrunner/time.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
"""Timing function that will attempt to circumvent freezegun and other common
2+
techniques of mocking time in Python unit tests; will only succeed at this on
3+
Unix currently. Falls back to regular time.time().
4+
"""
5+
6+
import time
7+
8+
def get_real_time_if_possible():
9+
if hasattr(time, 'clock_gettime'):
10+
try:
11+
return time.clock_gettime(time.CLOCK_REALTIME)
12+
except OSError:
13+
# would occur if time.CLOCK_REALTIME is not available, for example
14+
pass
15+
return time.time()

0 commit comments

Comments
 (0)