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.
Files changed (74) hide show
  1. package/README.md +10 -198
  2. package/bin/probatio.js +52 -0
  3. package/package.json +21 -29
  4. package/LICENSE +0 -21
  5. package/dist/cli.js +0 -721
  6. package/dist/contract.js +0 -32
  7. package/dist/flags.js +0 -87
  8. package/dist/golden/check.js +0 -188
  9. package/dist/golden/record.js +0 -395
  10. package/dist/golden/recorder.mjs +0 -78
  11. package/dist/ledger/build.js +0 -348
  12. package/dist/ledger/check.js +0 -133
  13. package/dist/matrix/report.js +0 -256
  14. package/dist/mcp.js +0 -188
  15. package/dist/memory/state.js +0 -222
  16. package/dist/mutate/affected.js +0 -173
  17. package/dist/mutate/child-lines.mjs +0 -39
  18. package/dist/mutate/coverage-map.js +0 -229
  19. package/dist/mutate/csharp-coverage.js +0 -161
  20. package/dist/mutate/fail-reporter.mjs +0 -65
  21. package/dist/mutate/find.js +0 -13
  22. package/dist/mutate/generate.js +0 -156
  23. package/dist/mutate/go-coverage.js +0 -100
  24. package/dist/mutate/ids.js +0 -22
  25. package/dist/mutate/java-coverage.js +0 -217
  26. package/dist/mutate/kill-label.js +0 -34
  27. package/dist/mutate/load-failure.mjs +0 -40
  28. package/dist/mutate/mocha-coverage.cjs +0 -77
  29. package/dist/mutate/node-batch-loader.mjs +0 -99
  30. package/dist/mutate/node-batch.mjs +0 -167
  31. package/dist/mutate/node-coverage.mjs +0 -77
  32. package/dist/mutate/operators.js +0 -181
  33. package/dist/mutate/patch.js +0 -136
  34. package/dist/mutate/precise-lines.mjs +0 -304
  35. package/dist/mutate/probatio-jacoco-run.java +0 -516
  36. package/dist/mutate/project-config.js +0 -58
  37. package/dist/mutate/py-pytest-reporter.py +0 -145
  38. package/dist/mutate/py-reporter.py +0 -79
  39. package/dist/mutate/run.js +0 -1335
  40. package/dist/mutate/rust-coverage.js +0 -144
  41. package/dist/mutate/sealed.js +0 -118
  42. package/dist/mutate/suite-decision.js +0 -130
  43. package/dist/mutate/suites.js +0 -1524
  44. package/dist/mutate/tally.js +0 -240
  45. package/dist/mutate/text-operators.js +0 -637
  46. package/dist/mutate/titles.js +0 -23
  47. package/dist/node-version.js +0 -13
  48. package/dist/swarm/check-kill.js +0 -114
  49. package/dist/swarm/queue.js +0 -68
  50. package/dist/verify/change.js +0 -340
  51. package/schemas/check-kill.schema.json +0 -93
  52. package/schemas/findings.add.schema.json +0 -53
  53. package/schemas/gap.fix.schema.json +0 -53
  54. package/schemas/gap.revert.schema.json +0 -53
  55. package/schemas/golden.check.schema.json +0 -89
  56. package/schemas/golden.compare.schema.json +0 -151
  57. package/schemas/golden.record.schema.json +0 -126
  58. package/schemas/guard.check.schema.json +0 -53
  59. package/schemas/ledger.build.schema.json +0 -132
  60. package/schemas/ledger.check.schema.json +0 -143
  61. package/schemas/matrix.report.schema.json +0 -129
  62. package/schemas/mcp.schema.json +0 -53
  63. package/schemas/mutate.generate.schema.json +0 -127
  64. package/schemas/mutate.help.schema.json +0 -53
  65. package/schemas/mutate.run.schema.json +0 -272
  66. package/schemas/mutate.sealed.schema.json +0 -53
  67. package/schemas/mutate.tally.schema.json +0 -137
  68. package/schemas/queue.claim.schema.json +0 -53
  69. package/schemas/queue.reap.schema.json +0 -71
  70. package/schemas/queue.seed.schema.json +0 -71
  71. package/schemas/schema.schema.json +0 -74
  72. package/schemas/seal.schema.json +0 -69
  73. package/schemas/status.schema.json +0 -103
  74. 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()