Viewing files at commit d3edd8c5e361Commit detailsView latest

Browse files
tests/test_lane_contract.py 318 lines · 14.2 KB · python
1
"""Lane-contract conformance: every lane, five checks, positive and negative.
2
3
Parameterized (subTest) over lane_contract.LANE_TABLE. For each lane the
4
valid synthetic rows (mirroring the lane's real emitters) must pass all five
5
checks, and a crafted violation must fail each check that applies — negative
6
tests prove the checks have teeth. The meta-test guarantees a new lane cannot
7
ship without a LANE_TABLE entry.
8
"""
9
10
from __future__ import annotations
11
12
import copy
13
import sys
14
from pathlib import Path
15
16
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts"))
17
18
import unittest
19
20
from oakbench import lane_contract, remotes, rows as oakbench_rows
21
from oakbench.lane_contract import (
22
    DE_FACTO_LANES,
23
    LANE_TABLE,
24
    catalog_operations,
25
    check_catalog_membership,
26
    check_measurement_source,
27
    check_row_contract,
28
    check_skip_coherence,
29
    check_transport_purity,
30
    expected_lanes,
31
)
32
33
34
def sample_rows(lane: str) -> list[dict]:
35
    """Fresh deep copy so one subTest's mutation never leaks into another."""
36
    return copy.deepcopy(LANE_TABLE[lane].sample_rows_builder())
37
38
39
def catalog_errors(contract: lane_contract.LaneContract, rows: list[dict]) -> list[str]:
40
    catalog = catalog_operations(contract.catalog_path) if contract.catalog_path else None
41
    return check_catalog_membership(
42
        rows,
43
        catalog,
44
        exempt_ops=contract.catalog_exempt_ops,
45
        exempt_prefixes=contract.catalog_exempt_prefixes,
46
        exempt_suffixes=contract.catalog_exempt_suffixes,
47
    )
48
49
50
class LaneTableMetaTest(unittest.TestCase):
51
    """Adding a lane without a contract entry fails CI with a clear message."""
52
53
    def test_every_lane_has_a_contract_entry(self) -> None:
54
        missing = sorted(expected_lanes() - set(LANE_TABLE))
55
        self.assertEqual(
56
            missing,
57
            [],
58
            "New lane(s) without a LANE_TABLE entry in scripts/oakbench/lane_contract.py: "
59
            f"{missing}. Add one LaneContract per lane (see docs/new-lane-template.md) "
60
            "so row contracts, skip coherence, transport purity, measurement_source, "
61
            "and catalog membership stay machine-checked.",
62
        )
63
64
    def test_no_orphan_contract_entries(self) -> None:
65
        orphans = sorted(set(LANE_TABLE) - expected_lanes())
66
        self.assertEqual(
67
            orphans,
68
            [],
69
            f"LANE_TABLE entries for unknown lanes: {orphans}. Either add the lane to "
70
            "rows.LANES, list it in lane_contract.DE_FACTO_LANES, or delete the entry.",
71
        )
72
73
    def test_rows_lanes_all_validatable(self) -> None:
74
        # Every rows.py lane delegates to validate_rows under its own key.
75
        for lane in oakbench_rows.LANES:
76
            with self.subTest(lane=lane):
77
                self.assertEqual(LANE_TABLE[lane].validate_lane, lane)
78
79
    def test_de_facto_lanes_documented(self) -> None:
80
        self.assertEqual(set(DE_FACTO_LANES), {"netshape", "external", "agent_loop_hosted"})
81
        # netshape opts into the core key contract (netshape_bench.write_rows).
82
        self.assertEqual(LANE_TABLE["netshape"].validate_lane, "core")
83
        # external (tperf) rows are not bench rows; universal keys only.
84
        self.assertIsNone(LANE_TABLE["external"].validate_lane)
85
        self.assertTrue(LANE_TABLE["external"].universal_keys)
86
        # agent_loop_hosted rows are per-step hosted-loop rows; universal keys only.
