Skip to content

Commit c034871

Browse files
committed
Support addDuration
Fixes LP: #2045171
1 parent 750421e commit c034871

File tree

4 files changed

+151
-0
lines changed

4 files changed

+151
-0
lines changed

NEWS

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ Changes
1111

1212
* Drop support for Python 3.8. (Stephen Finucane)
1313

14+
Improvements
15+
------------
16+
17+
* Add support for Python 3.12's ``addDuration`` method and ``collectedDurations``
18+
attribute in ``TestResult`` classes. (Jelmer Vernooij, #2045171)
19+
1420
* Remove a number of deprecated classes and methods. (Stephen Finucane)
1521

1622
* ``testtools.matchers``

testtools/testresult/doubles.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ def __init__(self, event_log=None):
3232
self._was_successful = True
3333
self.testsRun = 0
3434
self.failfast = False
35+
self.collectedDurations = []
3536

3637
def addError(self, test, err):
3738
self._was_successful = False
@@ -59,6 +60,10 @@ def addUnexpectedSuccess(self, test):
5960
if self.failfast:
6061
self.stop()
6162

63+
def addDuration(self, test, duration):
64+
self._events.append(("addDuration", test, duration))
65+
self.collectedDurations.append((test, duration))
66+
6267
def startTest(self, test):
6368
self._events.append(("startTest", test))
6469
self.testsRun += 1
@@ -113,6 +118,9 @@ def addUnexpectedSuccess(self, test, details=None):
113118
else:
114119
self._events.append(("addUnexpectedSuccess", test))
115120

121+
def addDuration(self, test, duration):
122+
self._events.append(("addDuration", test, duration))
123+
116124
def progress(self, offset, whence):
117125
self._events.append(("progress", offset, whence))
118126

testtools/testresult/real.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,14 @@ def addUnexpectedSuccess(self, test, details=None):
159159
if self.failfast:
160160
self.stop()
161161

162+
def addDuration(self, test, duration):
163+
"""Called to add a test duration.
164+
165+
:param test: The test that completed.
166+
:param duration: The duration of the test as a float in seconds.
167+
"""
168+
self.collectedDurations.append((test, duration))
169+
162170
def wasSuccessful(self):
163171
"""Has this result been successful so far?
164172
@@ -215,6 +223,8 @@ def startTestRun(self):
215223
# -- End: As per python 2.7 --
216224
# -- Python 3.5
217225
self.tb_locals = tb_locals
226+
# -- Python 3.12
227+
self.collectedDurations = []
218228

219229
def stopTestRun(self):
220230
"""Called after a test run completes
@@ -1994,6 +2004,9 @@ def addExpectedFailure(self, test, err=None, details=None):
19942004
def addUnexpectedSuccess(self, test, details=None):
19952005
return self.decorated.addUnexpectedSuccess(test, details=details)
19962006

2007+
def addDuration(self, test, duration):
2008+
return self.decorated.addDuration(test, duration)
2009+
19972010
def progress(self, offset, whence):
19982011
return self.decorated.progress(offset, whence)
19992012

testtools/tests/test_testresult.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,75 @@ def makeResult(self):
478478
return TestResultDecorator(TestResult())
479479

480480

481+
class TestPython3TestResultDuration(TestCase):
482+
"""Tests for addDuration functionality in Python3TestResult."""
483+
484+
def test_addDuration_logging(self):
485+
# Python3TestResult should log addDuration calls
486+
result = Python3TestResult()
487+
test = make_erroring_test()
488+
489+
result.addDuration(test, 1.5)
490+
expected_call = ("addDuration", test, 1.5)
491+
self.assertIn(expected_call, result._events)
492+
493+
def test_addDuration_stores_in_collectedDurations(self):
494+
# Python3TestResult should store durations in collectedDurations
495+
result = Python3TestResult()
496+
test = make_erroring_test()
497+
498+
result.addDuration(test, 2.3)
499+
self.assertEqual([(test, 2.3)], result.collectedDurations)
500+
501+
# Multiple durations should accumulate
502+
result.addDuration(test, 1.7)
503+
self.assertEqual([(test, 2.3), (test, 1.7)], result.collectedDurations)
504+
505+
506+
class TestExtendedTestResultDuration(TestCase):
507+
"""Tests for addDuration functionality in ExtendedTestResult."""
508+
509+
def test_addDuration_logging(self):
510+
# ExtendedTestResult should log addDuration calls
511+
result = ExtendedTestResult()
512+
test = make_erroring_test()
513+
514+
result.addDuration(test, 3.1)
515+
expected_call = ("addDuration", test, 3.1)
516+
self.assertIn(expected_call, result._events)
517+
518+
519+
class TestTestResultDecoratorDuration(TestCase):
520+
"""Tests for addDuration functionality in TestResultDecorator."""
521+
522+
def test_addDuration_forwards_to_decorated_result(self):
523+
# TestResultDecorator should forward addDuration calls to decorated result
524+
base_result = TestResult()
525+
base_result.startTestRun() # Initialize collectedDurations
526+
decorator = TestResultDecorator(base_result)
527+
test = make_erroring_test()
528+
529+
decorator.addDuration(test, 4.2)
530+
531+
# Check that the duration was stored in the base result
532+
self.assertEqual([(test, 4.2)], base_result.collectedDurations)
533+
534+
def test_addDuration_multiple_forwards(self):
535+
# Multiple addDuration calls should all be forwarded
536+
base_result = TestResult()
537+
base_result.startTestRun()
538+
decorator = TestResultDecorator(base_result)
539+
test1 = make_erroring_test()
540+
test2 = make_erroring_test()
541+
542+
decorator.addDuration(test1, 1.1)
543+
decorator.addDuration(test2, 2.2)
544+
decorator.addDuration(test1, 3.3)
545+
546+
expected = [(test1, 1.1), (test2, 2.2), (test1, 3.3)]
547+
self.assertEqual(expected, base_result.collectedDurations)
548+
549+
481550
# DetailsContract because ExtendedToStreamDecorator follows Python for
482551
# uxsuccess handling.
483552
class TestStreamToExtendedContract(TestCase, DetailsContract):
@@ -1521,6 +1590,61 @@ def test_traceback_with_locals(self):
15211590
),
15221591
)
15231592

