[3.12] gh-108822: regrtest computes statistics (#108793) (#108833) · python/cpython@c8cf6be

GitHub

@@ -11,15 +11,14 @@

1111importunittest

1212fromtest.libregrtest.cmdlineimport_parse_args

1313fromtest.libregrtest.runtestimport (

14-findtests, split_test_packages, runtest, get_abs_module, is_failed,

15-PROGRESS_MIN_TIME,

16-Passed, Failed, EnvChanged, Skipped, ResourceDenied, Interrupted,

17-ChildError, DidNotRun)

14+findtests, split_test_packages, runtest, get_abs_module,

15+PROGRESS_MIN_TIME, State)

1816fromtest.libregrtest.setupimportsetup_tests

1917fromtest.libregrtest.pgoimportsetup_pgo_tests

2018fromtest.libregrtest.utilsimport (removepy, count, format_duration,

2119printlist, get_build_info)

2220fromtestimportsupport

21+fromtest.supportimportTestStats

2322fromtest.supportimportos_helper

2423fromtest.supportimportthreading_helper

2524@@ -78,13 +77,14 @@ def __init__(self):

7877self.good= []

7978self.bad= []

8079self.skipped= []

81-self.resource_denieds= []

80+self.resource_denied= []

8281self.environment_changed= []

8382self.run_no_tests= []

8483self.need_rerun= []

8584self.rerun= []

8685self.first_result=None

8786self.interrupted=False

87+self.stats_dict: dict[str, TestStats] = {}

88888989# used by --slow

9090self.test_times= []

@@ -93,7 +93,7 @@ def __init__(self):

9393self.tracer=None

94949595# used to display the progress bar "[ 3/100]"

96-self.start_time=time.monotonic()

96+self.start_time=time.perf_counter()

9797self.test_count=''

9898self.test_count_width=1

9999@@ -111,36 +111,41 @@ def __init__(self):

111111112112defget_executed(self):

113113return (set(self.good) |set(self.bad) |set(self.skipped)

114-|set(self.resource_denieds) |set(self.environment_changed)

114+|set(self.resource_denied) |set(self.environment_changed)

115115|set(self.run_no_tests))

116116117117defaccumulate_result(self, result, rerun=False):

118-test_name=result.name

119-120-ifnotisinstance(result, (ChildError, Interrupted)) andnotrerun:

121-self.test_times.append((result.duration_sec, test_name))

122-123-ifisinstance(result, Passed):

124-self.good.append(test_name)

125-elifisinstance(result, ResourceDenied):

126-self.skipped.append(test_name)

127-self.resource_denieds.append(test_name)

128-elifisinstance(result, Skipped):

129-self.skipped.append(test_name)

130-elifisinstance(result, EnvChanged):

131-self.environment_changed.append(test_name)

132-elifisinstance(result, Failed):

133-ifnotrerun:

134-self.bad.append(test_name)

135-self.need_rerun.append(result)

136-elifisinstance(result, DidNotRun):

137-self.run_no_tests.append(test_name)

138-elifisinstance(result, Interrupted):

139-self.interrupted=True

140-else:

141-raiseValueError("invalid test result: %r"%result)

118+test_name=result.test_name

119+120+ifresult.has_meaningful_duration() andnotrerun:

121+self.test_times.append((result.duration, test_name))

142122143-ifrerunandnotisinstance(result, (Failed, Interrupted)):

123+matchresult.state:

124+caseState.PASSED:

125+self.good.append(test_name)

126+caseState.ENV_CHANGED:

127+self.environment_changed.append(test_name)

128+caseState.SKIPPED:

129+self.skipped.append(test_name)

130+caseState.RESOURCE_DENIED:

131+self.skipped.append(test_name)

132+self.resource_denied.append(test_name)

133+caseState.INTERRUPTED:

134+self.interrupted=True

135+caseState.DID_NOT_RUN:

136+self.run_no_tests.append(test_name)

137+case _:

138+ifresult.is_failed(self.ns.fail_env_changed):

139+ifnotrerun:

140+self.bad.append(test_name)

141+self.need_rerun.append(result)

142+else:

143+raiseValueError(f"invalid test state: {state!r}")

144+145+ifresult.statsisnotNone:

146+self.stats_dict[result.test_name] =result.stats

147+148+ifrerunandnot(result.is_failed(False) orresult.state==State.INTERRUPTED):

