def _kill(self) -> None: popen = self._popen if popen is None: return if self._killed: return self._killed = True if USE_PROCESS_GROUP: what = f"{self} process group" else: what = f"{self}" print(f"Kill {what}", file=sys.stderr, flush=True) try: if USE_PROCESS_GROUP: os.killpg(popen.pid, signal.SIGKILL) else: popen.kill() except ProcessLookupError: # popen.kill(): the process completed, the TestWorkerProcess thread # read its exit status, but Popen.send_signal() read the returncode # just before Popen.wait() set returncode. pass except OSError as exc: print_warning(f"Failed to kill {what}: {exc!r}")
def _wait_completed(self) -> None: popen = self._popen # stdout and stderr must be closed to ensure that communicate() # does not hang popen.stdout.close() popen.stderr.close() try: popen.wait(JOIN_TIMEOUT) except (subprocess.TimeoutExpired, OSError) as exc: print_warning(f"Failed to wait for {self} completion " f"(timeout={format_duration(JOIN_TIMEOUT)}): " f"{exc!r}")
def _runtest_inner2(ns: Namespace, test_name: str) -> bool: # Load the test function, run the test function, handle huntrleaks # and findleaks to detect leaks abstest = get_abs_module(ns, test_name) # remove the module from sys.module to reload it if it was already imported try: del sys.modules[abstest] except KeyError: pass the_module = importlib.import_module(abstest) if ns.huntrleaks: from yp_test.libregrtest.refleak import dash_R # If the test has a test_main, that will run the appropriate # tests. If not, use normal unittest test loading. test_runner = getattr(the_module, "test_main", None) if test_runner is None: test_runner = functools.partial(_test_module, the_module) try: with save_env(ns, test_name): if ns.huntrleaks: # Return True if the test leaked references refleak = dash_R(ns, test_name, test_runner) else: test_runner() refleak = False finally: cleanup_test_droppings(test_name, ns.verbose) support.gc_collect() if gc.garbage: support.environment_altered = True print_warning(f"{test_name} created {len(gc.garbage)} " f"uncollectable object(s).") # move the uncollectable objects somewhere, # so we don't see them again FOUND_GARBAGE.extend(gc.garbage) gc.garbage.clear() support.reap_children() return refleak
def getloadavg(self): if self._popen is None: return None returncode = self._popen.poll() if returncode is not None: self.close(kill=False) return None try: lines = self._read_lines() except BrokenPipeError: self.close() return None for line in lines: line = line.rstrip() # Ignore the initial header: # "(PDH-CSV 4.0)","\\\\WIN\\System\\Processor Queue Length" if 'PDH-CSV' in line: continue # Ignore blank lines if not line: continue try: processor_queue_length = self._parse_line(line) except ValueError: print_warning("Failed to parse typeperf output: %a" % line) continue # We use an exponentially weighted moving average, imitating the # load calculation on Unix systems. # https://en.wikipedia.org/wiki/Load_(computing)#Unix-style_load_calculation # https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average if self._load is not None: self._load = (self._load * LOAD_FACTOR_1 + processor_queue_length * (1.0 - LOAD_FACTOR_1)) elif len(self._values) < NVALUE: self._values.append(processor_queue_length) else: self._load = sum(self._values) / len(self._values) return self._load
def __exit__(self, exc_type, exc_val, exc_tb): saved_values = self.saved_values self.saved_values = None # Some resources use weak references support.gc_collect() for name, get, restore, original in saved_values: current = get() # Check for changes to the resource's value if current != original: support.environment_altered = True restore(original) if not self.quiet and not self.pgo: print_warning(f"{name} was modified by {self.testname}") print(f" Before: {original}\n After: {current} ", file=sys.stderr, flush=True) return False
def _process_result(self, item: QueueOutput) -> bool: """Returns True if test runner must stop.""" if item[0]: # Thread got an exception format_exc = item[1] print_warning(f"regrtest worker thread failed: {format_exc}") return True self.test_index += 1 mp_result = item[1] self.regrtest.accumulate_result(mp_result.result) self.display_result(mp_result) if mp_result.stdout: print(mp_result.stdout, flush=True) if mp_result.stderr and not self.ns.pgo: print(mp_result.stderr, file=sys.stderr, flush=True) if must_stop(mp_result.result, self.ns): return True return False
def wait_stopped(self, start_time: float) -> None: # bpo-38207: MultiprocessTestRunner.stop_workers() called self.stop() # which killed the process. Sometimes, killing the process from the # main thread does not interrupt popen.communicate() in # TestWorkerProcess thread. This loop with a timeout is a workaround # for that. # # Moreover, if this method fails to join the thread, it is likely # that Python will hang at exit while calling threading._shutdown() # which tries again to join the blocked thread. Regrtest.main() # uses EXIT_TIMEOUT to workaround this second bug. while True: # Write a message every second self.join(1.0) if not self.is_alive(): break dt = time.monotonic() - start_time self.regrtest.log(f"Waiting for {self} thread " f"for {format_duration(dt)}") if dt > JOIN_TIMEOUT: print_warning( f"Failed to join {self} in {format_duration(dt)}") break
def cleanup_test_droppings(test_name: str, verbose: int) -> None: # First kill any dangling references to open files etc. # This can also issue some ResourceWarnings which would otherwise get # triggered during the following test run, and possibly produce failures. support.gc_collect() # Try to clean up junk commonly left behind. While tests shouldn't leave # any files or directories behind, when a test fails that can be tedious # for it to arrange. The consequences can be especially nasty on Windows, # since if a test leaves a file open, it cannot be deleted by name (while # there's nothing we can do about that here either, we can display the # name of the offending test, which is a real help). for name in (os_helper.TESTFN, ): if not os.path.exists(name): continue if os.path.isdir(name): import shutil kind, nuker = "directory", shutil.rmtree elif os.path.isfile(name): kind, nuker = "file", os.unlink else: raise RuntimeError(f"os.path says {name!r} exists but is neither " f"directory nor file") if verbose: print_warning(f"{test_name} left behind {kind} {name!r}") support.environment_altered = True try: import stat # fix possible permissions problems that might prevent cleanup os.chmod(name, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) nuker(name) except Exception as exc: print_warning(f"{test_name} left behind {kind} {name!r} " f"and it couldn't be removed: {exc}")