Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion tests/testsuite.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,8 +240,18 @@ def _test_xmlrunner(self, suite, runner=None, outdir=None):
runner.run(suite)
if isinstance(outdir, BytesIO):
self.assertTrue(outdir.getvalue())
xml_content = outdir.getvalue()
else:
self.assertEqual(1, len(glob(os.path.join(outdir, '*xml'))))
xml_files = glob(os.path.join(outdir, '*xml'))
self.assertEqual(1, len(xml_files))
with open(xml_files[0], 'rb') as f:
xml_content = f.read()

doc = minidom.parseString(xml_content)
for testcase in doc.getElementsByTagName('testcase'):
elapsed = float(testcase.getAttribute('time'))
self.assertGreaterEqual(elapsed, 0.0)

return runner

def test_basic_unittest_constructs(self):
Expand Down Expand Up @@ -903,6 +913,15 @@ class TestWithPartialmethod(unittest.TestCase):
suite.addTest(TestWithPartialmethod('test_partialmethod'))
self._test_xmlrunner(suite)

def test_elapsed_time_with_docstring(self):
class DocstringTest(unittest.TestCase):
def test_with_doc(self):
"""This test has a docstring."""
pass

suite = unittest.TestSuite()
suite.addTest(DocstringTest('test_with_doc'))
self._test_xmlrunner(suite)


class DuplicateWriterTestCase(unittest.TestCase):
Expand Down
20 changes: 18 additions & 2 deletions xmlrunner/result.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,10 +167,12 @@ def __init__(self, test_result, test_method, outcome=SUCCESS, err=None, subTest=

self.test_name = testcase_name(test_method)
self.test_id = test_method.id()
self.test_str = str(test_method)

if subTest:
self.test_id = subTest.id()
self.test_description = self.test_result.getDescription(subTest)
self.test_str = str(subTest)

self.filename = filename
self.lineno = lineno
Expand All @@ -179,11 +181,25 @@ def __init__(self, test_result, test_method, outcome=SUCCESS, err=None, subTest=
def id(self):
return self.test_id

def _get_elapsed_time(self):
"""Return elapsed time from addDuration (3.12+) or start/stop delta.

In parallel test runners like Django's, startTest/stopTest events are
replayed in the parent process, making stop_time - start_time near-zero.
Python 3.12's addDuration carries the real worker-measured elapsed time
correctly allowing us to prevent this issue for 3.12+.
"""
collected = getattr(self.test_result, 'collectedDurations', [])
for test_repr, elapsed in reversed(collected):
if test_repr == self.test_str:
return elapsed

return self.test_result.stop_time - self.test_result.start_time

def test_finished(self):
"""Save info that can only be calculated once a test has run.
"""
self.elapsed_time = \
self.test_result.stop_time - self.test_result.start_time
self.elapsed_time = self._get_elapsed_time()
timestamp = datetime.datetime.fromtimestamp(self.test_result.stop_time)
self.timestamp = timestamp.replace(microsecond=0).isoformat()

Expand Down