87
        self.assertIsNone(LANE_TABLE["agent_loop_hosted"].validate_lane)
88
        self.assertIn("measurement_source", LANE_TABLE["agent_loop_hosted"].universal_keys)
89
90
91
class ValidRowsPassAllChecksTest(unittest.TestCase):
92
    def test_all_lanes(self) -> None:
93
        for lane, contract in LANE_TABLE.items():
94
            rows = sample_rows(lane)
95
            with self.subTest(lane=lane, check="row_contract"):
96
                self.assertEqual(check_row_contract(rows, contract), [])
97
            with self.subTest(lane=lane, check="skip_coherence"):
98
                self.assertEqual(check_skip_coherence(rows), [])
99
            with self.subTest(lane=lane, check="transport_purity"):
100
                self.assertEqual(check_transport_purity(rows), [])
101
            with self.subTest(lane=lane, check="measurement_source"):
102
                self.assertEqual(
103
                    check_measurement_source(
104
                        rows, contract.allowed_sources, required=contract.source_required
105
                    ),
106
                    [],
107
                )
108
            with self.subTest(lane=lane, check="catalog_membership"):
109
                self.assertEqual(catalog_errors(contract, rows), [])
110
111
112
class RowContractNegativeTest(unittest.TestCase):
113
    def test_missing_required_key_fails(self) -> None:
114
        for lane, contract in LANE_TABLE.items():
115
            with self.subTest(lane=lane):
116
                rows = sample_rows(lane)
117
                if contract.validate_lane is not None:
118
                    victim = oakbench_rows.LANE_REQUIRED_KEYS[contract.validate_lane][0]
119
                else:
120
                    victim = contract.universal_keys[0]
121
                del rows[0][victim]
122
                errors = check_row_contract(rows, contract)
123
                self.assertTrue(errors, f"deleting {victim!r} must fail the row contract")
124
                self.assertIn(victim, "\n".join(errors))
125
126
    def test_non_numeric_elapsed_fails(self) -> None:
127
        for lane, contract in LANE_TABLE.items():
128
            with self.subTest(lane=lane):
129
                rows = sample_rows(lane)
130
                rows[0]["elapsed_ms"] = "fast"
131
                self.assertTrue(check_row_contract(rows, contract))
132
133
134
class SkipCoherenceNegativeTest(unittest.TestCase):
135
    def test_77_without_reason_fails(self) -> None:
136
        for lane in LANE_TABLE:
137
            with self.subTest(lane=lane):
138
                rows = sample_rows(lane)
139
                bad = copy.deepcopy(rows[0])
140
                bad["returncode"] = oakbench_rows.SKIP_RETURNCODE
141
                bad.pop("skipped", None)
142
                bad.pop("skip_reason", None)
143
                errors = check_skip_coherence(rows + [bad])
144
                self.assertTrue(any("skipped: true" in e for e in errors))
145
                self.assertTrue(any("skip_reason" in e for e in errors))
146
147
    def test_skipped_without_77_fails(self) -> None:
148
        for lane in LANE_TABLE:
149
            with self.subTest(lane=lane):
150
                rows = sample_rows(lane)
151
                bad = copy.deepcopy(rows[0])
152
                bad["returncode"] = 0
153
                bad["skipped"] = True
154
                bad["skip_reason"] = "half-marked skip"
155
                errors = check_skip_coherence(rows + [bad])
156
                self.assertTrue(any("requires returncode 77" in e for e in errors))
157
158
159
class TransportPurityNegativeTest(unittest.TestCase):
160
    def test_mixed_transports_same_subject_scenario_operation_fail(self) -> None:
161
        for lane in LANE_TABLE:
162
            with self.subTest(lane=lane):
163
                rows = sample_rows(lane)
164
                a = copy.deepcopy(rows[0])
165
                b = copy.deepcopy(rows[0])
