Viewing files at commit 21316fe59317Commit detailsView latest

Browse files
tests/test_baseline_book.py 174 lines · 6.1 KB · python
1
from __future__ import annotations
2
3
import json
4
import subprocess
5
import sys
6
import tempfile
7
import unittest
8
from pathlib import Path
9
10
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts"))
11
12
from oakbench.baseline_book import cells_from_rows
13
14
15
def row(subject: str, kind: str, elapsed: float | None, returncode: int = 0, **extra) -> dict:
16
    base = {
17
        "bench_id": "b",
18
        "profile": "smoke",
19
        "scenario": "tiny_text",
20
        "operation": "status.clean",
21
        "run": extra.pop("run", 0),
22
        "subject": subject,
23
        "subject_kind": kind,
24
        "returncode": returncode,
25
        "command": [subject, "status"],
26
    }
27
    if elapsed is not None:
28
        base["elapsed_ms"] = elapsed
29
    base.update(extra)
30
    return base
31
32
33
class BaselineBookTest(unittest.TestCase):
34
    def test_failed_trial_with_different_identity_cannot_enter_baseline_denominator(self):
35
        with self.assertRaisesRegex(ValueError, "incompatible baseline population.*runner_class"):
36
            cells_from_rows([row("git", "git", 10, runner_class="host-a"),
37
                             row("git", "git", 50, returncode=1, runner_class="host-b")],
38
                            lane="core", metrics=("elapsed_ms",), statistics_names=("median",), bootstrap_samples=20)
39
40
    def test_cells_from_rows_uses_successful_git_rows_only(self) -> None:
41
        cells = cells_from_rows(
42
            [
43
                row("git", "git", 10.0),
44
                row("git", "git", 12.0),
45
                row("git", "git", 99.0, returncode=1),
46
                row("oak_local", "oak", 1.0),
47
            ],
48
            lane="core",
49
            metrics=("elapsed_ms",),
50
            statistics_names=("median",),
51
            bootstrap_samples=20,
52
        )
53
        self.assertEqual(len(cells), 1)
54
        self.assertEqual(cells[0].subject, "git")
55
        self.assertEqual(cells[0].value, 11.0)
56
        self.assertEqual(cells[0].n, 2)
57
58
    def test_cells_from_rows_ignores_unmeasured_metrics(self) -> None:
59
        cells = cells_from_rows(
60
            [row("git", "git", None)],
61
            lane="core",
62
            metrics=("elapsed_ms",),
63
            statistics_names=("median",),
64
            bootstrap_samples=20,
65
        )
66
        self.assertEqual(cells, [])
67
68
    def test_cells_from_rows_stamps_noise_floor_pct(self) -> None:
69
        cells = cells_from_rows(
70
            [row("git", "git", 10.0), row("git", "git", 12.0)],
71
            lane="core",
72
            metrics=("elapsed_ms",),
73
            statistics_names=("median",),
74
            bootstrap_samples=20,
75
            noise_floor_pct=4.2,
76
        )
77
        self.assertEqual(cells[0].noise_floor_pct, 4.2)
78
79
    def test_cells_from_rows_excludes_null_returncode_rows_without_crashing(self) -> None:
80
        broken = row("git", "git", 10.0)
81
        broken["returncode"] = None
82
        cells = cells_from_rows(
83
            [broken],
84
            lane="core",
85
            metrics=("elapsed_ms",),
86
            statistics_names=("median",),
87
            bootstrap_samples=20,
88
        )
89
        self.assertEqual(cells, [])
90
91
    def test_cli_writes_book_files_and_refuses_overwrite(self) -> None:
92
        with tempfile.TemporaryDirectory() as tmp:
93
            root = Path(tmp)
94
            rows = root / "rows.jsonl"
95
            rows.write_text(json.dumps(row("git", "git", 10.0)) + "\n")
96
            out = root / "book"
97
            proc = subprocess.run(
98
                [
99
                    sys.executable,
100
                    "scripts/baseline_book.py",
101
                    "--book-id",
102
                    "test-book",
103
                    "--out",
104
                    str(out),
105
                    "--bootstrap-samples",
106
                    "20",
107
                    "--noise-floor-pct",
108
                    "4.2",
109
                    str(rows),
110
                ],
111
                cwd=Path(__file__).resolve().parents[1],
112
                text=True,
113
                stdout=subprocess.PIPE,
114
                stderr=subprocess.PIPE,
115
                check=False,
116
            )
117
            self.assertEqual(proc.returncode, 0, proc.stderr)
118
            self.assertTrue((out / "manifest.json").exists())
119
            manifest = json.loads((out / "manifest.json").read_text())
120
            self.assertIsNotNone(manifest["runner_id"], "books must carry runner identity (ADR-0007)")
121
            self.assertIsNotNone(manifest["runner_class"])
122
            book = json.loads((out / "book.json").read_text())
123
            self.assertEqual(book["schema_version"], 2)
124
            self.assertEqual(book["cells"][0]["subject"], "git")
125
            self.assertEqual(book["cells"][0]["noise_floor_pct"], 4.2)
126
127
            proc = subprocess.run(
128
                [
129
                    sys.executable,
130
                    "scripts/baseline_book.py",
131
                    "--book-id",
132
                    "test-book",
133
                    "--out",
134
                    str(out),
135
                    str(rows),
136
                ],
137
                cwd=Path(__file__).resolve().parents[1],
138
                text=True,
139
                stdout=subprocess.PIPE,
140
                stderr=subprocess.PIPE,
141
                check=False,
142
            )
143
            self.assertEqual(proc.returncode, 2)
144
            self.assertIn("refusing to overwrite", proc.stderr)
145
146
    def test_cli_rejects_negative_noise_floor_pct(self) -> None:
147
        with tempfile.TemporaryDirectory() as tmp:
148
            root = Path(tmp)
149
            rows = root / "rows.jsonl"
150
            rows.write_text(json.dumps(row("git", "git", 10.0)) + "\n")
151
            proc = subprocess.run(
152
                [
153
                    sys.executable,
154
                    "scripts/baseline_book.py",
155
                    "--book-id",
156
                    "test-book",
157
                    "--out",
158
                    str(root / "book"),
159
                    "--noise-floor-pct",
160
                    "-1",
161
                    str(rows),
162
                ],
163
                cwd=Path(__file__).resolve().parents[1],
164
                text=True,
165
                stdout=subprocess.PIPE,
166
                stderr=subprocess.PIPE,
167
                check=False,
168
            )
169
            self.assertEqual(proc.returncode, 2)
170
            self.assertIn("must be >= 0", proc.stderr)
171
172
173
if __name__ == "__main__":
174
    unittest.main()