Skip to content

Commit 995e18b

Browse files
author
Mark R. Tuttle
committed
Add scripts compare-coverage and compare-result.
These scripts compare viewer output more or less semantically. In particular, they ignore textual output of viewer that changes with every invocation.
1 parent 1fa1fdd commit 995e18b

File tree

2 files changed

+186
-0
lines changed

2 files changed

+186
-0
lines changed

compare-coverage.py

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
#!/usr/bin/env python3
2+
3+
import argparse
4+
import json
5+
import sys
6+
import subprocess
7+
8+
def create_parser():
9+
"""Command line parser."""
10+
11+
parser = argparse.ArgumentParser(
12+
description='Compare CBMC coverage checking results.'
13+
)
14+
15+
parser.add_argument(
16+
'coverage1',
17+
help="""
18+
JSON coverage output of make-coverage or cbmc-viewer.
19+
"""
20+
)
21+
parser.add_argument(
22+
'coverage2',
23+
help="""
24+
JSON coverage output of make-coverage or cbmc-viewer.
25+
"""
26+
)
27+
return parser
28+
29+
def dict_diff(dict1, dict2):
30+
with open('/tmp/coverage1.json', 'w') as out1:
31+
print(json.dumps(dict1, indent=2, sort_keys=True), file=out1)
32+
with open('/tmp/coverage2.json', 'w') as out2:
33+
print(json.dumps(dict2, indent=2, sort_keys=True), file=out2)
34+
35+
proc = subprocess.run(
36+
['diff', '/tmp/coverage1.json', '/tmp/coverage2.json'],
37+
text=True,
38+
capture_output=True,
39+
check=False
40+
)
41+
return proc.stdout
42+
43+
def diff(coverage1, coverage2):
44+
try:
45+
dict1 = coverage1['viewer-coverage']['line_coverage']
46+
dict2 = coverage2['viewer-coverage']['line_coverage']
47+
48+
dict1_missing = []
49+
dict2_missing = []
50+
difference = {}
51+
52+
for key in set([*dict1.keys(), *dict2.keys()]):
53+
if key not in dict1:
54+
dict1_missing.append(key)
55+
continue
56+
if key not in dict2:
57+
dict2_missing.append(key)
58+
continue
59+
60+
idict1 = {int(line): value for line, value in dict1[key].items()}
61+
with open('/tmp/coverage1.json', 'w') as out1:
62+
print(json.dumps(idict1, indent=2, sort_keys=True), file=out1)
63+
64+
idict2 = {int(line): value for line, value in dict2[key].items()}
65+
with open('/tmp/coverage2.json', 'w') as out2:
66+
print(json.dumps(idict2, indent=2, sort_keys=True), file=out2)
67+
68+
proc = subprocess.run(
69+
['diff', '/tmp/coverage1.json', '/tmp/coverage2.json'],
70+
text=True,
71+
capture_output=True,
72+
check=False
73+
)
74+
if proc.stdout:
75+
difference[key] = proc.stdout
76+
77+
if not dict1_missing and not dict2_missing and not difference:
78+
return {}
79+
80+
return {'left-missing': sorted(dict1_missing),
81+
'right-missing': sorted(dict2_missing),
82+
'difference': {key: value
83+
for key, value in sorted(difference.items())}
84+
}
85+
except KeyError:
86+
raise UserWarning("Diff computation failed.")
87+
88+
def compare(coverage1, coverage2, full=False, verbose=True):
89+
try:
90+
dict1 = coverage1['viewer-coverage']
91+
dict2 = coverage2['viewer-coverage']
92+
93+
if full:
94+
return dict1 == dict2
95+
96+
return dict1["line_coverage"] == dict2["line_coverage"]
97+
except KeyError:
98+
return False
99+
100+
################################################################
101+
102+
def main():
103+
"""Compare CBMC coverage checking results."""
104+
105+
args = create_parser().parse_args()
106+
107+
with open(args.coverage1) as res1:
108+
coverage1 = json.load(res1)
109+
with open(args.coverage2) as res2:
110+
coverage2 = json.load(res2)
111+
112+
res = diff(coverage1, coverage2)
113+
if res:
114+
if 'left_missing' in res:
115+
print('Files missing from {}'.format(args.coverage1))
116+
print(json.dumps(res['left_missing'], indent=2))
117+
if 'right_missing' in res:
118+
print('Files missing from {}'.format(args.coverage2))
119+
print(json.dumps(res['right_missing'], indent=2))
120+
if 'difference' in res:
121+
for key in sorted(res['difference'].keys()):
122+
print(key)
123+
print(res['difference'][key])
124+
125+
if __name__ == "__main__":
126+
main()

compare-result.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#!/usr/bin/env python3
2+
3+
import argparse
4+
import json
5+
import sys
6+
7+
def create_parser():
8+
"""Command line parser."""
9+
10+
parser = argparse.ArgumentParser(
11+
description='Compare CBMC property checking results.'
12+
)
13+
14+
parser.add_argument(
15+
'result1',
16+
help="""
17+
JSON result output of make-result or cbmc-viewer.
18+
"""
19+
)
20+
parser.add_argument(
21+
'result2',
22+
help="""
23+
JSON result output of make-result or cbmc-viewer.
24+
"""
25+
)
26+
27+
return parser
28+
29+
def compare(result1, result2, full=False):
30+
try:
31+
dict1 = result1['viewer-result']
32+
dict2 = result2['viewer-result']
33+
34+
keys = set([*dict1.keys(), *dict2.keys()])
35+
if not full:
36+
keys.remove('program')
37+
keys.remove('status')
38+
39+
return all([dict1[key] == dict2[key] for key in keys])
40+
except KeyError:
41+
return False
42+
43+
################################################################
44+
45+
def main():
46+
"""Compare CBMC property checking results."""
47+
48+
args = create_parser().parse_args()
49+
50+
with open(args.result1) as res1:
51+
result1 = json.load(res1)
52+
with open(args.result2) as res2:
53+
result2 = json.load(res2)
54+
55+
if not compare(result1, result2):
56+
print('Results differ')
57+
sys.exit(1)
58+
59+
if __name__ == "__main__":
60+
main()

0 commit comments

Comments
 (0)