166
                a.update(
167
                    remote_repo="x",
168
                    remote_transport=remotes.TRANSPORT_NETWORK,
169
                    remote_server="oak.space",
170
                )
171
                a.pop("netshape_profile", None)
172
                b.update(
173
                    remote_repo="y",
174
                    remote_transport=remotes.TRANSPORT_LOCAL_FILE,
175
                    remote_server="local",
176
                )
177
                b.pop("netshape_profile", None)
178
                errors = check_transport_purity(rows + [a, b])
179
                self.assertTrue(any("transport mixing" in e for e in errors))
180
181
    def test_same_operation_different_subjects_is_legal(self) -> None:
182
        # Side-by-side files may hold one operation at different transports
183
        # for different subjects (publish_gate precedent).
184
        rows = sample_rows("core")
185
        a = {**copy.deepcopy(rows[0]), "subject": "git", "remote_repo": "x",
186
             "remote_transport": remotes.TRANSPORT_LOCAL_FILE, "remote_server": "local"}
187
        b = {**copy.deepcopy(rows[0]), "subject": "oak", "remote_repo": "y",
188
             "remote_transport": remotes.TRANSPORT_NETWORK, "remote_server": "oak.space"}
189
        self.assertEqual(check_transport_purity(rows + [a, b]), [])
190
191
    def test_remote_row_without_transport_fails(self) -> None:
192
        rows = sample_rows("core")
193
        bad = copy.deepcopy(rows[0])
194
        bad["remote_repo"] = "oak/bench-tmp"
195
        bad.pop("remote_transport", None)
196
        errors = check_transport_purity(rows + [bad])
197
        self.assertTrue(any("without remote_transport" in e for e in errors))
198
199
    def test_unknown_transport_fails(self) -> None:
200
        rows = sample_rows("core")
201
        bad = copy.deepcopy(rows[0])
202
        bad.update(remote_repo="x", remote_transport="carrier_pigeon", remote_server="loft")
203
        errors = check_transport_purity(rows + [bad])
204
        self.assertTrue(any("unknown transport" in e for e in errors))
205
206
    def test_shaped_row_without_profile_fails(self) -> None:
207
        rows = sample_rows("netshape")
208
        bad = copy.deepcopy(rows[0])
209
        bad["netshape_profile"] = ""
210
        errors = check_transport_purity([bad])
211
        self.assertTrue(any("netshape_profile" in e for e in errors))
212
213
214
class MeasurementSourceNegativeTest(unittest.TestCase):
215
    def test_off_list_source_fails_or_lane_is_exempt(self) -> None:
216
        for lane, contract in LANE_TABLE.items():
217
            with self.subTest(lane=lane):
218
                rows = sample_rows(lane)
219
                rows[0]["measurement_source"] = "made_up_source"
220
                errors = check_measurement_source(
221
                    rows, contract.allowed_sources, required=contract.source_required
222
                )
223
                if contract.allowed_sources is None:
224
                    # Encoded reality: agent/contention nest their sources in
225
                    # metric blocks; mount/netshape carry none. The top-level
226
                    # check is exempt for them, by the table, not by accident.
227
                    self.assertEqual(errors, [])
228
                else:
229
                    self.assertTrue(any("made_up_source" in e for e in errors))
230
231
    def test_required_source_missing_fails(self) -> None:
232
        contract = LANE_TABLE["external"]
233
        rows = sample_rows("external")
234
        del rows[0]["measurement_source"]
235
        errors = check_measurement_source(rows, contract.allowed_sources, required=True)
236
        self.assertTrue(any("missing required measurement_source" in e for e in errors))
237
238
239
class CatalogMembershipNegativeTest(unittest.TestCase):
240
    def test_off_catalog_operation_fails_or_lane_is_exempt(self) -> None:
241
        for lane, contract in LANE_TABLE.items():
242
            with self.subTest(lane=lane):
243
                rows = sample_rows(lane)
244
                bad = copy.deepcopy(rows[0])
245
                bad["operation"] = "made.up.operation"
