Skip to content

Commit 5a0874f

Browse files
committed
Remove a bunch of redundant code from parallel_testsuite.py
All this infrastructure with the BufferedTestBase was added to work around that fact that we were previously using using a fake TestResult class so that is could be serialized. However in #25737 I converted to using TestResult as the superclass of BufferedParallelTestResult. There were only two members that needed to me removed to make it work. With that change landed we can now remove a log of the other code here.
1 parent 33ec3ec commit 5a0874f

File tree

1 file changed

+25
-118
lines changed

1 file changed

+25
-118
lines changed

test/parallel_testsuite.py

Lines changed: 25 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@ def printResult(res):
7777
msg = f'{res.test} ... FAIL'
7878
errlog(f'{progress}{with_color(RED, msg)}')
7979
elif res.test_result == 'skipped':
80-
msg = f"skipped '{res.buffered_result.reason}'"
80+
reason = res.skipped[0][1]
81+
msg = f"skipped '{reason}'"
8182
errlog(f"{progress}{res.test} ... {with_color(CYAN, msg)}")
8283
elif res.test_result == 'unexpected success':
8384
msg = f'unexpected success ({elapsed:.2f}s)'
@@ -276,14 +277,10 @@ class BufferedParallelTestResult(unittest.TestResult):
276277
"""
277278
def __init__(self):
278279
super().__init__()
279-
self.buffered_result = None
280280
self.test_duration = 0
281281
self.test_result = 'errored'
282282
self.test_name = ''
283-
284-
@property
285-
def test(self):
286-
return self.buffered_result.test
283+
self.test = None
287284

288285
def test_short_name(self):
289286
# Given a test name e.g. "test_atomic_cxx (test_core.core0.test_atomic_cxx)"
@@ -294,8 +291,28 @@ def addDuration(self, test, elapsed):
294291
self.test_duration = elapsed
295292

296293
def updateResult(self, result):
294+
# The exception info objects that we are adding here have already
295+
# been turned into strings so make _exc_info_to_string into a no-op.
296+
result._exc_info_to_string = lambda x, _y: x
297+
# No need to worry about stdou/stderr buffering since are a not
298+
# actually runnin the test here, only setting the results.
299+
result.buffer = False
297300
result.startTest(self.test)
298-
self.buffered_result.updateResult(result)
301+
if self.test_result == 'success':
302+
result.addSuccess(self.test)
303+
elif self.test_result == 'failed':
304+
result.addFailure(*self.failures[0])
305+
elif self.test_result == 'errored':
306+
result.addError(*self.errors[0])
307+
elif self.test_result == 'skipped':
308+
result.addSkip(*self.skipped[0])
309+
elif self.test_result == 'unexpected success':
310+
result.addUnexpectedSuccess(*self.unexpectedSuccessesed[0])
311+
elif self.test_result == 'expected failed':
312+
result.addExpectedFailure(*self.expectedFailures[0])
313+
else:
314+
assert False
315+
result.addDuration(self.test, self.test_duration)
299316
result.stopTest(self.test)
300317
result.core_time += self.test_duration
301318
self.log_test_run_for_visualization()
@@ -325,13 +342,11 @@ def log_test_run_for_visualization(self):
325342

326343
def startTest(self, test):
327344
super().startTest(test)
345+
self.test = test
328346
self.test_name = str(test)
329347

330348
def stopTest(self, test):
331349
super().stopTest(test)
332-
# TODO(sbc): figure out a way to display this duration information again when
333-
# these results get passed back to the TextTestRunner/TextTestResult.
334-
self.buffered_result.duration = self.test_duration
335350
# Once we are done running the test and any stdout/stderr buffering has
336351
# being taking care or, we delete these fields which the parent class uses.
337352
# This is because they are not picklable (serializable).
@@ -340,137 +355,29 @@ def stopTest(self, test):
340355

341356
def addSuccess(self, test):
342357
super().addSuccess(test)
343-
self.buffered_result = BufferedTestSuccess(test)
344358
self.test_result = 'success'
345359

346360
def addExpectedFailure(self, test, err):
347361
super().addExpectedFailure(test, err)
348-
self.buffered_result = BufferedTestExpectedFailure(test, err)
349362
self.test_result = 'expected failure'
350363

351364
def addUnexpectedSuccess(self, test):
352365
super().addUnexpectedSuccess(test)
353-
self.buffered_result = BufferedTestUnexpectedSuccess(test)
354366
self.test_result = 'unexpected success'
355367

356368
def addSkip(self, test, reason):
357369
super().addSkip(test, reason)
358-
self.buffered_result = BufferedTestSkip(test, reason)
359370
self.test_result = 'skipped'
360371

361372
def addFailure(self, test, err):
362373
super().addFailure(test, err)
363-
self.buffered_result = BufferedTestFailure(test, err)
364374
self.test_result = 'failed'
365375

366376
def addError(self, test, err):
367377
super().addError(test, err)
368-
self.buffered_result = BufferedTestError(test, err)
369378
self.test_result = 'errored'
370379

371380

372-
class BufferedTestBase:
373-
"""Abstract class that holds test result data, split by type of result."""
374-
def __init__(self, test, err=None):
375-
self.test = test
376-
if err:
377-
exctype, value, tb = err
378-
self.error = exctype, value, FakeTraceback(tb)
379-
380-
def updateResult(self, result):
381-
assert False, 'Base class should not be used directly'
382-
383-
384-
class BufferedTestSuccess(BufferedTestBase):
385-
def updateResult(self, result):
386-
result.addSuccess(self.test)
387-
388-
389-
class BufferedTestSkip(BufferedTestBase):
390-
def __init__(self, test, reason):
391-
self.test = test
392-
self.reason = reason
393-
394-
def updateResult(self, result):
395-
result.addSkip(self.test, self.reason)
396-
397-
398-
def fixup_fake_exception(fake_exc):
399-
ex = fake_exc[2]
400-
if ex.tb_frame.f_code.positions is None:
401-
return
402-
while ex is not None:
403-
# .co_positions is supposed to be a function that returns an enumerable
404-
# to the list of code positions. Create a function object wrapper around
405-
# the data
406-
def make_wrapper(rtn):
407-
return lambda: rtn
408-
ex.tb_frame.f_code.co_positions = make_wrapper(ex.tb_frame.f_code.positions)
409-
ex = ex.tb_next
410-
411-
412-
class BufferedTestFailure(BufferedTestBase):
413-
def updateResult(self, result):
414-
fixup_fake_exception(self.error)
415-
result.addFailure(self.test, self.error)
416-
417-
418-
class BufferedTestExpectedFailure(BufferedTestBase):
419-
def updateResult(self, result):
420-
fixup_fake_exception(self.error)
421-
result.addExpectedFailure(self.test, self.error)
422-
423-
424-
class BufferedTestError(BufferedTestBase):
425-
def updateResult(self, result):
426-
fixup_fake_exception(self.error)
427-
result.addError(self.test, self.error)
428-
429-
430-
class BufferedTestUnexpectedSuccess(BufferedTestBase):
431-
def updateResult(self, result):
432-
fixup_fake_exception(self.error)
433-
result.addUnexpectedSuccess(self.test)
434-
435-
436-
class FakeTraceback:
437-
"""A fake version of a traceback object that is picklable across processes.
438-
439-
Python's traceback objects contain hidden stack information that isn't able
440-
to be pickled. Further, traceback objects aren't constructable from Python,
441-
so we need a dummy object that fulfills its interface.
442-
443-
The fields we expose are exactly those which are used by
444-
unittest.TextTestResult to show a text representation of a traceback. Any
445-
other use is not intended.
446-
"""
447-
448-
def __init__(self, tb):
449-
self.tb_frame = FakeFrame(tb.tb_frame)
450-
self.tb_lineno = tb.tb_lineno
451-
self.tb_next = FakeTraceback(tb.tb_next) if tb.tb_next is not None else None
452-
self.tb_lasti = tb.tb_lasti
453-
454-
455-
class FakeFrame:
456-
def __init__(self, f):
457-
self.f_code = FakeCode(f.f_code)
458-
# f.f_globals is not picklable, not used in stack traces, and needs to be iterable
459-
self.f_globals = []
460-
461-
462-
class FakeCode:
463-
def __init__(self, co):
464-
self.co_filename = co.co_filename
465-
self.co_name = co.co_name
466-
# co.co_positions() returns an iterator. Flatten it to a list so that it can
467-
# be pickled to the parent process
468-
if hasattr(co, 'co_positions'):
469-
self.positions = list(co.co_positions())
470-
else:
471-
self.positions = None
472-
473-
474381
def num_cores():
475382
if NUM_CORES:
476383
return int(NUM_CORES)

0 commit comments

Comments
 (0)