probatio 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -198
- package/bin/probatio.js +52 -0
- package/package.json +21 -29
- package/LICENSE +0 -21
- package/dist/cli.js +0 -721
- package/dist/contract.js +0 -32
- package/dist/flags.js +0 -87
- package/dist/golden/check.js +0 -188
- package/dist/golden/record.js +0 -395
- package/dist/golden/recorder.mjs +0 -78
- package/dist/ledger/build.js +0 -348
- package/dist/ledger/check.js +0 -133
- package/dist/matrix/report.js +0 -256
- package/dist/mcp.js +0 -188
- package/dist/memory/state.js +0 -222
- package/dist/mutate/affected.js +0 -173
- package/dist/mutate/child-lines.mjs +0 -39
- package/dist/mutate/coverage-map.js +0 -229
- package/dist/mutate/csharp-coverage.js +0 -161
- package/dist/mutate/fail-reporter.mjs +0 -65
- package/dist/mutate/find.js +0 -13
- package/dist/mutate/generate.js +0 -156
- package/dist/mutate/go-coverage.js +0 -100
- package/dist/mutate/ids.js +0 -22
- package/dist/mutate/java-coverage.js +0 -217
- package/dist/mutate/kill-label.js +0 -34
- package/dist/mutate/load-failure.mjs +0 -40
- package/dist/mutate/mocha-coverage.cjs +0 -77
- package/dist/mutate/node-batch-loader.mjs +0 -99
- package/dist/mutate/node-batch.mjs +0 -167
- package/dist/mutate/node-coverage.mjs +0 -77
- package/dist/mutate/operators.js +0 -181
- package/dist/mutate/patch.js +0 -136
- package/dist/mutate/precise-lines.mjs +0 -304
- package/dist/mutate/probatio-jacoco-run.java +0 -516
- package/dist/mutate/project-config.js +0 -58
- package/dist/mutate/py-pytest-reporter.py +0 -145
- package/dist/mutate/py-reporter.py +0 -79
- package/dist/mutate/run.js +0 -1335
- package/dist/mutate/rust-coverage.js +0 -144
- package/dist/mutate/sealed.js +0 -118
- package/dist/mutate/suite-decision.js +0 -130
- package/dist/mutate/suites.js +0 -1524
- package/dist/mutate/tally.js +0 -240
- package/dist/mutate/text-operators.js +0 -637
- package/dist/mutate/titles.js +0 -23
- package/dist/node-version.js +0 -13
- package/dist/swarm/check-kill.js +0 -114
- package/dist/swarm/queue.js +0 -68
- package/dist/verify/change.js +0 -340
- package/schemas/check-kill.schema.json +0 -93
- package/schemas/findings.add.schema.json +0 -53
- package/schemas/gap.fix.schema.json +0 -53
- package/schemas/gap.revert.schema.json +0 -53
- package/schemas/golden.check.schema.json +0 -89
- package/schemas/golden.compare.schema.json +0 -151
- package/schemas/golden.record.schema.json +0 -126
- package/schemas/guard.check.schema.json +0 -53
- package/schemas/ledger.build.schema.json +0 -132
- package/schemas/ledger.check.schema.json +0 -143
- package/schemas/matrix.report.schema.json +0 -129
- package/schemas/mcp.schema.json +0 -53
- package/schemas/mutate.generate.schema.json +0 -127
- package/schemas/mutate.help.schema.json +0 -53
- package/schemas/mutate.run.schema.json +0 -272
- package/schemas/mutate.sealed.schema.json +0 -53
- package/schemas/mutate.tally.schema.json +0 -137
- package/schemas/queue.claim.schema.json +0 -53
- package/schemas/queue.reap.schema.json +0 -71
- package/schemas/queue.seed.schema.json +0 -71
- package/schemas/schema.schema.json +0 -74
- package/schemas/seal.schema.json +0 -69
- package/schemas/status.schema.json +0 -103
- package/schemas/verify-change.schema.json +0 -222
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""Run pytest and print one Probatio report as a single JSON line.
|
|
3
|
-
|
|
4
|
-
When PROBATIO_COVERAGE_MAP is set and coverage.py imports, also write a
|
|
5
|
-
per-test line map. A missing coverage module leaves the map unwritten.
|
|
6
|
-
"""
|
|
7
|
-
import json
|
|
8
|
-
import os
|
|
9
|
-
import sys
|
|
10
|
-
|
|
11
|
-
# The sysmon core on this interpreter drops dynamic context switches.
|
|
12
|
-
os.environ.setdefault("COVERAGE_CORE", "ctrace")
|
|
13
|
-
|
|
14
|
-
import pytest
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class Collector:
|
|
18
|
-
def __init__(self):
|
|
19
|
-
self.names = []
|
|
20
|
-
self.failed = []
|
|
21
|
-
|
|
22
|
-
def pytest_collectreport(self, report):
|
|
23
|
-
if not report.failed:
|
|
24
|
-
return
|
|
25
|
-
self._add(getattr(report, "nodeid", "") or "pytest", report)
|
|
26
|
-
|
|
27
|
-
def pytest_runtest_logreport(self, report):
|
|
28
|
-
if report.when not in ("setup", "call"):
|
|
29
|
-
return
|
|
30
|
-
if report.when == "setup" and not report.failed:
|
|
31
|
-
return
|
|
32
|
-
name = report.nodeid
|
|
33
|
-
if report.when == "call" and name not in self.names:
|
|
34
|
-
self.names.append(name)
|
|
35
|
-
if report.failed:
|
|
36
|
-
self._add(name, report)
|
|
37
|
-
|
|
38
|
-
def _add(self, name, report):
|
|
39
|
-
if any(item["name"] == name for item in self.failed):
|
|
40
|
-
return
|
|
41
|
-
location = getattr(report, "location", None) or ("", 0, "")
|
|
42
|
-
self.failed.append({"name": name, "file": str(location[0] or ""), "line": int(location[1] or 0)})
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
class ContextSwitch:
|
|
46
|
-
def __init__(self, cov):
|
|
47
|
-
self.cov = cov
|
|
48
|
-
|
|
49
|
-
def pytest_runtest_setup(self, item):
|
|
50
|
-
self.cov.switch_context(item.nodeid)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
def start_coverage():
|
|
54
|
-
dest = os.environ.get("PROBATIO_COVERAGE_MAP")
|
|
55
|
-
if not dest:
|
|
56
|
-
return None, None
|
|
57
|
-
try:
|
|
58
|
-
import coverage
|
|
59
|
-
except ImportError:
|
|
60
|
-
return None, None
|
|
61
|
-
data_file = dest + ".data"
|
|
62
|
-
try:
|
|
63
|
-
cov = coverage.Coverage(cover_pylib=False, data_file=data_file)
|
|
64
|
-
cov.start()
|
|
65
|
-
return cov, dest
|
|
66
|
-
except Exception:
|
|
67
|
-
return None, None
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def write_coverage(cov, dest):
|
|
71
|
-
try:
|
|
72
|
-
cov.stop()
|
|
73
|
-
cov.save()
|
|
74
|
-
except Exception:
|
|
75
|
-
return
|
|
76
|
-
try:
|
|
77
|
-
data = cov.get_data()
|
|
78
|
-
except Exception:
|
|
79
|
-
return
|
|
80
|
-
root = os.path.realpath(os.getcwd())
|
|
81
|
-
files = {}
|
|
82
|
-
for filename in data.measured_files():
|
|
83
|
-
full = os.path.realpath(filename)
|
|
84
|
-
if not full.startswith(root + os.sep):
|
|
85
|
-
continue
|
|
86
|
-
if "/site-packages/" in full or "/node_modules/" in full:
|
|
87
|
-
continue
|
|
88
|
-
rel = full[len(root) + 1 :]
|
|
89
|
-
try:
|
|
90
|
-
by_line = data.contexts_by_lineno(filename) or {}
|
|
91
|
-
except Exception:
|
|
92
|
-
continue
|
|
93
|
-
lines = {}
|
|
94
|
-
for lineno, contexts in by_line.items():
|
|
95
|
-
names = [item for item in contexts if item]
|
|
96
|
-
if names:
|
|
97
|
-
lines[str(lineno)] = names
|
|
98
|
-
if lines:
|
|
99
|
-
files[rel] = lines
|
|
100
|
-
with open(dest, "w", encoding="utf-8") as handle:
|
|
101
|
-
json.dump({"files": files}, handle)
|
|
102
|
-
handle.write("\n")
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
def main():
|
|
106
|
-
args = []
|
|
107
|
-
index = 1
|
|
108
|
-
while index < len(sys.argv):
|
|
109
|
-
if sys.argv[index] == "--pattern":
|
|
110
|
-
args.extend(["-k", sys.argv[index + 1]])
|
|
111
|
-
index += 2
|
|
112
|
-
continue
|
|
113
|
-
args.append(sys.argv[index])
|
|
114
|
-
index += 1
|
|
115
|
-
cov, dest = start_coverage()
|
|
116
|
-
collector = Collector()
|
|
117
|
-
plugins = [collector]
|
|
118
|
-
if cov is not None:
|
|
119
|
-
plugins.append(ContextSwitch(cov))
|
|
120
|
-
code = 1
|
|
121
|
-
try:
|
|
122
|
-
code = pytest.main([*args, "-p", "no:cacheprovider", "--tb=no", "-q"], plugins=plugins) or 0
|
|
123
|
-
except BaseException:
|
|
124
|
-
code = 1
|
|
125
|
-
finally:
|
|
126
|
-
if cov is not None and dest:
|
|
127
|
-
write_coverage(cov, dest)
|
|
128
|
-
names = collector.names or [item["name"] for item in collector.failed]
|
|
129
|
-
failed = collector.failed
|
|
130
|
-
# Exit 4 is a usage error (a bad -k expression). No test ran, so this is not a kill.
|
|
131
|
-
if not names and code != 0:
|
|
132
|
-
names = []
|
|
133
|
-
failed = []
|
|
134
|
-
report = {
|
|
135
|
-
"tests": len(names),
|
|
136
|
-
"pass": len(names) - len(failed),
|
|
137
|
-
"fail": len(failed),
|
|
138
|
-
"failed": failed,
|
|
139
|
-
"names": names,
|
|
140
|
-
}
|
|
141
|
-
sys.stdout.write(json.dumps(report) + "\n")
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
if __name__ == "__main__":
|
|
145
|
-
main()
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""Run unittest modules and print one Probatio report as a single JSON line."""
|
|
3
|
-
import importlib.util
|
|
4
|
-
import json
|
|
5
|
-
import os
|
|
6
|
-
import re
|
|
7
|
-
import sys
|
|
8
|
-
import unittest
|
|
9
|
-
from pathlib import Path
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def load(path: Path):
|
|
13
|
-
name = f"probatio_{path.stem}"
|
|
14
|
-
spec = importlib.util.spec_from_file_location(name, path)
|
|
15
|
-
if spec is None or spec.loader is None:
|
|
16
|
-
raise RuntimeError(f"cannot load {path}")
|
|
17
|
-
module = importlib.util.module_from_spec(spec)
|
|
18
|
-
sys.modules[name] = module
|
|
19
|
-
spec.loader.exec_module(module)
|
|
20
|
-
return module
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def walk(suite):
|
|
24
|
-
for item in suite:
|
|
25
|
-
if isinstance(item, unittest.TestSuite):
|
|
26
|
-
yield from walk(item)
|
|
27
|
-
else:
|
|
28
|
-
yield item
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def parse_args(argv):
|
|
32
|
-
pattern = None
|
|
33
|
-
files = []
|
|
34
|
-
index = 0
|
|
35
|
-
while index < len(argv):
|
|
36
|
-
if argv[index] == "--pattern":
|
|
37
|
-
pattern = argv[index + 1]
|
|
38
|
-
index += 2
|
|
39
|
-
continue
|
|
40
|
-
files.append(argv[index])
|
|
41
|
-
index += 1
|
|
42
|
-
return pattern, files
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def main():
|
|
46
|
-
pattern, files = parse_args(sys.argv[1:])
|
|
47
|
-
sys.path.insert(0, str(Path.cwd()))
|
|
48
|
-
loader = unittest.TestLoader()
|
|
49
|
-
suite = unittest.TestSuite()
|
|
50
|
-
compiled = re.compile(pattern) if pattern else None
|
|
51
|
-
for file in files:
|
|
52
|
-
loaded = loader.loadTestsFromModule(load(Path(file)))
|
|
53
|
-
if compiled is None:
|
|
54
|
-
suite.addTest(loaded)
|
|
55
|
-
else:
|
|
56
|
-
for case in walk(loaded):
|
|
57
|
-
if compiled.fullmatch(case.id()):
|
|
58
|
-
suite.addTest(case)
|
|
59
|
-
cases = [case for case in walk(suite) if case is not None]
|
|
60
|
-
names = [case.id() for case in cases]
|
|
61
|
-
with open(os.devnull, "w") as stream:
|
|
62
|
-
result = unittest.TextTestRunner(stream=stream, verbosity=0).run(suite)
|
|
63
|
-
failed = []
|
|
64
|
-
for case, tb in list(result.failures) + list(result.errors):
|
|
65
|
-
frames = re.findall(r'File "([^"]+)", line (\d+)', tb)
|
|
66
|
-
file, line = frames[-1] if frames else ("", "0")
|
|
67
|
-
failed.append({"name": case.id(), "file": file, "line": int(line)})
|
|
68
|
-
report = {
|
|
69
|
-
"tests": len(names),
|
|
70
|
-
"pass": len(names) - len(failed),
|
|
71
|
-
"fail": len(failed),
|
|
72
|
-
"failed": failed,
|
|
73
|
-
"names": names,
|
|
74
|
-
}
|
|
75
|
-
sys.stdout.write(json.dumps(report) + "\n")
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if __name__ == "__main__":
|
|
79
|
-
main()
|