246
                errors = catalog_errors(contract, rows + [bad])
247
                if contract.catalog_path is None:
248
                    # Encoded reality: workflow/contention/agent/task-loop/
249
                    # netshape/external declare no operation catalog today.
250
                    self.assertEqual(errors, [])
251
                else:
252
                    self.assertTrue(any("made.up.operation" in e for e in errors))
253
254
    def test_catalogs_parse_and_are_nonempty(self) -> None:
255
        for lane, contract in LANE_TABLE.items():
256
            if contract.catalog_path is None:
257
                continue
258
            with self.subTest(lane=lane):
259
                ops = catalog_operations(contract.catalog_path)
260
                self.assertTrue(ops, f"{contract.catalog_path} parsed to an empty catalog")
261
                self.assertTrue(all("." in op or op.isidentifier() for op in ops), ops)
262
263
    def test_core_catalog_contains_known_operations(self) -> None:
264
        ops = catalog_operations(LANE_TABLE["core"].catalog_path)
265
        for op in ("repo.init", "status.clean", "merge.conflict"):
266
            self.assertIn(op, ops)
267
268
    def test_exemptions_are_documented_reality_not_wildcards(self) -> None:
269
        # The core exemption families mirror bench.py's own diagnostic-row
270
        # exclusions (average_by_subject); keep them in sync deliberately.
271
        core = LANE_TABLE["core"]
272
        self.assertIn("proc.spawn", core.catalog_exempt_ops)
273
        self.assertIn("remote.", core.catalog_exempt_prefixes)
274
        mount = LANE_TABLE["mount"]
275
        self.assertIn("acquire.", mount.catalog_exempt_prefixes)
276
277
278
class SampleRowsMirrorRealEmittersTest(unittest.TestCase):
279
    """Spot-check the synthetic rows against the real emitters' fixed points
280
    so the table cannot drift into testing an imaginary suite."""
281
282
    def test_core_sample_passes_rows_py_directly(self) -> None:
283
        self.assertEqual(oakbench_rows.validate_rows(sample_rows("core"), "core"), [])
284
285
    def test_netshape_sample_carries_shaped_pipe_identity(self) -> None:
286
        for row in sample_rows("netshape"):
287
            self.assertEqual(row["remote_transport"], remotes.TRANSPORT_NETWORK_SHAPED)
288
            self.assertTrue(row["netshape_profile"])
289
            self.assertTrue(row["remote_server"])
290
291
    def test_netshape_shaped_rows_compare_legally_same_pipe_only(self) -> None:
292
        rows = sample_rows("netshape")
293
        self.assertTrue(remotes.shaped_comparison_legal(rows[0], rows[1]))
294
        other_pipe = {**rows[1], "netshape_profile": "wan-80ms"}
295
        self.assertFalse(remotes.shaped_comparison_legal(rows[0], other_pipe))
296
297
    def test_external_sample_matches_tperf_runner_source_label(self) -> None:
298
        import tperf_runner
299
300
        self.assertEqual(
301
            LANE_TABLE["external"].allowed_sources,
302
            frozenset({tperf_runner.MEASUREMENT_SOURCE}),
303
        )
304
        for row in sample_rows("external"):
305
            self.assertEqual(row["measurement_source"], tperf_runner.MEASUREMENT_SOURCE)
306
307
    def test_skip_rows_present_in_lanes_that_emit_them(self) -> None:
308
        for lane in ("core", "workflow", "mount", "platform", "netshape"):
309
            with self.subTest(lane=lane):
310
                skips = [r for r in sample_rows(lane) if r.get("skipped")]
311
                self.assertTrue(skips, f"{lane} sample must include a skip row")
312
                for row in skips:
313
                    self.assertEqual(row["returncode"], oakbench_rows.SKIP_RETURNCODE)
314
                    self.assertTrue(str(row["skip_reason"]).strip())
315
316
317
if __name__ == "__main__":
318
    unittest.main()