Skip to content

Commit 23fba40

Browse files
committed
Added option for updating a CSV file with test results
This is mostly for the bench runner which will contain more interesting results besides just pass/fail.
1 parent 03c1a4e commit 23fba40

File tree

3 files changed

+100
-37
lines changed

3 files changed

+100
-37
lines changed

scripts/tailpipe.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ def read():
4141
event.set()
4242
if not keep_open:
4343
break
44+
# don't just flood open calls
45+
time.sleep(sleep)
4446
done = True
4547

4648
th.Thread(target=read, daemon=True).start()

scripts/test.py

Lines changed: 94 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#
55

66
import collections as co
7+
import csv
78
import errno
89
import glob
910
import itertools as it
@@ -26,7 +27,7 @@
2627

2728
def openio(path, mode='r', buffering=-1, nb=False):
2829
if path == '-':
29-
if 'r' in mode:
30+
if mode == 'r':
3031
return os.fdopen(os.dup(sys.stdin.fileno()), 'r', buffering)
3132
else:
3233
return os.fdopen(os.dup(sys.stdout.fileno()), 'w', buffering)
@@ -475,9 +476,8 @@ def write_case_functions(f, suite, case):
475476
f.writeln('#endif')
476477
f.writeln()
477478

478-
def find_runner(runner, test_ids, **args):
479+
def find_runner(runner, **args):
479480
cmd = runner.copy()
480-
cmd.extend(test_ids)
481481

482482
# run under some external command?
483483
cmd[:0] = args.get('exec', [])
@@ -514,8 +514,8 @@ def find_runner(runner, test_ids, **args):
514514

515515
return cmd
516516

517-
def list_(runner, test_ids, **args):
518-
cmd = find_runner(runner, test_ids, **args)
517+
def list_(runner, test_ids=[], **args):
518+
cmd = find_runner(runner, **args) + test_ids
519519
if args.get('summary'): cmd.append('--summary')
520520
if args.get('list_suites'): cmd.append('--list-suites')
521521
if args.get('list_cases'): cmd.append('--list-cases')
@@ -534,9 +534,9 @@ def list_(runner, test_ids, **args):
534534
return sp.call(cmd)
535535

536536

537-
def find_cases(runner_, **args):
537+
def find_cases(runner_, ids=[], **args):
538538
# query from runner
539-
cmd = runner_ + ['--list-cases']
539+
cmd = runner_ + ['--list-cases'] + ids
540540
if args.get('verbose'):
541541
print(' '.join(shlex.quote(c) for c in cmd))
542542
proc = sp.Popen(cmd,
@@ -635,17 +635,52 @@ def find_defines(runner_, id, **args):
635635
return defines
636636

637637

638+
# Thread-safe CSV writer
639+
class TestOutput:
640+
def __init__(self, path, head=None, tail=None):
641+
self.f = openio(path, 'w+', 1)
642+
self.lock = th.Lock()
643+
self.head = head or []
644+
self.tail = tail or []
645+
self.writer = csv.DictWriter(self.f, self.head + self.tail)
646+
self.rows = []
647+
648+
def close(self):
649+
self.f.close()
650+
651+
def __enter__(self):
652+
return self
653+
654+
def __exit__(self, *_):
655+
self.f.close()
656+
657+
def writerow(self, row):
658+
with self.lock:
659+
self.rows.append(row)
660+
if all(k in self.head or k in self.tail for k in row.keys()):
661+
# can simply append
662+
self.writer.writerow(row)
663+
else:
664+
# need to rewrite the file
665+
self.head.extend(row.keys() - (self.head + self.tail))
666+
self.f.truncate()
667+
self.writer = csv.DictWriter(self.f, self.head + self.tail)
668+
self.writer.writeheader()
669+
for row in self.rows:
670+
self.writer.writerow(row)
671+
672+
# A test failure
638673
class TestFailure(Exception):
639674
def __init__(self, id, returncode, stdout, assert_=None):
640675
self.id = id
641676
self.returncode = returncode
642677
self.stdout = stdout
643678
self.assert_ = assert_
644679

645-
def run_stage(name, runner_, **args):
680+
def run_stage(name, runner_, ids, output_, **args):
646681
# get expected suite/case/perm counts
647682
expected_suite_perms, expected_case_perms, expected_perms, total_perms = (
648-
find_cases(runner_, **args))
683+
find_cases(runner_, ids, **args))
649684

