@@ -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 object 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-
474381def num_cores ():
475382 if NUM_CORES :
476383 return int (NUM_CORES )
0 commit comments