probatio 0.2.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.
Files changed (47) hide show
  1. package/README.md +10 -169
  2. package/bin/probatio.js +52 -0
  3. package/package.json +21 -25
  4. package/LICENSE +0 -21
  5. package/dist/cli.js +0 -610
  6. package/dist/contract.js +0 -31
  7. package/dist/flags.js +0 -87
  8. package/dist/golden/check.js +0 -188
  9. package/dist/ledger/build.js +0 -341
  10. package/dist/matrix/report.js +0 -256
  11. package/dist/mcp.js +0 -188
  12. package/dist/memory/state.js +0 -222
  13. package/dist/mutate/affected.js +0 -173
  14. package/dist/mutate/child-lines.mjs +0 -39
  15. package/dist/mutate/coverage-map.js +0 -229
  16. package/dist/mutate/csharp-coverage.js +0 -161
  17. package/dist/mutate/fail-reporter.mjs +0 -65
  18. package/dist/mutate/find.js +0 -13
  19. package/dist/mutate/generate.js +0 -153
  20. package/dist/mutate/go-coverage.js +0 -100
  21. package/dist/mutate/ids.js +0 -22
  22. package/dist/mutate/java-coverage.js +0 -217
  23. package/dist/mutate/kill-label.js +0 -34
  24. package/dist/mutate/load-failure.mjs +0 -40
  25. package/dist/mutate/mocha-coverage.cjs +0 -77
  26. package/dist/mutate/node-batch-loader.mjs +0 -99
  27. package/dist/mutate/node-batch.mjs +0 -167
  28. package/dist/mutate/node-coverage.mjs +0 -77
  29. package/dist/mutate/operators.js +0 -122
  30. package/dist/mutate/patch.js +0 -136
  31. package/dist/mutate/precise-lines.mjs +0 -304
  32. package/dist/mutate/probatio-jacoco-run.java +0 -516
  33. package/dist/mutate/project-config.js +0 -58
  34. package/dist/mutate/py-pytest-reporter.py +0 -145
  35. package/dist/mutate/py-reporter.py +0 -79
  36. package/dist/mutate/run.js +0 -1333
  37. package/dist/mutate/rust-coverage.js +0 -144
  38. package/dist/mutate/sealed.js +0 -118
  39. package/dist/mutate/suite-decision.js +0 -130
  40. package/dist/mutate/suites.js +0 -1524
  41. package/dist/mutate/tally.js +0 -240
  42. package/dist/mutate/text-operators.js +0 -547
  43. package/dist/mutate/titles.js +0 -23
  44. package/dist/node-version.js +0 -13
  45. package/dist/swarm/check-kill.js +0 -114
  46. package/dist/swarm/queue.js +0 -68
  47. package/dist/verify/change.js +0 -340
@@ -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()