def run(dirname, envgetter): # Run all the tests to be found in directory dirname. # This is not recursive into other directories. # # return 1 if there was an error, 0 otherwise; try to # do as much as possible, though. # printed_dirname = 0 was_error = 0 dirname = os.path.abspath(dirname) try: dir_list = os.listdir(dirname) except Exception as e: print("Cannot search for tests in %s" % dirname) print(e) print("") return 1, {} dir_list.sort() # As we run each file, we will get a count of status values of each type. # t_stat keeps a running sum for the current directory. t_stat = {} for basename in dir_list: full_name = os.path.join(dirname, basename) try: file_stat = os.stat(full_name) except OSError as e: if e.errno == errno.ENOENT: # not an error for somebody to delete a file between when # we start the test and end the test. most commonly, # this is a temp file or something, not a test. continue print("Cannot stat file %s" % full_name) print(e) was_error = 1 continue except Exception as e: print("Cannot stat file %s" % full_name) print(e) was_error = 1 continue if not stat.S_ISREG(file_stat.st_mode): # we skip any thing that is not a file continue # Decide which test runner this file wants to use. If the # answer is None, this file does not contain tests. runner = pandokia.run_file.select_runner(dirname, basename) # if runner comes back None, that means that select_runner is # saying "this is not a test" if runner is None: continue # Don't print the directory name until it looks like we are # actually going to do anything here. This suppresses all # output completely for directories that do not have any tests. if not printed_dirname: print("directory %s" % dirname) printed_dirname = 1 # If the file is disabled, skip it if file_disabled(dirname, basename): print("Disabled : %s/%s" % (dirname, basename)) m = pandokia.run_file.get_runner_mod(runner) env = {} env.update(envgetter.envdir(dirname)) env['PDK_TESTPREFIX'] = pandokia.run_file.get_prefix( envgetter, dirname) env['PDK_FILE'] = basename # ask the runner what disabled tests are in that file. save_dir = os.getcwd() os.chdir(dirname) # flush the output file because m.list() might start a new process. # really, m.list() should do that itself, but I don't trust every # author of every new runner to do it. sys.stdout.flush() sys.stderr.flush() l = m.lst(env) os.chdir(save_dir) if l is not None: # l is a list of the names of tests that were in that file. # We will write a status=D record for each disabled test. write_disabled_list(env, l, dirname, basename, runner) else: pass # If the function returns None, it means that it does not # know how to report the disabled tests. That's too bad, # but we can at least go on. continue # not disabled - run it try: (err, lstat) = pandokia.run_file.run(dirname, basename, envgetter, runner) was_error |= err for x in lstat: t_stat[x] = t_stat.get(x, 0) + lstat[x] except Exception as e: xstr = traceback.format_exc() print("Exception running file %s/%s: %s" % (dirname, basename, e)) print(xstr) print('') was_error = 1 # print the status summary for the directory. print("") print("%s:" % dirname) common.print_stat_dict(t_stat) return (was_error, t_stat)
continue raise for line in f: line = line.strip() if line == 'START': continue if line.startswith('.'): continue if line == '': continue line = line.split('=') status = line[0] count = line[1] stat_summary[status] = stat_summary.get(status, 0) + int(count) f.close() # we are the only consumer for this file, so toss it (but don't # get too excited if it doesn't work) try: os.unlink(fn) except: pass print "" print "Summary of entire run:" common.print_stat_dict(stat_summary) # bug: multirun is not reporting exit status back to us, so we have no # error status to return. return (0, stat_summary)
def run(dirs, envgetter, max_procs=None): # The basic command to run tests in a directory is # pdk run --dir --environment_already_set $directory cmd = ['pdkrun', '--dir', '--environment_already_set'] # We use multirun to runs up to max_procs concurrent processes. # In each process, we run tests in one directory. We don't # know max_procs in advance, or even if it is the same through # the whole run. So, remember which slots were used. Later, # will will look for a status file from each slot. slots_used = {} # loop over the directories they gave us; recurse into each. for x in dirs: x = os.path.abspath(x) # Declare the maximum processes to the parallel runner. # Each process can run tests in one directory at a time. # This is inside the loop because we are not certain of # the value for PDK_PARALLEL until we have a directory # where we look for pdk_environment. d = envgetter.envdir(x) if max_procs is not None: pass elif 'PDK_PARALLEL' in d: max_procs = d['PDK_PARALLEL'] else: max_procs = 1 try: max_procs = int(max_procs) except ValueError: print( "cannot convert %d to integer - running one process at a time" % maxprocs) max_procs = 1 pandokia.multirun.set_max_procs(max_procs) # x is a directory; y is a loop over all nested subdirectories # that may be of interest. for y in generate_directories(x): # For max_procs=N, we have slots 0 to N-1 to # run test processes in. We wait for a process # slot to open up because we want to tell the # new process which slot it is in. (It can use that # for things like file names.) n = pandokia.multirun.await_process_slot() # remember that we used this slot at least once slots_used[n] = 1 # Get the environment to use for this directory. # We will pass this in as the process's inherited # environment. (That's why we say --environment_already_set.) d = envgetter.envdir(y) # Add the process slot number to the environment. The # test runners can use this, but it is now way too late # to define other environment variables in terms of # PDK_PROCESS_SLOT. # # run_dir uses PDK_PROCESS_SLOT to choose a pdk log file # to report results into. d['PDK_PROCESS_SLOT'] = str(n) # Start the actual process to run the tests in that directory. pandokia.multirun.start(cmd + [y], d) # multirun starts several concurrent processes, but we don't want # to say we are finished until they are all done. pandokia.multirun.wait_all() # ensure that all the slots are reporting empty by the time we # are finished. for slot in slots_used: pandokia.run_status.pdkrun_status('', slot) # collect the summary of how many tests had each status stat_summary = {} for x in slots_used: fn = "%s.%s.summary" % (os.environ['PDK_LOG'], str(x)) try: f = open(fn, "r") except IOError as e: # It is possible for a process slot to run a process without # creating a log file. (e.g. when there is a directory that # does not contain any tests.) So, if there is no file, that # is not an errr. if e.errno == errno.ENOENT: continue raise for line in f: line = line.strip() if line == 'START': continue if line.startswith('.'): continue if line == '': continue line = line.split('=') status = line[0] count = line[1] stat_summary[status] = stat_summary.get(status, 0) + int(count) f.close() # we are the only consumer for this file, so toss it (but don't # get too excited if it doesn't work) try: os.unlink(fn) except: pass print("") print("Summary of entire run:") common.print_stat_dict(stat_summary) # bug: multirun is not reporting exit status back to us, so we have no # error status to return. return (0, stat_summary)
def run(dirname, envgetter): # Run all the tests to be found in directory dirname. # This is not recursive into other directories. # # return 1 if there was an error, 0 otherwise; try to # do as much as possible, though. # printed_dirname = 0 was_error = 0 dirname = os.path.abspath(dirname) try: dir_list = os.listdir(dirname) except Exception as e: print("Cannot search for tests in %s" % dirname) print(e) print("") return 1, {} dir_list.sort() # As we run each file, we will get a count of status values of each type. # t_stat keeps a running sum for the current directory. t_stat = {} for basename in dir_list: full_name = os.path.join(dirname, basename) try: file_stat = os.stat(full_name) except OSError as e: if e.errno == errno.ENOENT: # not an error for somebody to delete a file between when # we start the test and end the test. most commonly, # this is a temp file or something, not a test. continue print("Cannot stat file %s" % full_name) print(e) was_error = 1 continue except Exception as e: print("Cannot stat file %s" % full_name) print(e) was_error = 1 continue if not stat.S_ISREG(file_stat.st_mode): # we skip any thing that is not a file continue # Decide which test runner this file wants to use. If the # answer is None, this file does not contain tests. runner = pandokia.run_file.select_runner(dirname, basename) # if runner comes back None, that means that select_runner is # saying "this is not a test" if runner is None: continue # Don't print the directory name until it looks like we are # actually going to do anything here. This suppresses all # output completely for directories that do not have any tests. if not printed_dirname: print("directory %s" % dirname) printed_dirname = 1 # If the file is disabled, skip it if file_disabled(dirname, basename): print("Disabled : %s/%s" % (dirname, basename)) m = pandokia.run_file.get_runner_mod(runner) env = {} env.update(envgetter.envdir(dirname)) env['PDK_TESTPREFIX'] = pandokia.run_file.get_prefix( envgetter, dirname) env['PDK_FILE'] = basename # ask the runner what disabled tests are in that file. save_dir = os.getcwd() os.chdir(dirname) # flush the output file because m.list() might start a new process. # really, m.list() should do that itself, but I don't trust every # author of every new runner to do it. sys.stdout.flush() sys.stderr.flush() l = m.lst(env) os.chdir(save_dir) if l is not None: # l is a list of the names of tests that were in that file. # We will write a status=D record for each disabled test. write_disabled_list(env, l, dirname, basename, runner) else: pass # If the function returns None, it means that it does not # know how to report the disabled tests. That's too bad, # but we can at least go on. continue # not disabled - run it try: (err, lstat) = pandokia.run_file.run( dirname, basename, envgetter, runner) was_error |= err for x in lstat: t_stat[x] = t_stat.get(x, 0) + lstat[x] except Exception as e: xstr = traceback.format_exc() print("Exception running file %s/%s: %s" % (dirname, basename, e)) print(xstr) print('') was_error = 1 # print the status summary for the directory. print("") print("%s:" % dirname) common.print_stat_dict(t_stat) return (was_error, t_stat)
def run(dirname, basename, envgetter, runner): cause = "unknown" return_status = 0 dirname = os.path.abspath(dirname) save_dir = os.getcwd() os.chdir(dirname) if runner is None: runner = select_runner(dirname, basename) if runner is not None: # copy of the environment because we will mess with it env = dict(envgetter.envdir(dirname)) env['PDK_TESTPREFIX'] = get_prefix(envgetter, dirname) env['PDK_TOP'] = envgetter.gettop() runner_mod = get_runner_mod(runner) env['PDK_FILE'] = basename env['PDK_LOG'] = pdk_log_name(env) stat_summary = {} for x in pandokia.cfg.statuses: stat_summary[x] = 0 # We will write a count of statuses to a summary file. But where? # If no PDK_PROCESS_SLOT, we do not need a summary -- we just return # it to our caller. Otherwise, we use a file based on the log file # name. if 'PDK_PROCESS_SLOT' in env: summary_file = env['PDK_LOG'] + '.summary' slot_id = env['PDK_PROCESS_SLOT'] else: summary_file = None slot_id = '0' f = open(env['PDK_LOG'], 'a+') # 2 == os.SEEK_END, but not in python 2.4 f.seek(0, 2) end_of_log = f.tell() f.close() env['PDK_DIRECTORY'] = dirname full_filename = dirname + "/" + env['PDK_FILE'] f = open(env['PDK_LOG'], "a") f.write("\n\nSTART\n") f.write('test_run=%s\n' % env['PDK_TESTRUN']) f.write('project=%s\n' % env['PDK_PROJECT']) f.write('host=%s\n' % env['PDK_HOST']) f.write('location=%s\n' % full_filename) f.write('test_runner=%s\n' % runner) f.write('context=%s\n' % env['PDK_CONTEXT']) f.write("SETDEFAULT\n") f.close() # fetch the command that executes the tests cmd = runner_mod.command(env) output_buffer = '' if cmd is not None: # run the command -- To understand how we do it, see # "Replacing os.system()" in the docs for the subprocess module, # then consider the source code for subprocess.call() if not isinstance(cmd, list): cmd = [cmd] for thiscmd in cmd: print('COMMAND : %s (for file %s) %s' % (repr(thiscmd), full_filename, datetime.datetime.now())) sys.stdout.flush() sys.stderr.flush() if windows: # on Windows, we dare not let the child process have access to the stdout/stderr # that we are using. Apparently, the child process closes stderr and it somehow # ends up closed for us too. So, stuff it into a temp file and then copy the # temp file to our stdout/stderr. f = open("stdout.%s.tmp" % slot_id, "w") p = subprocess.Popen( thiscmd, stdout=f, stderr=f, shell=True, env=env, creationflags=subprocess.CREATE_NEW_PROCESS_GROUP) # bug: implement timeouts status = p.wait() f.close() f = open("stdout.%s.tmp" % slot_id, "r") output_buffer = f.read() sys.stdout.write(output_buffer) f.close() os.unlink("stdout.%s.tmp" % slot_id) else: # on unix, just do it with NamedTemporaryFile(mode='r+b') as f: p = subprocess.Popen(thiscmd.split(), stdout=f, stderr=f, shell=False, env=env, preexec_fn=unix_preexec) if 'PDK_TIMEOUT' in env: proc_timeout_start(env['PDK_TIMEOUT'], p) status = p.wait() print("return from wait, status=%d" % status) proc_timeout_terminate() if timeout_proc_kills > 0: # we tried to kill it for taking too long - # report an error even if it managed to exit 0 status = -15 else: status = p.wait() f.seek(0) output_buffer = f.read().decode() sys.stdout.write(output_buffer) # subprocess gives you weird status values if status >= 0: cause = "exit" return_status = status elif status < 0: cause = "signal" return_status = -status print("COMMAND EXIT: %s %s %s" % (cause, return_status, datetime.datetime.now())) else: # BUG: no timeout! - fortunately, this is a minor issue # because we don't currently have anything that uses # run_internally() except to cough out errors about # unsupported runners on Windows # There is no command, so we run it by calling a function. # This runs the test in the same python interpreter that # this file is executing in, which is normally not # preferred because a problem in the test runner, or even # in the test, could potentially kill the pandokia meta-runner. print("RUNNING INTERNALLY (for file %s)" % full_filename) runner_mod.run_internally(env) print("DONE RUNNING INTERNALLY") # # A test runner that only works within pandokia can assume # that we made all of these log entries for it. pdkrun_status(full_filename) # Trap unhandled exit information if return_status > 1 or return_status < 0: f = open(env['PDK_LOG'], "a") f.write('\n') f.write('test_name=%s\n' % full_filename) f.write('status=E\n') if output_buffer: f.write('log:\n') f.write('.[PANDOKIA]\n') f.write('.FATAL: NON-STANDARD EXIT VALUE DETECTED! ' '({})\n'.format(return_status)) f.write('.\n') f.write('.[STDOUT/STDERR STREAM]\n') for line in output_buffer.splitlines(): f.write('.{}\n'.format(line)) f.write('\n') f.write('END\n') f.close() if 1: # if the runner did not provide a status summary, collect it from # the log file. # [ This is "if 1" because no runners currently know how to do it. # Later, some of them will leave behind a status summary file; we # will read that instead. ] f = open(env['PDK_LOG'], 'r') # 0 == os.SEEK_SET, but not in python 2.4 f.seek(end_of_log, 0) while True: l = f.readline() if l == '': break l = l.strip() if l.startswith('status='): l = l[7:].strip() stat_summary[l] = stat_summary.get(l, 0) + 1 f.close() common.print_stat_dict(stat_summary) print("") if summary_file: # The summary file has a similar format to the log, but there is # a "START" line after each record. If somebody accidentally # tries to import it, the importer can never find any data. f = open(summary_file, 'a') for x in stat_summary: f.write("%s=%s\n" % (x, stat_summary[x])) # ".file" can never look like a valid status f.write(".file=%s\n" % full_filename) f.write("START\n\n") f.close() else: print("NO RUNNER FOR %s\n" % (dirname + "/" + basename)) os.chdir(save_dir) pdkrun_status('') return (return_status, stat_summary)
try : ( err, lstat ) = pandokia.run_file.run(dirname, basename, envgetter, runner ) was_error |= err for x in lstat : t_stat[x] = t_stat.get(x,0) + lstat[x] except Exception, e: xstr=traceback.format_exc() print "Exception running file %s/%s: %s"%(dirname, basename, e) print xstr print '' was_error = 1 # print the status summary for the directory. print "" print "%s:"%dirname common.print_stat_dict(t_stat) return ( was_error, t_stat ) # # check for a disable file, indicating that we should not run tests in a file # def file_disabled(dirname, basename) : # dirname = engine/spider/stis/spec/stellar-ext # basename = s_stis_spec_stellar-ext_10759.peng n=basename.rfind(".") if n >= 0 : disable_name = basename[:n] else :
def run(args): import pandokia.envgetter recursive = False environment_already_set = False directory = False log = os.environ.get("PDK_LOG", None) project = os.environ.get("PDK_PROJECT", None) test_run = os.environ.get("PDK_TESTRUN", None) test_prefix = os.environ.get("PDK_TESTPREFIX", None) context = os.environ.get("PDK_CONTEXT", 'default') custom = os.environ.get("PDK_CUSTOM", "") parallel = os.environ.get("PDK_PARALLEL", None) tmpdir = os.environ.get("PDK_TMP", None) host = os.environ.get("PDK_HOST", None) verbose = 0 # not implemented dry_run = 0 # not implemented if args == []: args = ['-r', '.'] opts, args = getopt.gnu_getopt(args, "rvpsw", [ "recursive", "environment_already_set", "dir", "log=", "project=", "test_run=", "test_prefix=", "show-command", "verbose", "parallel=", "help", "context=", ]) for (opt, optarg) in opts: if opt == '-r' or opt == '--recursive': recursive = True elif opt == '--environment_already_set': environment_already_set = True elif opt == '--dir': # we don't do anything with --dir, but it is there to see # when you run ps pass elif opt == '--help': print(helpstr) return (0, {}) elif opt == '--log': log = optarg elif opt == '--test_run': test_run = optarg elif opt == '--test_prefix': test_prefix = optarg elif opt == '--project': project = optarg elif opt == '--context': context = optarg elif opt == '--verbose' or opt == '-v': verbose = 1 elif opt == '--dry-run': dry_run = 1 elif opt == '-' or opt == '--parallel': parallel = str(int(optarg)) elif opt == '--host': host = optarg elif opt == '--custom': custom = optarg if project is None: project = default_project() if test_run is None: test_run = default_test_run() if log is None: log = "PDK_DEFAULT.LOG." + test_run if host is None: host = common.gethostname() if parallel is not None: os.environ['PDK_PARALLEL'] = parallel log = os.path.abspath(log) os.environ['PDK_LOG'] = log os.environ['PDK_PROJECT'] = project os.environ['PDK_TESTRUN'] = test_run os.environ['PDK_CONTEXT'] = context os.environ['PDK_CUSTOM'] = custom os.environ['PDK_HOST'] = host initialized_status_file = 0 if 'PDK_STATUSFILE' not in os.environ: status_file_name = os.getcwd() + '/pdk_statusfile' import pandokia.run_status if parallel is None: n_status_records = 1 else: n_status_records = int(parallel) f = pandokia.run_status.init_status(filename=status_file_name, n_records=n_status_records) if f: os.environ['PDK_STATUSFILE'] = os.getcwd() + '/pdk_statusfile' initialized_status_file = 1 if test_prefix is not None: os.environ['PDK_TESTPREFIX'] = test_prefix if tmpdir is None: tmpdir = os.getcwd() os.environ['PDK_TMP'] = tmpdir envgetter = pandokia.envgetter.EnvGetter(context=context) # environment_already_set=environment_already_set ) bug: we need to # get this optimization in some how if recursive: import pandokia.run_recursive (was_error, t_stat) = pandokia.run_recursive.run(args, envgetter) else: # t_stat is a count of status values of each type. The counts were printed at the end # of each file/dir that we ran, but if there are more than one file/dir, we will print # the total in t_stat. t_stat = {} was_error = 0 n_things_run = 0 for x in args: try: file_stat = os.stat(x) except OSError as e: print("%s %s" % (x, e)) continue lstat = {} if stat.S_ISDIR(file_stat.st_mode): import pandokia.run_dir n_things_run += 1 (err, lstat) = pandokia.run_dir.run(x, envgetter) elif stat.S_ISREG(file_stat.st_mode): import pandokia.run_file n_things_run += 1 basename = os.path.basename(x) dirname = os.path.dirname(x) if dirname == '': dirname = '.' runner = pandokia.run_file.select_runner(dirname, basename) if runner is not None: (err, lstat) = pandokia.run_file.run(dirname, basename, envgetter, runner) else: print("no runner for %s" % x) err = 1 else: lstat = {} err = 0 was_error |= err for y in lstat: t_stat[y] = t_stat.get(y, 0) + lstat[y] if n_things_run > 1: print("") print("Summary:") common.print_stat_dict(t_stat) if initialized_status_file: for x in range(0, n_status_records): pandokia.run_status.pdkrun_status('', slot=x) os.unlink(os.environ['PDK_STATUSFILE']) return (was_error, t_stat)
def run(args): import pandokia.envgetter recursive = False environment_already_set = False directory = False log = os.environ.get("PDK_LOG", None) project = os.environ.get("PDK_PROJECT", None) test_run = os.environ.get("PDK_TESTRUN", None) test_prefix = os.environ.get("PDK_TESTPREFIX", None) context = os.environ.get("PDK_CONTEXT", 'default') custom = os.environ.get("PDK_CUSTOM", None) parallel = os.environ.get("PDK_PARALLEL", None) tmpdir = os.environ.get("PDK_TMP", None) host = os.environ.get("PDK_HOST", None) verbose = 0 # not implemented dry_run = 0 # not implemented if args == []: args = ['-r', '.'] opts, args = getopt.gnu_getopt(args, "rvpsw", ["recursive", "environment_already_set", "dir", "log=", "project=", "test_run=", "test_prefix=", "show-command", "verbose", "parallel=", "help", "context=", ]) for (opt, optarg) in opts: if opt == '-r' or opt == '--recursive': recursive = True elif opt == '--environment_already_set': environment_already_set = True elif opt == '--dir': # we don't do anything with --dir, but it is there to see # when you run ps pass elif opt == '--help': print(helpstr) return (0, {}) elif opt == '--log': log = optarg elif opt == '--test_run': test_run = optarg elif opt == '--test_prefix': test_prefix = optarg elif opt == '--project': project = optarg elif opt == '--context': context = optarg elif opt == '--verbose' or opt == '-v': verbose = 1 elif opt == '--dry-run': dry_run = 1 elif opt == '-' or opt == '--parallel': parallel = str(int(optarg)) elif opt == '--host': host = optarg elif opt == '--custom': custom = optarg if project is None: project = default_project() if test_run is None: test_run = default_test_run() if log is None: log = "PDK_DEFAULT.LOG." + test_run if host is None: host = common.gethostname() if parallel is not None: os.environ['PDK_PARALLEL'] = parallel log = os.path.abspath(log) os.environ['PDK_LOG'] = log os.environ['PDK_PROJECT'] = project os.environ['PDK_TESTRUN'] = test_run os.environ['PDK_CONTEXT'] = context os.environ['PDK_CUSTOM'] = custom os.environ['PDK_HOST'] = host initialized_status_file = 0 if 'PDK_STATUSFILE' not in os.environ: status_file_name = os.getcwd() + '/pdk_statusfile' import pandokia.run_status if parallel is None: n_status_records = 1 else: n_status_records = int(parallel) f = pandokia.run_status.init_status( filename=status_file_name, n_records=n_status_records) if f: os.environ['PDK_STATUSFILE'] = os.getcwd() + '/pdk_statusfile' initialized_status_file = 1 if test_prefix is not None: os.environ['PDK_TESTPREFIX'] = test_prefix if tmpdir is None: tmpdir = os.getcwd() os.environ['PDK_TMP'] = tmpdir envgetter = pandokia.envgetter.EnvGetter(context=context) # environment_already_set=environment_already_set ) bug: we need to # get this optimization in some how if recursive: import pandokia.run_recursive (was_error, t_stat) = pandokia.run_recursive.run(args, envgetter) else: # t_stat is a count of status values of each type. The counts were printed at the end # of each file/dir that we ran, but if there are more than one file/dir, we will print # the total in t_stat. t_stat = {} was_error = 0 n_things_run = 0 for x in args: try: file_stat = os.stat(x) except OSError as e: print("%s %s" % (x, e)) continue lstat = {} if stat.S_ISDIR(file_stat.st_mode): import pandokia.run_dir n_things_run += 1 (err, lstat) = pandokia.run_dir.run(x, envgetter) elif stat.S_ISREG(file_stat.st_mode): import pandokia.run_file n_things_run += 1 basename = os.path.basename(x) dirname = os.path.dirname(x) if dirname == '': dirname = '.' runner = pandokia.run_file.select_runner(dirname, basename) if runner is not None: (err, lstat) = pandokia.run_file.run( dirname, basename, envgetter, runner) else: print("no runner for %s" % x) err = 1 else: lstat = {} err = 0 was_error |= err for y in lstat: t_stat[y] = t_stat.get(y, 0) + lstat[y] if n_things_run > 1: print("") print("Summary:") common.print_stat_dict(t_stat) if initialized_status_file: for x in range(0, n_status_records): pandokia.run_status.pdkrun_status('', slot=x) os.unlink(os.environ['PDK_STATUSFILE']) return (was_error, t_stat)
def run(dirname, basename, envgetter, runner): cause = "unknown" return_status = 0 dirname = os.path.abspath(dirname) save_dir = os.getcwd() os.chdir(dirname) if runner is None: runner = select_runner(dirname, basename) if runner is not None: # copy of the environment because we will mess with it env = dict(envgetter.envdir(dirname)) env['PDK_TESTPREFIX'] = get_prefix(envgetter, dirname) env['PDK_TOP'] = envgetter.gettop() runner_mod = get_runner_mod(runner) env['PDK_FILE'] = basename env['PDK_LOG'] = pdk_log_name(env) stat_summary = {} for x in pandokia.cfg.statuses: stat_summary[x] = 0 # We will write a count of statuses to a summary file. But where? # If no PDK_PROCESS_SLOT, we do not need a summary -- we just return # it to our caller. Otherwise, we use a file based on the log file # name. if 'PDK_PROCESS_SLOT' in env: summary_file = env['PDK_LOG'] + '.summary' slot_id = env['PDK_PROCESS_SLOT'] else: summary_file = None slot_id = '0' f = open(env['PDK_LOG'], 'a+') # 2 == os.SEEK_END, but not in python 2.4 f.seek(0, 2) end_of_log = f.tell() f.close() env['PDK_DIRECTORY'] = dirname full_filename = dirname + "/" + env['PDK_FILE'] f = open(env['PDK_LOG'], "a") f.write("\n\nSTART\n") f.write('test_run=%s\n' % env['PDK_TESTRUN']) f.write('project=%s\n' % env['PDK_PROJECT']) f.write('host=%s\n' % env['PDK_HOST']) f.write('location=%s\n' % full_filename) f.write('test_runner=%s\n' % runner) f.write('context=%s\n' % env['PDK_CONTEXT']) f.write('custom=%s\n' % env['PDK_CUSTOM']) f.write("SETDEFAULT\n") f.close() # fetch the command that executes the tests cmd = runner_mod.command(env) output_buffer = '' if cmd is not None: # run the command -- To understand how we do it, see # "Replacing os.system()" in the docs for the subprocess module, # then consider the source code for subprocess.call() if not isinstance(cmd, list): cmd = [cmd] for thiscmd in cmd: print('COMMAND : %s (for file %s) %s' % (repr(thiscmd), full_filename, datetime.datetime.now())) sys.stdout.flush() sys.stderr.flush() if windows: # on Windows, we dare not let the child process have access to the stdout/stderr # that we are using. Apparently, the child process closes stderr and it somehow # ends up closed for us too. So, stuff it into a temp file and then copy the # temp file to our stdout/stderr. f = open("stdout.%s.tmp" % slot_id, "w") p = subprocess.Popen( thiscmd, stdout=f, stderr=f, shell=True, env=env, creationflags=subprocess.CREATE_NEW_PROCESS_GROUP) # bug: implement timeouts status = p.wait() f.close() f = open("stdout.%s.tmp" % slot_id, "r") output_buffer = f.read() sys.stdout.write(output_buffer) f.close() os.unlink("stdout.%s.tmp" % slot_id) else: # on unix, just do it with NamedTemporaryFile(mode='r+b') as f: p = subprocess.Popen( thiscmd.split(), stdout=f, stderr=f, shell=False, env=env, preexec_fn=unix_preexec) if 'PDK_TIMEOUT' in env: proc_timeout_start(env['PDK_TIMEOUT'], p) status = p.wait() print("return from wait, status=%d" % status) proc_timeout_terminate() if timeout_proc_kills > 0: # we tried to kill it for taking too long - # report an error even if it managed to exit 0 status = -15 else: status = p.wait() f.seek(0) output_buffer = f.read().decode() sys.stdout.write(output_buffer) # subprocess gives you weird status values if status >= 0: cause="exit" return_status = status elif status < 0: cause = "signal" return_status = -status print("COMMAND EXIT: %s %s %s" % (cause, return_status, datetime.datetime.now())) else: # BUG: no timeout! - fortunately, this is a minor issue # because we don't currently have anything that uses # run_internally() except to cough out errors about # unsupported runners on Windows # There is no command, so we run it by calling a function. # This runs the test in the same python interpreter that # this file is executing in, which is normally not # preferred because a problem in the test runner, or even # in the test, could potentially kill the pandokia meta-runner. print("RUNNING INTERNALLY (for file %s)" % full_filename) runner_mod.run_internally(env) print("DONE RUNNING INTERNALLY") # # A test runner that only works within pandokia can assume # that we made all of these log entries for it. pdkrun_status(full_filename) # Trap unhandled exit information if return_status > 1 or return_status < 0: f = open(env['PDK_LOG'], "a") f.write('\n') f.write('test_name=%s\n' % full_filename) f.write('status=E\n') if output_buffer: f.write('log:\n') f.write('.[PANDOKIA]\n') f.write('.FATAL: NON-STANDARD EXIT VALUE DETECTED! ' '({})\n'.format(return_status)) f.write('.\n') f.write('.[STDOUT/STDERR STREAM]\n') for line in output_buffer.splitlines(): f.write('.{}\n'.format(line)) f.write('\n') f.write('END\n') f.close() if 1: # if the runner did not provide a status summary, collect it from # the log file. # [ This is "if 1" because no runners currently know how to do it. # Later, some of them will leave behind a status summary file; we # will read that instead. ] f = open(env['PDK_LOG'], 'r') # 0 == os.SEEK_SET, but not in python 2.4 f.seek(end_of_log, 0) while True: l = f.readline() if l == '': break l = l.strip() if l.startswith('status='): l = l[7:].strip() stat_summary[l] = stat_summary.get(l, 0) + 1 f.close() common.print_stat_dict(stat_summary) print("") if summary_file: # The summary file has a similar format to the log, but there is # a "START" line after each record. If somebody accidentally # tries to import it, the importer can never find any data. f = open(summary_file, 'a') for x in stat_summary: f.write("%s=%s\n" % (x, stat_summary[x])) # ".file" can never look like a valid status f.write(".file=%s\n" % full_filename) f.write("START\n\n") f.close() else: print("NO RUNNER FOR %s\n" % (dirname + "/" + basename)) os.chdir(save_dir) pdkrun_status('') return (return_status, stat_summary)