144149self.bad.remove(test_name)

145150146151xml_data=result.xml_data

@@ -162,7 +167,7 @@ def log(self, line=''):

162167line=f"load avg: {load_avg:.2f}{line}"

163168164169# add the timestamp prefix: "0:01:05 "

165-test_time=time.monotonic() -self.start_time

170+test_time=time.perf_counter() -self.start_time

166171167172mins, secs=divmod(int(test_time), 60)

168173hours, mins=divmod(mins, 60)

@@ -337,7 +342,7 @@ def rerun_failed_tests(self):

337342rerun_list=list(self.need_rerun)

338343self.need_rerun.clear()

339344forresultinrerun_list:

340-test_name=result.name

345+test_name=result.test_name

341346self.rerun.append(test_name)

342347343348errors=result.errorsor []

@@ -364,7 +369,7 @@ def rerun_failed_tests(self):

364369365370self.accumulate_result(result, rerun=True)

366371367-ifisinstance(result, Interrupted):

372+ifresult.state==State.INTERRUPTED:

368373break

369374370375ifself.bad:

@@ -461,7 +466,7 @@ def run_tests_sequential(self):

461466462467previous_test=None

463468fortest_index, test_nameinenumerate(self.tests, 1):

464-start_time=time.monotonic()

469+start_time=time.perf_counter()

465470466471text=test_name

467472ifprevious_test:

@@ -480,14 +485,14 @@ def run_tests_sequential(self):

480485result=runtest(self.ns, test_name)

481486self.accumulate_result(result)

482487483-ifisinstance(result, Interrupted):

488+ifresult.state==State.INTERRUPTED:

484489break

485490486491previous_test=str(result)

487-test_time=time.monotonic() -start_time

492+test_time=time.perf_counter() -start_time

488493iftest_time>=PROGRESS_MIN_TIME:

489494previous_test="%s in %s"% (previous_test, format_duration(test_time))

490-elifisinstance(result, Passed):

495+elifresult.state==State.PASSED:

491496# be quiet: say nothing if the test passed shortly

492497previous_test=None

493498@@ -496,7 +501,7 @@ def run_tests_sequential(self):

496501ifmodulenotinsave_modulesandmodule.startswith("test."):

497502support.unload(module)

498503499-ifself.ns.failfastandis_failed(result, self.ns):

504+ifself.ns.failfastandresult.is_failed(self.ns.fail_env_changed):

500505break

501506502507ifprevious_test:

@@ -631,13 +636,48 @@ def finalize(self):

631636coverdir=self.ns.coverdir)

632637633638print()

634-duration=time.monotonic() -self.start_time

635-print("Total duration: %s"%format_duration(duration))

636-print("Tests result: %s"%self.get_tests_result())

639+self.display_summary()

637640638641ifself.ns.runleaks:

639642os.system("leaks %d"%os.getpid())

640643644+defdisplay_summary(self):

645+duration=time.perf_counter() -self.start_time

646+647+# Total duration

648+print("Total duration: %s"%format_duration(duration))

649+650+# Total tests

651+total=TestStats()

652+forstatsinself.stats_dict.values():

653+total.accumulate(stats)

654+stats= [f'run={total.tests_run:,}']

655+iftotal.failures:

656+stats.append(f'failures={total.failures:,}')

657+iftotal.skipped:

658+stats.append(f'skipped={total.skipped:,}')

659+print(f"Total tests: {' '.join(stats)}")

660+661+# Total test files

662+report= [f'success={len(self.good)}']

663+ifself.bad:

664+report.append(f'failed={len(self.bad)}')

665+ifself.environment_changed:

666+report.append(f'env_changed={len(self.environment_changed)}')

667+ifself.skipped:

668+report.append(f'skipped={len(self.skipped)}')

669+ifself.resource_denied:

670+report.append(f'resource_denied={len(self.resource_denied)}')

671+ifself.rerun:

672+report.append(f'rerun={len(self.rerun)}')

673+ifself.run_no_tests:

674+report.append(f'run_no_tests={len(self.run_no_tests)}')

675+print(f"Total test files: {' '.join(report)}")

676+677+# Result

678+result=self.get_tests_result()

679+print(f"Result: {result}")

680+641681defsave_xml_result(self):

642682ifnotself.ns.xmlpathandnotself.testsuite_xml:

643683return