1593+
def test_addDuration(self):
1594+
# addDuration stores test-duration pairs in collectedDurations
1595+
result = self.makeResult()
1596+
test1 = make_erroring_test()
1597+
test2 = make_erroring_test()
1598+
1599+
# Initially collectedDurations should be empty after startTestRun
1600+
result.startTestRun()
1601+
self.assertEqual([], result.collectedDurations)
1602+
1603+
# Adding durations should store them as (test, duration) tuples
1604+
result.addDuration(test1, 1.5)
1605+
self.assertEqual([(test1, 1.5)], result.collectedDurations)
1606+
1607+
result.addDuration(test2, 2.3)
1608+
self.assertEqual([(test1, 1.5), (test2, 2.3)], result.collectedDurations)
1609+
1610+
# Adding duration for same test should create separate entries
1611+
result.addDuration(test1, 3.7)
1612+
expected = [(test1, 1.5), (test2, 2.3), (test1, 3.7)]
1613+
self.assertEqual(expected, result.collectedDurations)
1614+
1615+
def test_addDuration_with_zero_duration(self):
1616+
# addDuration should work with zero duration
1617+
result = self.makeResult()
1618+
test = make_erroring_test()
1619+
result.startTestRun()
1620+
1621+
result.addDuration(test, 0.0)
1622+
self.assertEqual([(test, 0.0)], result.collectedDurations)
1623+
1624+
def test_addDuration_with_negative_duration(self):
1625+
# addDuration should work with negative duration (edge case)
1626+
result = self.makeResult()
1627+
test = make_erroring_test()
1628+
result.startTestRun()
1629+
1630+
result.addDuration(test, -1.0)
1631+
self.assertEqual([(test, -1.0)], result.collectedDurations)
1632+
1633+
def test_collectedDurations_resets_on_startTestRun(self):
1634+
# collectedDurations should be reset when startTestRun is called
1635+
result = self.makeResult()
1636+
test = make_erroring_test()
1637+
1638+
# Add some durations
1639+
result.startTestRun()
1640+
result.addDuration(test, 1.0)
1641+
result.addDuration(test, 2.0)
1642+
self.assertEqual([(test, 1.0), (test, 2.0)], result.collectedDurations)
1643+
1644+
# Starting a new test run should reset
1645+
result.startTestRun()
1646+
self.assertEqual([], result.collectedDurations)
1647+
15241648

15251649
class TestMultiTestResult(TestCase):
15261650
"""Tests for 'MultiTestResult'."""

0 commit comments

Comments
 (0)