From 5952694d446dcaea19b9077f50fdfe7e0a76599b Mon Sep 17 00:00:00 2001 From: Pau Aliagas Date: Tue, 27 Oct 2026 06:37:21 +0100 Subject: [PATCH 1/7] Bench schema: xruns, latency_frames, stress, repeats and spread; modes plan and pool (#188) Fields only, so the document stays omx-bench/1. A spread has to hold its median and name a field the row has, and a stressed row has to report its xruns. The same scenario with and without stress is two rows. --- bench/lib/validate.py | 22 ++++- bench/schema/omx-bench.v1.schema.json | 16 +++- bench/tests/sabotage.sh | 22 +++++ bench/tests/test_bench.py | 127 ++++++++++++++++++++++++++ 4 files changed, 182 insertions(+), 5 deletions(-) create mode 100644 bench/tests/test_bench.py diff --git a/bench/lib/validate.py b/bench/lib/validate.py index 73689ec..842f1b4 100644 --- a/bench/lib/validate.py +++ b/bench/lib/validate.py @@ -96,10 +96,10 @@ def cross_rules(doc, errors): for n, s in enumerate(doc.get("scenarios", [])): if not isinstance(s, dict): continue - key = (s.get("id"), s.get("host"), s.get("mode"), s.get("rate"), s.get("frames")) + key = (s.get("id"), s.get("host"), s.get("mode"), s.get("rate"), s.get("frames"), s.get("stress", "none")) if key in seen: - errors.append((["scenarios", n], "rule:unique-row", "repeats row #%d (same id, host, mode, rate, frames)" - % seen[key])) + errors.append((["scenarios", n], "rule:unique-row", + "repeats row #%d (same id, host, mode, rate, frames, stress)" % seen[key])) seen[key] = n avg, mx = s.get("dsp_load_avg"), s.get("dsp_load_max") if isinstance(avg, (int, float)) and isinstance(mx, (int, float)) and mx < avg: @@ -109,6 +109,22 @@ def cross_rules(doc, errors): errors.append((["scenarios", n], "rule:recover-only-ok", "has recover_ms on a row that is not ok")) if s.get("status") == "ok" and s.get("plugins") and s.get("add_ms") is None: errors.append((["scenarios", n], "rule:add-measured", "is ok with plugins and no add_ms")) + spread = s.get("spread") + if isinstance(spread, dict): + if s.get("repeats", 1) < 2: + errors.append((["scenarios", n], "rule:spread-needs-repeats", "has a spread and fewer than two repeats")) + for field, span in spread.items(): + if field not in s: + errors.append((["scenarios", n], "rule:spread-names-a-field", "spread of %s, which the row lacks" + % field)) + elif len(span) != 2 or span[0] > span[1]: + errors.append((["scenarios", n], "rule:spread-min-max", "spread of %s is %s, not [min, max]" + % (field, json.dumps(span)))) + elif isinstance(s[field], (int, float)) and not span[0] <= s[field] <= span[1]: + errors.append((["scenarios", n], "rule:spread-holds-median", "%s %s is outside its spread %s" + % (field, s[field], json.dumps(span)))) + if s.get("stress", "none") != "none" and s.get("status") == "ok" and "xruns" not in s: + errors.append((["scenarios", n], "rule:stress-reports-xruns", "ran under stress and reports no xruns")) if "rss_mb_total" in s and "processes" not in s: errors.append((["scenarios", n], "rule:totals-name-processes", "has totals and no processes count")) m = re.match(r"^([a-z0-9]+)-", str(doc.get("id", ""))) diff --git a/bench/schema/omx-bench.v1.schema.json b/bench/schema/omx-bench.v1.schema.json index 4a980b5..cefd615 100644 --- a/bench/schema/omx-bench.v1.schema.json +++ b/bench/schema/omx-bench.v1.schema.json @@ -58,13 +58,25 @@ "recover_ms": { "type": ["number", "null"], "minimum": 0 }, "status": { "enum": ["ok", "unsupported", "failed"] }, "note": { "type": "string" }, - "mode": { "enum": ["individual", "chain", "shared-process"] }, + "mode": { "enum": ["individual", "chain", "shared-process", "plan", "pool"] }, "processes": { "type": "integer", "minimum": 0 }, "rss_mb_total": { "type": "number", "minimum": 0 }, "pss_mb_total": { "type": "number", "minimum": 0 }, "cpu_pct": { "type": "number", "minimum": 0 }, "ctx_switches": { "type": "integer", "minimum": 0 }, - "ctx_involuntary": { "type": "integer", "minimum": 0 } + "ctx_involuntary": { "type": "integer", "minimum": 0 }, + "xruns": { "type": "integer", "minimum": 0 }, + "latency_frames": { "type": ["integer", "null"], "minimum": 0 }, + "stress": { "type": "string", "pattern": "^(none|busy[0-9]+\\+mem[0-9]+)$" }, + "repeats": { "type": "integer", "minimum": 1 }, + "spread": { + "type": "object", + "additionalProperties": { + "type": "array", + "minItems": 2, + "items": { "type": "number", "minimum": 0 } + } + } }, "allOf": [ { diff --git a/bench/tests/sabotage.sh b/bench/tests/sabotage.sh index 872ed7d..4cc1ff2 100755 --- a/bench/tests/sabotage.sh +++ b/bench/tests/sabotage.sh @@ -64,6 +64,28 @@ break_doc extra "${first_ok}[\"dsp\"] = 1" red "validator: a field the schema does not declare" "extra.json: scenario .*: scenarios/[0-9]+:additionalProperties" \ "$here/validate.sh" "$work/extra.json" +# --- the rules the unit tests guard: a copy of bench/ with one line changed must turn the named test red +break_unit() { # break_unit