650685
passed_suite_perms = co.defaultdict(lambda: 0)
651686
passed_case_perms = co.defaultdict(lambda: 0)
@@ -662,15 +697,15 @@ def run_stage(name, runner_, **args):
662697
locals = th.local()
663698
children = set()
664699

665-
def run_runner(runner_):
700+
def run_runner(runner_, ids=[]):
666701
nonlocal passed_suite_perms
667702
nonlocal passed_case_perms
668703
nonlocal passed_perms
669704
nonlocal powerlosses
670705
nonlocal locals
671706

672707
# run the tests!
673-
cmd = runner_.copy()
708+
cmd = runner_ + ids
674709
if args.get('verbose'):
675710
print(' '.join(shlex.quote(c) for c in cmd))
676711

@@ -726,6 +761,14 @@ def run_runner(runner_):
726761
passed_suite_perms[m.group('suite')] += 1
727762
passed_case_perms[m.group('case')] += 1
728763
passed_perms += 1
764+
if output_:
765+
# get defines and write to csv
766+
defines = find_defines(
767+
runner_, m.group('id'), **args)
768+
output_.writerow({
769+
'case': m.group('case'),
770+
'test_pass': 1,
771+
**defines})
729772
elif op == 'skipped':
730773
locals.seen_perms += 1
731774
elif op == 'assert':
@@ -750,28 +793,38 @@ def run_runner(runner_):
750793
last_stdout,
751794
last_assert)
752795

753-
def run_job(runner, start=None, step=None):
796+
def run_job(runner_, ids=[], start=None, step=None):
754797
nonlocal failures
755798
nonlocal killed
756799
nonlocal locals
757800

758801
start = start or 0
759802
step = step or 1
760803
while start < total_perms:
761-
runner_ = runner.copy()
804+
job_runner = runner_.copy()
762805
if args.get('isolate') or args.get('valgrind'):
763-
runner_.append('-s%s,%s,%s' % (start, start+step, step))
806+
job_runner.append('-s%s,%s,%s' % (start, start+step, step))
764807
else:
765-
runner_.append('-s%s,,%s' % (start, step))
808+
job_runner.append('-s%s,,%s' % (start, step))
766809

767810
try:
768811
# run the tests
769812
locals.seen_perms = 0
770-
run_runner(runner_)
813+
run_runner(job_runner, ids)
771814
assert locals.seen_perms > 0
772815
start += locals.seen_perms*step
773816

774817
except TestFailure as failure:
818+
# keep track of failures
819+
if output_:
820+
suite, case, _ = failure.id.split(':', 2)
821+
# get defines and write to csv
822+
defines = find_defines(runner_, failure.id, **args)
823+
output_.writerow({
824+
'case': ':'.join([suite, case]),
825+
'test_pass': 0,
826+
**defines})
827+
775828
# race condition for multiple failures?
776829
if failures and not args.get('keep_going'):
777830
break
@@ -796,11 +849,11 @@ def run_job(runner, start=None, step=None):
796849
if 'jobs' in args:
797850
for job in range(args['jobs']):
798851
runners.append(th.Thread(
799-
target=run_job, args=(runner_, job, args['jobs']),
852+
target=run_job, args=(runner_, ids, job, args['jobs']),
800853
daemon=True))
801854
else:
802855
runners.append(th.Thread(
803-
target=run_job, args=(runner_, None, None),
856+
target=run_job, args=(runner_, ids, None, None),
804857
daemon=True))
805858

806859
def print_update(done):
@@ -861,13 +914,12 @@ def print_update(done):
861914
killed)
862915

863916

