-
Notifications
You must be signed in to change notification settings - Fork 0
/
testy.py
executable file
·89 lines (73 loc) · 2.57 KB
/
testy.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
#!/usr/bin/env python3
"""
run the testy test executables built from the c files being tested.
pass a list of c files you want to run.
each one is built (potentially with exploding fakes inserted for linker
undefined reference errors) and the resulting binary is run, which will execute
the testy main.
"""
import os
import re
import subprocess
import sys
import tempfile
# todo support adjusting compiler commands
# right now it assumes testy.c + testy.h are in the cwd
COMPILER = os.environ.get("CC", "gcc")
TESTY_INCLUDE_PATH = os.path.abspath(os.path.dirname(__file__))
TESTY_C_PATH = os.path.join(TESTY_INCLUDE_PATH, "testy.c")
COMPILE_COMMAND = (
COMPILER
+ " -Og -DTESTY_UNIT_TEST -Wl,--wrap=main "
+ TESTY_C_PATH
+ " -I"
+ TESTY_INCLUDE_PATH
+ " {} -I. -o testy.test && ./testy.test"
)
# regex for locating linker missing definition errors
FIND_LINKER_ERRORS = re.compile(r"undefined reference to `(.*?)'")
def compile_and_run(c_file, fake_file=""):
"""attempt to compile the file under test and run the result"""
result = subprocess.run(
COMPILE_COMMAND.format(c_file + " " + fake_file),
capture_output=True,
shell=True,
)
if result.returncode == 0:
output = result.stdout.decode("utf-8").strip()
if output:
print("\n" + output)
else:
sys.stdout.write(".")
sys.stdout.flush()
return None
# if it failed, check if we need to generate exploding fakes
stderr = result.stderr.decode("utf-8")
m = FIND_LINKER_ERRORS.findall(stderr)
if fake_file or not m:
# bail, don't know what to do here
print("unrecoverable error:")
print(result.args)
print(stderr)
sys.exit(-1)
# return deduplicated set of exploding fakes
return set(m)
def main():
"""main cli entrance point"""
for c_file in sys.argv[1:]:
# run gcc and get a list of fakes needed
fake_list = compile_and_run(c_file)
# check if we need to re-run with autogenerated fakes
if fake_list:
# make the fake file
with tempfile.NamedTemporaryFile("w", suffix=".c") as fake_file:
fake_file.write('#include "testy.h"\n')
fake_file.write(
"\n".join(["TESTY_EXPLODING_FAKE({})".format(x) for x in fake_list])
)
fake_file.write("\n")
fake_file.flush()
# try again, with exploding fakes inserted
compile_and_run(c_file, fake_file.name)
if __name__ == "__main__":
main()