def _run_cmd(self, cmd): starttime = time.time() if len(self.test.extra_paths) > 0: self.env['PATH'] = os.pathsep.join(self.test.extra_paths + ['']) + self.env['PATH'] if substring_is_in_list('wine', cmd): wine_paths = ['Z:' + p for p in self.test.extra_paths] wine_path = ';'.join(wine_paths) # Don't accidentally end with an `;` because that will add the # current directory and might cause unexpected behaviour if 'WINEPATH' in self.env: self.env[ 'WINEPATH'] = wine_path + ';' + self.env['WINEPATH'] else: self.env['WINEPATH'] = wine_path # If MALLOC_PERTURB_ is not set, or if it is set to an empty value, # (i.e., the test or the environment don't explicitly set it), set # it ourselves. We do this unconditionally for regular tests # because it is extremely useful to have. # Setting MALLOC_PERTURB_="0" will completely disable this feature. if ('MALLOC_PERTURB_' not in self.env or not self.env['MALLOC_PERTURB_'] ) and not self.options.benchmark: self.env['MALLOC_PERTURB_'] = str(random.randint(1, 255)) stdout = None stderr = None if not self.options.verbose: stdout = tempfile.TemporaryFile("wb+") stderr = tempfile.TemporaryFile( "wb+") if self.options and self.options.split else stdout # Let gdb handle ^C instead of us if self.options.gdb: previous_sigint_handler = signal.getsignal(signal.SIGINT) # Make the meson executable ignore SIGINT while gdb is running. signal.signal(signal.SIGINT, signal.SIG_IGN) def preexec_fn(): if self.options.gdb: # Restore the SIGINT handler for the child process to # ensure it can handle it. signal.signal(signal.SIGINT, signal.SIG_DFL) else: # We don't want setsid() in gdb because gdb needs the # terminal in order to handle ^C and not show tcsetpgrp() # errors avoid not being able to use the terminal. os.setsid() p = subprocess.Popen( cmd, stdout=stdout, stderr=stderr, env=self.env, cwd=self.test.workdir, preexec_fn=preexec_fn if not is_windows() else None) timed_out = False kill_test = False if self.test.timeout is None: timeout = None elif self.options.timeout_multiplier is not None: timeout = self.test.timeout * self.options.timeout_multiplier else: timeout = self.test.timeout try: p.communicate(timeout=timeout) except subprocess.TimeoutExpired: if self.options.verbose: print('%s time out (After %d seconds)' % (self.test.name, timeout)) timed_out = True except KeyboardInterrupt: mlog.warning('CTRL-C detected while running %s' % (self.test.name)) kill_test = True finally: if self.options.gdb: # Let us accept ^C again signal.signal(signal.SIGINT, previous_sigint_handler) additional_error = None if kill_test or timed_out: # Python does not provide multiplatform support for # killing a process and all its children so we need # to roll our own. if is_windows(): subprocess.call(['taskkill', '/F', '/T', '/PID', str(p.pid)]) else: try: # Kill the process group that setsid() created. os.killpg(p.pid, signal.SIGKILL) except ProcessLookupError: # Sometimes (e.g. with Wine) this happens. # There's nothing we can do (maybe the process # already died) so carry on. pass try: p.communicate(timeout=1) except subprocess.TimeoutExpired: # An earlier kill attempt has not worked for whatever reason. # Try to kill it one last time with a direct call. # If the process has spawned children, they will remain around. p.kill() try: p.communicate(timeout=1) except subprocess.TimeoutExpired: additional_error = b'Test process could not be killed.' except ValueError: additional_error = b'Could not read output. Maybe the process has redirected its stdout/stderr?' endtime = time.time() duration = endtime - starttime if additional_error is None: if stdout is None: # if stdout is None stderr should be as well stdo = '' stde = '' else: stdout.seek(0) stdo = decode(stdout.read()) if stderr != stdout: stderr.seek(0) stde = decode(stderr.read()) else: stde = "" else: stdo = "" stde = additional_error if timed_out: res = TestResult.TIMEOUT elif p.returncode == GNU_SKIP_RETURNCODE: res = TestResult.SKIP elif self.test.should_fail: res = TestResult.EXPECTEDFAIL if bool( p.returncode) else TestResult.UNEXPECTEDPASS else: res = TestResult.FAIL if bool(p.returncode) else TestResult.OK return TestRun(res, p.returncode, self.test.should_fail, duration, stdo, stde, cmd, self.test.env)
def _run_cmd(self, cmd): starttime = time.time() if len(self.test.extra_paths) > 0: self.env['PATH'] = os.pathsep.join(self.test.extra_paths + ['']) + self.env['PATH'] if substring_is_in_list('wine', cmd): wine_paths = ['Z:' + p for p in self.test.extra_paths] wine_path = ';'.join(wine_paths) # Don't accidentally end with an `;` because that will add the # current directory and might cause unexpected behaviour if 'WINEPATH' in self.env: self.env['WINEPATH'] = wine_path + ';' + self.env['WINEPATH'] else: self.env['WINEPATH'] = wine_path # If MALLOC_PERTURB_ is not set, or if it is set to an empty value, # (i.e., the test or the environment don't explicitly set it), set # it ourselves. We do this unconditionally for regular tests # because it is extremely useful to have. # Setting MALLOC_PERTURB_="0" will completely disable this feature. if ('MALLOC_PERTURB_' not in self.env or not self.env['MALLOC_PERTURB_']) and not self.options.benchmark: self.env['MALLOC_PERTURB_'] = str(random.randint(1, 255)) stdout = None stderr = None if not self.options.verbose: stdout = subprocess.PIPE stderr = subprocess.PIPE if self.options and self.options.split else subprocess.STDOUT # Let gdb handle ^C instead of us if self.options.gdb: previous_sigint_handler = signal.getsignal(signal.SIGINT) # Make the meson executable ignore SIGINT while gdb is running. signal.signal(signal.SIGINT, signal.SIG_IGN) def preexec_fn(): if self.options.gdb: # Restore the SIGINT handler for the child process to # ensure it can handle it. signal.signal(signal.SIGINT, signal.SIG_DFL) else: # We don't want setsid() in gdb because gdb needs the # terminal in order to handle ^C and not show tcsetpgrp() # errors avoid not being able to use the terminal. os.setsid() p = subprocess.Popen(cmd, stdout=stdout, stderr=stderr, env=self.env, cwd=self.test.workdir, preexec_fn=preexec_fn if not is_windows() else None) timed_out = False kill_test = False if self.test.timeout is None: timeout = None elif self.options.timeout_multiplier is not None: timeout = self.test.timeout * self.options.timeout_multiplier else: timeout = self.test.timeout try: (stdo, stde) = p.communicate(timeout=timeout) except subprocess.TimeoutExpired: if self.options.verbose: print('%s time out (After %d seconds)' % (self.test.name, timeout)) timed_out = True except KeyboardInterrupt: mlog.warning('CTRL-C detected while running %s' % (self.test.name)) kill_test = True finally: if self.options.gdb: # Let us accept ^C again signal.signal(signal.SIGINT, previous_sigint_handler) if kill_test or timed_out: # Python does not provide multiplatform support for # killing a process and all its children so we need # to roll our own. if is_windows(): subprocess.call(['taskkill', '/F', '/T', '/PID', str(p.pid)]) else: try: # Kill the process group that setsid() created. os.killpg(p.pid, signal.SIGKILL) except ProcessLookupError: # Sometimes (e.g. with Wine) this happens. # There's nothing we can do (maybe the process # already died) so carry on. pass (stdo, stde) = p.communicate() endtime = time.time() duration = endtime - starttime stdo = decode(stdo) if stde: stde = decode(stde) if timed_out: res = TestResult.TIMEOUT elif p.returncode == GNU_SKIP_RETURNCODE: res = TestResult.SKIP elif self.test.should_fail == bool(p.returncode): res = TestResult.OK else: res = TestResult.FAIL return TestRun(res, p.returncode, self.test.should_fail, duration, stdo, stde, cmd, self.test.env)