864-
def run(runner, test_ids, **args):
917+
def run(runner, test_ids=[], **args):
865918
# query runner for tests
866-
runner_ = find_runner(runner, test_ids, **args)
867-
print('using runner: %s'
868-
% ' '.join(shlex.quote(c) for c in runner_))
919+
runner_ = find_runner(runner, **args)
920+
print('using runner: %s' % ' '.join(shlex.quote(c) for c in runner_))
869921
expected_suite_perms, expected_case_perms, expected_perms, total_perms = (
870-
find_cases(runner_, **args))
922+
find_cases(runner_, test_ids, **args))
871923
print('found %d suites, %d cases, %d/%d permutations'
872924
% (len(expected_suite_perms),
873925
len(expected_case_perms),
@@ -882,6 +934,9 @@ def run(runner, test_ids, **args):
882934
trace = None
883935
if args.get('trace'):
884936
trace = openio(args['trace'], 'w', 1)
937+
output = None
938+
if args.get('output'):
939+
output = TestOutput(args['output'], ['case'], ['test_pass'])
885940

886941
# measure runtime
887942
start = time.time()
@@ -894,14 +949,12 @@ def run(runner, test_ids, **args):
894949
for by in (expected_case_perms.keys() if args.get('by_cases')
895950
else expected_suite_perms.keys() if args.get('by_suites')
896951
else [None]):
897-
# rebuild runner for each stage to override test identifier if needed
898-
stage_runner = find_runner(runner,
899-
[by] if by is not None else test_ids, **args)
900-
901952
# spawn jobs for stage
902953
expected_, passed_, powerlosses_, failures_, killed = run_stage(
903954
by or 'tests',
904-
stage_runner,
955+
runner_,
956+
[by] if by is not None else test_ids,
957+
output,
905958
**args)
906959
expected += expected_
907960
passed += passed_
@@ -916,6 +969,8 @@ def run(runner, test_ids, **args):
916969
stdout.close()
917970
if trace:
918971
trace.close()
972+
if output:
973+
output.close()
919974

920975
# show summary
921976
print()
@@ -975,29 +1030,29 @@ def run(runner, test_ids, **args):
9751030
or args.get('gdb_case')
9761031
or args.get('gdb_main')):
9771032
failure = failures[0]
978-
runner_ = find_runner(runner, [failure.id], **args)
1033+
cmd = runner_ + [failure.id]
9791034

9801035
if args.get('gdb_main'):
981-
cmd = ['gdb',
1036+
cmd[:0] = ['gdb',
9821037
'-ex', 'break main',
9831038
'-ex', 'run',
984-
'--args'] + runner_
1039+
'--args']
9851040
elif args.get('gdb_case'):
9861041
path, lineno = find_path(runner_, failure.id, **args)
987-
cmd = ['gdb',
1042+
cmd[:0] = ['gdb',
9881043
'-ex', 'break %s:%d' % (path, lineno),
9891044
'-ex', 'run',
990-
'--args'] + runner_
1045+
'--args']
9911046
elif failure.assert_ is not None:
992-
cmd = ['gdb',
1047+
cmd[:0] = ['gdb',
9931048
'-ex', 'run',
9941049
'-ex', 'frame function raise',
9951050
'-ex', 'up 2',
996-
'--args'] + runner_
1051+
'--args']
9971052
else:
998-
cmd = ['gdb',
1053+
cmd[:0] = ['gdb',
9991054
'-ex', 'run',
1000-
'--args'] + runner_
1055+
'--args']
10011056

10021057
# exec gdb interactively
10031058
if args.get('verbose'):
@@ -1088,6 +1143,8 @@ def main(**args):
10881143
help="Direct trace output to this file.")
10891144
test_parser.add_argument('-O', '--stdout',
10901145
help="Direct stdout to this file. Note stderr is already merged here.")
1146+
test_parser.add_argument('-o', '--output',
1147+
help="CSV file to store results.")
10911148
test_parser.add_argument('--read-sleep',
10921149
help="Artificial read delay in seconds.")
10931150
test_parser.add_argument('--prog-sleep',

scripts/tracebd.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,8 @@ def print_line():
600600
time.sleep(sleep)
601601
if not keep_open:
602602
break
603+
# don't just flood open calls
604+
time.sleep(sleep)
603605
except KeyboardInterrupt:
604606
pass
605607
else:
@@ -618,6 +620,8 @@ def parse():
618620
event.set()
619621
if not keep_open:
620622
break
623+
# don't just flood open calls
624+
time.sleep(sleep)
621625
done = True
622626

623627
th.Thread(target=parse, daemon=True).start()

0 commit comments

Comments
 (0)