Skip to content

Commit b0c0078

Browse files
committed
a proof-of-concept for coverage-goals
1 parent e4f0f9e commit b0c0078

File tree

1 file changed

+91
-0
lines changed

1 file changed

+91
-0
lines changed

lab/goals.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
"""\
2+
Check coverage goals.
3+
4+
Use `coverage json` to get a coverage.json file, then run this tool
5+
to check goals for subsets of files.
6+
7+
Patterns can use '**/foo*.py' to find files anywhere in the project,
8+
and '!**/something.py' to exclude files matching a pattern.
9+
10+
--file will check each file individually for the required coverage.
11+
--group checks the entire group collectively.
12+
13+
"""
14+
15+
import argparse
16+
import json
17+
import sys
18+
19+
from wcmatch import fnmatch as wcfnmatch # python -m pip install wcmatch
20+
21+
from coverage.results import Numbers # Note: an internal class!
22+
23+
24+
def get_data():
25+
with open("coverage.json") as j:
26+
return json.load(j)
27+
28+
def select_files(files, pat):
29+
flags = wcfnmatch.NEGATE | wcfnmatch.NEGATEALL
30+
selected = [f for f in files if wcfnmatch.fnmatch(f, pat, flags=flags)]
31+
return selected
32+
33+
def total_for_files(data, files):
34+
total = Numbers(precision=3)
35+
for f in files:
36+
sel_summ = data["files"][f]["summary"]
37+
total += Numbers(
38+
n_statements=sel_summ["num_statements"],
39+
n_excluded=sel_summ["excluded_lines"],
40+
n_missing=sel_summ["missing_lines"],
41+
n_branches=sel_summ["num_branches"],
42+
n_partial_branches=sel_summ["num_partial_branches"],
43+
n_missing_branches=sel_summ["missing_branches"],
44+
)
45+
46+
return total
47+
48+
def main(argv):
49+
parser = argparse.ArgumentParser(__doc__)
50+
parser.add_argument("--file", "-f", action="store_true", help="Check each file individually")
51+
parser.add_argument("--group", "-g", action="store_true", help="Check a group of files")
52+
parser.add_argument("--verbose", "-v", action="store_true", help="Be chatty about what's happening")
53+
parser.add_argument("goal", type=float, help="Coverage goal")
54+
parser.add_argument("pattern", type=str, nargs="+", help="Patterns to check")
55+
args = parser.parse_args(argv)
56+
57+
if args.file and args.group:
58+
print("Can't use --file and --group together")
59+
return 1
60+
if not (args.file or args.group):
61+
print("Need either --file or --group")
62+
return 1
63+
64+
data = get_data()
65+
all_files = list(data["files"].keys())
66+
selected = select_files(all_files, args.pattern)
67+
68+
ok = True
69+
if args.group:
70+
total = total_for_files(data, selected)
71+
pat_nice = ",".join(args.pattern)
72+
result = f"Coverage for {pat_nice} is {total.pc_covered_str}"
73+
if total.pc_covered < args.goal:
74+
print(f"{result}, below {args.goal}")
75+
ok = False
76+
elif args.verbose:
77+
print(result)
78+
else:
79+
for fname in selected:
80+
total = total_for_files(data, [fname])
81+
result = f"Coverage for {fname} is {total.pc_covered_str}"
82+
if total.pc_covered < args.goal:
83+
print(f"{result}, below {args.goal}")
84+
ok = False
85+
elif args.verbose:
86+
print(result)
87+
88+
return 0 if ok else 2
89+
90+
if __name__ == "__main__":
91+
sys.exit(main(sys.argv[1:]))

0 commit comments

Comments
 (0)