This repository has been archived by the owner on Aug 18, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 35
/
test_runner.py
executable file
·148 lines (132 loc) · 5.41 KB
/
test_runner.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#!/usr/bin/env python3
import argparse
import os
import os.path
import tempfile
import subprocess
import signal
import re
import sys
script_dir = os.path.realpath(sys.path[0])
logisim_location = os.path.join(script_dir, "logisim-evolution.jar")
class LogisimTest():
"""
Runs a circuit file and compares output against the provided reference file.
"""
def __init__(self, group_path, circ_path, trace_path):
self.group_path = group_path
self.circ_path = circ_path
self.trace_path = trace_path
def __call__(self, filename):
output = tempfile.TemporaryFile(mode="r+")
try:
stdinf = open("/dev/null")
except Exception as e:
try:
stdinf = open("nul")
except Exception as e:
print("Could not open nul or /dev/null. Program will most likely error now.")
proc = subprocess.Popen(["java", "-jar", logisim_location, "-tty", "table", self.circ_path],
cwd=self.group_path, stdin=stdinf, stdout=subprocess.PIPE)
try:
reference = open(self.trace_path)
passed = compare_unbounded(proc.stdout, reference, filename)
finally:
try:
os.kill(proc.pid, signal.SIGTERM)
except Exception as e:
pass
if passed:
return (True, "Matched expected output")
else:
return (False, "Did not match expected output")
def compare_unbounded(student_out, reference_out, filename):
passed = True
student_output_array = []
while True:
line1 = student_out.readline().rstrip().decode("utf-8", "namereplace")
line2 = reference_out.readline().rstrip()
if line2 == "":
break
student_output_array.append(line1)
m = re.match(line2, line1)
if m == None or m.start() != 0 or m.end() != len(line2):
passed = False
with open(filename, "w") as student_output:
for line in student_output_array:
student_output.write(line + "\n")
return passed
def run_test(group_path, test_slug, output_type=None):
output_slug = test_slug
if output_type:
output_slug += "-" + output_type
circ_path = os.path.join(group_path, "%s.circ" % test_slug)
reference_output_path = os.path.join(group_path, "reference_output/%s-ref.out" % output_slug)
student_output_path = os.path.join(group_path, "student_output/%s-student.out" % output_slug)
test_runner = LogisimTest(group_path, circ_path, reference_output_path)
return test_runner(student_output_path)
class TestPart:
def __init__(self, part, group, tests):
self.part = part
self.group = group
self.tests = tests
def get(self):
return (self.part, self.group, self.tests)
def run_tests(mpart, mgroup, test):
test_parts = []
parts = sorted([folder for folder in os.listdir(os.path.join(script_dir, "tests")) if os.path.isdir(os.path.join(script_dir, "tests", folder))]) if mpart is None else [mpart]
for part in parts:
part_path = os.path.join(script_dir, "tests", part)
groups = [mgroup] if mgroup is not None else sorted([folder for folder in os.listdir(part_path) if os.path.isdir(os.path.join(part_path, folder))])
for group in groups:
tests = []
group_path = "%s/%s" % (part_path, group)
if not os.path.isdir(group_path):
groups = [filename for filename in os.listdir(part_path) if not filename.startswith(".")]
raise ValueError("Invalid test group: %s (choose from %s)" % (group, ", ".join(groups)))
fls = []
if test is None:
fls = os.listdir(group_path)
else:
f = os.path.join(group_path, test)
if not os.path.isfile(f) or not re.match(r"^(.+)\.circ$", test):
raise ValueError("Invalid test file: %s" % f)
fls.append(test)
for filename in sorted(fls):
match = re.match(r"^(.+)\.circ$", filename)
if match:
test_slug = match.group(1)
tests.append(("%s test" % test_slug, test_slug))
test_parts.append(TestPart(part, group, tests))
for part, group, tests in [p.get() for p in test_parts]:
part_path = os.path.join(script_dir, "tests", part)
group_path = "%s/%s" % (part_path, group)
print("Running tests for %s/%s..." % (part, group))
tests_passed = 0
tests_failed = 0
for test in tests:
description, test_slug = test[:2]
output_type = (test[2] if len(test) >= 3 else None)
did_pass, fail_reason = False, "Unknown test error"
try:
did_pass, fail_reason = run_test(group_path, test_slug, output_type)
except Exception as ex:
print(ex)
if did_pass:
print("\tPASSED test: %s" % description)
tests_passed += 1
else:
print("\tFAILED test: %s (%s)" % (description, fail_reason))
tests_failed += 1
if len(tests) == 0:
print("There are no tests for %s/%s!\n" % (part, group))
else:
print("Passed %d/%d tests\n" % (tests_passed, (tests_passed + tests_failed)))
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Run Logisim tests")
parts = ["part_a", "part_b"]
parser.add_argument("part", choices=parts, help="The project part the test is under (a/b)", nargs="?", default=None)
parser.add_argument("group", help="The group of tests to run. If left blank, it will run all the tests of the part", default=None, nargs="?")
parser.add_argument("test", help="A specific test file to run. You must specify the correct group and the full filename! If left blank, it will be ignored", nargs="?")
args = parser.parse_args()
run_tests(args.part, args.group, args.test)