From 1f5cde4a1e24fa32e87ebed0a1bc874aa2ba6f21 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 20:57:45 +0700 Subject: [PATCH] lab L1: the t27c lab's decisions as .t27 specs, lab.py as the oracle (Closes #11041) Five specs under specs/lab/ port every decision of infra/t27c-lab/lab.py as pure functions of text and numbers, host-agnostic, with the Python as the oracle until the cutover (epic #11040): - text.t27 the byte-text rules Python decides lines, spaces and characters by - paths.t27 SHA_RE, BRANCH_RE, the checkout and enqueue guards, safe_path, do_GET routing - plan.t27 fnmatch.fnmatchcase, watched, parse_heads, plan, stale_dropped - verdict.t27 the closed gate table, summary, misread_counts, misread_line, verdict, red_gates - prune.t27 the keep-N slice, the run ranking, the scratch-target reaping decision Every assert of lab.py self_check() is a test with the same inputs and outputs, and tables of inputs run through the Python functions are pinned (prune() and reap_scratch_targets() were run for real on temporary directories). Not ported, and refused rather than answered differently: a backwards range in a glob, a count of more than 18 digits. Seals under .trinity/seals/lab_Lab*.json. Co-Authored-By: Claude Sonnet 5.5 --- .trinity/seals/lab_LabPaths.json | 37 + .trinity/seals/lab_LabPlan.json | 41 ++ .trinity/seals/lab_LabPrune.json | 41 ++ .trinity/seals/lab_LabText.json | 33 + .trinity/seals/lab_LabVerdict.json | 37 + specs/lab/paths.t27 | 790 +++++++++++++++++++++ specs/lab/plan.t27 | 980 ++++++++++++++++++++++++++ specs/lab/prune.t27 | 595 ++++++++++++++++ specs/lab/text.t27 | 549 +++++++++++++++ specs/lab/verdict.t27 | 1056 ++++++++++++++++++++++++++++ 10 files changed, 4159 insertions(+) create mode 100644 .trinity/seals/lab_LabPaths.json create mode 100644 .trinity/seals/lab_LabPlan.json create mode 100644 .trinity/seals/lab_LabPrune.json create mode 100644 .trinity/seals/lab_LabText.json create mode 100644 .trinity/seals/lab_LabVerdict.json create mode 100644 specs/lab/paths.t27 create mode 100644 specs/lab/plan.t27 create mode 100644 specs/lab/prune.t27 create mode 100644 specs/lab/text.t27 create mode 100644 specs/lab/verdict.t27 diff --git a/.trinity/seals/lab_LabPaths.json b/.trinity/seals/lab_LabPaths.json new file mode 100644 index 0000000000..0c6feee2c3 --- /dev/null +++ b/.trinity/seals/lab_LabPaths.json @@ -0,0 +1,37 @@ +{ + "built_by": "t27c-bootstrap@0.5.2+c948f4df7", + "closure": [ + { + "spec": "specs/lab/text.t27", + "spec_hash": "sha256:5a108e7c6da57c5c4fbd2c3f109092dcf130ca9107b4f559c4bbba750a863c3e" + }, + { + "spec": "specs/tri/lab/receipt.t27", + "spec_hash": "sha256:00d9eec8d7be84dde596c9990b3a5574de24aff9f7a94579fc620eb585712441" + } + ], + "config": "gen=c,rust,verilog,zig;tests=zig", + "gen_hash_c": "sha256:c9f271333a749d29a47bb2c6d7ecaf3275ee24c78fcee9a55029b695d92bebf6", + "gen_hash_rust": "sha256:2234f8a5a18dd5e0cddb18e96459dffb7b2820ba035c63f9b5a6a888607039a2", + "gen_hash_verilog": "sha256:c0706d2fa2c7362e19fbfd7d5b42b226693ca1cc3abd88c3e36e8b38befa3013", + "gen_hash_zig": "sha256:78ccd452dc068000198c721bc04f7d271a3b24fa59003ca6a89c283e71f88c8a", + "module": "LabPaths", + "ring": 12, + "seal_schema": 2, + "sealed_at": "2026-10-19T13:56:39Z", + "sealed_by": "t27c-bootstrap@0.5.2", + "spec_hash": "sha256:c8606e3c5593f26dde3796256a019fe2d178d6da20511a46685a91561f8f6b63", + "spec_path": "specs/lab/paths.t27", + "tests": { + "failed": 0, + "failing": [], + "forced": false, + "passed": 12, + "total": 12, + "vacuous": 0 + }, + "toolchain": { + "test_runner": "sha256:8512b80da533b79289110fca4a65f3336abd7a059ca4b72ea595d5e33ff8ad10", + "zig": "0.16.0" + } +} \ No newline at end of file diff --git a/.trinity/seals/lab_LabPlan.json b/.trinity/seals/lab_LabPlan.json new file mode 100644 index 0000000000..ca427e97ca --- /dev/null +++ b/.trinity/seals/lab_LabPlan.json @@ -0,0 +1,41 @@ +{ + "built_by": "t27c-bootstrap@0.5.2+c948f4df7", + "closure": [ + { + "spec": "specs/lab/paths.t27", + "spec_hash": "sha256:c8606e3c5593f26dde3796256a019fe2d178d6da20511a46685a91561f8f6b63" + }, + { + "spec": "specs/lab/text.t27", + "spec_hash": "sha256:5a108e7c6da57c5c4fbd2c3f109092dcf130ca9107b4f559c4bbba750a863c3e" + }, + { + "spec": "specs/tri/lab/receipt.t27", + "spec_hash": "sha256:00d9eec8d7be84dde596c9990b3a5574de24aff9f7a94579fc620eb585712441" + } + ], + "config": "gen=c,rust,verilog,zig;tests=zig", + "gen_hash_c": "sha256:315818b8ffa5094b30d728932789997ae94867dff8a90dc2125e78f02d6feacc", + "gen_hash_rust": "sha256:211b82bcba95b2738d5e3e92f1b5d98f78322234bf181d4edcd64e5c43c53374", + "gen_hash_verilog": "sha256:f1c0c95d30f28672386e2bbc4ba1f5d556dab3407e8e86614e99c06afd59a608", + "gen_hash_zig": "sha256:0edef8ebaf855df22c666789a7e2df47e2d9cb549492ccfe9f9cee5f277186ad", + "module": "LabPlan", + "ring": 12, + "seal_schema": 2, + "sealed_at": "2026-10-19T13:56:47Z", + "sealed_by": "t27c-bootstrap@0.5.2", + "spec_hash": "sha256:2934471f53c13ab33d88013fa858dc368d71b27fef7ec6625970bd5c11510e0c", + "spec_path": "specs/lab/plan.t27", + "tests": { + "failed": 0, + "failing": [], + "forced": false, + "passed": 16, + "total": 16, + "vacuous": 0 + }, + "toolchain": { + "test_runner": "sha256:8512b80da533b79289110fca4a65f3336abd7a059ca4b72ea595d5e33ff8ad10", + "zig": "0.16.0" + } +} \ No newline at end of file diff --git a/.trinity/seals/lab_LabPrune.json b/.trinity/seals/lab_LabPrune.json new file mode 100644 index 0000000000..4ef580bba9 --- /dev/null +++ b/.trinity/seals/lab_LabPrune.json @@ -0,0 +1,41 @@ +{ + "built_by": "t27c-bootstrap@0.5.2+c948f4df7", + "closure": [ + { + "spec": "specs/claw/lab_disk.t27", + "spec_hash": "sha256:e6f72ecf00aec2c63dce43b3cce803db99b676ea4bf258e03689df9eb20b6018" + }, + { + "spec": "specs/lab/text.t27", + "spec_hash": "sha256:5a108e7c6da57c5c4fbd2c3f109092dcf130ca9107b4f559c4bbba750a863c3e" + }, + { + "spec": "specs/tri/lab/receipt.t27", + "spec_hash": "sha256:00d9eec8d7be84dde596c9990b3a5574de24aff9f7a94579fc620eb585712441" + } + ], + "config": "gen=c,rust,verilog,zig;tests=zig", + "gen_hash_c": "sha256:536ebcd57367aa5bf729cdd2496b056295a556d06c4f89a54e40fea7abccafc8", + "gen_hash_rust": "sha256:f16148ccd9899bc4e8ec8b387677747baec934f0249324d39103577809ba0804", + "gen_hash_verilog": "sha256:b90f6d18cc727397e16e48f95b30170ff7bfae781fade7d16be0ab67f8754208", + "gen_hash_zig": "sha256:b201f8be850be555c2306d224618eed7929eb64a0153174957a562023db591a6", + "module": "LabPrune", + "ring": 12, + "seal_schema": 2, + "sealed_at": "2026-10-19T13:57:01Z", + "sealed_by": "t27c-bootstrap@0.5.2", + "spec_hash": "sha256:d837e4b542b5c7b30df5f0daa7ad30ae04a1903443c787b686a1940adde9cd3e", + "spec_path": "specs/lab/prune.t27", + "tests": { + "failed": 0, + "failing": [], + "forced": false, + "passed": 8, + "total": 8, + "vacuous": 0 + }, + "toolchain": { + "test_runner": "sha256:8512b80da533b79289110fca4a65f3336abd7a059ca4b72ea595d5e33ff8ad10", + "zig": "0.16.0" + } +} \ No newline at end of file diff --git a/.trinity/seals/lab_LabText.json b/.trinity/seals/lab_LabText.json new file mode 100644 index 0000000000..44a61f6442 --- /dev/null +++ b/.trinity/seals/lab_LabText.json @@ -0,0 +1,33 @@ +{ + "built_by": "t27c-bootstrap@0.5.2+c948f4df7", + "closure": [ + { + "spec": "specs/tri/lab/receipt.t27", + "spec_hash": "sha256:00d9eec8d7be84dde596c9990b3a5574de24aff9f7a94579fc620eb585712441" + } + ], + "config": "gen=c,rust,verilog,zig;tests=zig", + "gen_hash_c": "sha256:7e358b87d1ada24c71f40df97aa2543f103785d24d2ef3bcb9a050a303d82bcc", + "gen_hash_rust": "sha256:a82937daacbbb1ee8251b598a9d53db713ceff05acca2d5c0ff246992866ce53", + "gen_hash_verilog": "sha256:0a5c1edd881d859b932f199cd9e4d76ea9c77c07c07f00c6149113fbca4d505d", + "gen_hash_zig": "sha256:541ff9b248b51544bb81a7e06dc8a5cebdb24071479bec8bc63e4508615319b2", + "module": "LabText", + "ring": 12, + "seal_schema": 2, + "sealed_at": "2026-10-19T13:56:33Z", + "sealed_by": "t27c-bootstrap@0.5.2", + "spec_hash": "sha256:5a108e7c6da57c5c4fbd2c3f109092dcf130ca9107b4f559c4bbba750a863c3e", + "spec_path": "specs/lab/text.t27", + "tests": { + "failed": 0, + "failing": [], + "forced": false, + "passed": 10, + "total": 10, + "vacuous": 0 + }, + "toolchain": { + "test_runner": "sha256:8512b80da533b79289110fca4a65f3336abd7a059ca4b72ea595d5e33ff8ad10", + "zig": "0.16.0" + } +} \ No newline at end of file diff --git a/.trinity/seals/lab_LabVerdict.json b/.trinity/seals/lab_LabVerdict.json new file mode 100644 index 0000000000..32c041e2f3 --- /dev/null +++ b/.trinity/seals/lab_LabVerdict.json @@ -0,0 +1,37 @@ +{ + "built_by": "t27c-bootstrap@0.5.2+c948f4df7", + "closure": [ + { + "spec": "specs/lab/text.t27", + "spec_hash": "sha256:5a108e7c6da57c5c4fbd2c3f109092dcf130ca9107b4f559c4bbba750a863c3e" + }, + { + "spec": "specs/tri/lab/receipt.t27", + "spec_hash": "sha256:00d9eec8d7be84dde596c9990b3a5574de24aff9f7a94579fc620eb585712441" + } + ], + "config": "gen=c,rust,verilog,zig;tests=zig", + "gen_hash_c": "sha256:bbd13f0e2daa01b8be31d74c2f0ebdf586a29a50f54fb258302f276c983fb408", + "gen_hash_rust": "sha256:4e2949771495943115a9bab073d5b0ed6becc6b0b186c3a433c86ce6a8a1b6c4", + "gen_hash_verilog": "sha256:6ec9fbab4e93b24c02b8be11b927404254392eff506391c0dde6cd09b415c777", + "gen_hash_zig": "sha256:547f199392e18c045b2856fc42957cfea7b40879a2e00701fbd2c1b6bc429301", + "module": "LabVerdict", + "ring": 12, + "seal_schema": 2, + "sealed_at": "2026-10-19T13:56:55Z", + "sealed_by": "t27c-bootstrap@0.5.2", + "spec_hash": "sha256:c4e99ad6489f6bbc76c8208178def04c1e171c5ee3b0cf174abc8eae8d1019c1", + "spec_path": "specs/lab/verdict.t27", + "tests": { + "failed": 0, + "failing": [], + "forced": false, + "passed": 20, + "total": 20, + "vacuous": 0 + }, + "toolchain": { + "test_runner": "sha256:8512b80da533b79289110fca4a65f3336abd7a059ca4b72ea595d5e33ff8ad10", + "zig": "0.16.0" + } +} \ No newline at end of file diff --git a/specs/lab/paths.t27 b/specs/lab/paths.t27 new file mode 100644 index 0000000000..7fc111c8e0 --- /dev/null +++ b/specs/lab/paths.t27 @@ -0,0 +1,790 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/lab/paths.t27 -- which names and which GET paths the t27c lab accepts (gHashTag/t27#11041, epic #11040) +; The t27c lab (infra/t27c-lab/lab.py) serves its runs over plain HTTP GET and takes work from two kinds of name: a +; 40-hex commit sha and a branch name. Every decision about what may be read, fetched or queued is one of five +; patterns, and each is ported here exactly, the Python function is the oracle: +; SHA_RE ^[0-9a-f]{40}$ lab_is_sha +; BRANCH_RE ^[A-Za-z0-9][A-Za-z0-9._/-]{0,199}$ lab_is_branch +; checkout() BRANCH_RE and ".." not in b lab_branch_fetchable (the refspec guard) +; enqueue() SHA_RE or (BRANCH_RE and ".." not in ref) lab_ref_enqueueable +; take_requests SHA_RE, else BRANCH_RE and the ref is a head lab_request_kind +; safe_path() the four routes a GET may read lab_path_parts, lab_path_write +; do_GET /health, /, a file, or 404 lab_route, lab_path_is_json +; THE `$` OF PYTHON. In `re` a `$` without MULTILINE matches at the end of the text AND just before a newline that ends +; it. So `SHA_RE.match(sha + "\n")` and `BRANCH_RE.match("main\n")` are matches, and so is every route below with one +; LF after its last byte; the path a route returns carries no LF (it is built from the captured groups). This file does +; the same, and tests it (the oracle is the specification here): the one extra LF is accepted and a second is not. In +; the lab no such text reaches these functions today (a git ref has no control byte, an HTTP request line ends at its +; LF), so it is a property of the port, not a hole. +; The routes (path with its query, from the first `?`, cut off): +; /latest.json -> latest.json (exact; no LF accepted) +; /runs/.json -> runs/.json +; /runs//. gate [a-z-]{1,40} -> runs//. +; /seals/. -> runs//seals. (#9080) +; Anything else, any upper-case sha or gate, any `..`, any other extension, is no route. do_GET answers /health and +; /health/ (the whole path, a query makes it a 404), / (the banner), a route whose file exists, and 404 otherwise. +; The glue that serves the files and queues a request is thin; this file holds the decisions, and names no host: whatever runs +; the lab (one machine, or any node that takes a lab job, #11043) asks these functions what a name or a path is. +; ASCII only (L3) +; phi^2 + 1/phi^2 = 3 | TRINITY + +module LabPaths; + +use tri::lab::receipt::span_is; +use lab::text::LAB_NO; +use lab::text::lab_has_lit; +use lab::text::lab_lit_at; +use lab::text::lab_find_byte; +use lab::text::lab_put_lit; +use lab::text::lab_put_span; + +pub const LAB_SHA_LEN : usize = 40; +/// BRANCH_RE: one leading letter or digit and at most 199 more of [A-Za-z0-9._/-]. +pub const LAB_BRANCH_MAX : usize = 200; +/// A gate's file name in a route: [a-z-]{1,40}. +pub const LAB_GATE_MAX : usize = 40; + +/// What a request name is (take_requests). +pub const LAB_REQ_NONE : u8 = 0; +pub const LAB_REQ_SHA : u8 = 1; +pub const LAB_REQ_BRANCH : u8 = 2; + +/// What a GET path is (safe_path). +pub const LAB_P_NONE : u8 = 0; +pub const LAB_P_LATEST : u8 = 1; +pub const LAB_P_RUN : u8 = 2; +pub const LAB_P_GATE : u8 = 3; +pub const LAB_P_SEAL : u8 = 4; + +/// What do_GET does with a path. +pub const LAB_R_NOT_FOUND : u8 = 0; +pub const LAB_R_HEALTH : u8 = 1; +pub const LAB_R_ROOT : u8 = 2; +pub const LAB_R_FILE : u8 = 3; + +/// Is the byte a lower-case hex digit? +pub fn lab_is_hex(c: u8) -> bool { + return (c >= 48 and c <= 57) or (c >= 97 and c <= 102); +} + +/// The end of text[from..to] without the one LF that Python's `$` lets a match leave behind it. +pub fn lab_dollar_end(t: string, from: usize, to: usize) -> usize { + if (to > from and t[to - 1] == 10) { return to - 1; } + return to; +} + +/// SHA_RE.match on text[from..to]: exactly 40 lower-case hex digits, then the end or one LF. +pub fn lab_is_sha(t: string, from: usize, to: usize) -> bool { + const e : usize = lab_dollar_end(t, from, to); + if (e - from != LAB_SHA_LEN) { return false; } + var k : usize = from; + while (k < e) { + if (lab_is_hex(t[k]) == false) { return false; } + k = k + 1; + } + return true; +} + +/// Is the byte one of [A-Za-z0-9]? +pub fn lab_is_alnum(c: u8) -> bool { + return (c >= 48 and c <= 57) or (c >= 65 and c <= 90) or (c >= 97 and c <= 122); +} + +/// BRANCH_RE.match on text[from..to]: a letter or digit, then up to 199 of [A-Za-z0-9._/-], then the end or one LF. +pub fn lab_is_branch(t: string, from: usize, to: usize) -> bool { + const e : usize = lab_dollar_end(t, from, to); + if (e <= from or e - from > LAB_BRANCH_MAX) { return false; } + if (lab_is_alnum(t[from]) == false) { return false; } + var k : usize = from + 1; + while (k < e) { + const c : u8 = t[k]; + if (lab_is_alnum(c) == false and c != 46 and c != 95 and c != 47 and c != 45) { return false; } + k = k + 1; + } + return true; +} + +/// checkout(): a branch the lab puts into a refspec: BRANCH_RE and no `..` anywhere in it. +pub fn lab_branch_fetchable(t: string, from: usize, to: usize) -> bool { + return lab_is_branch(t, from, to) and lab_has_lit(t, from, to, "..") == false; +} + +/// enqueue(): a request the lab drops into its queue: a sha, or a branch with no `..` in it. +pub fn lab_ref_enqueueable(t: string, from: usize, to: usize) -> bool { + return lab_is_sha(t, from, to) or lab_branch_fetchable(t, from, to); +} + +/// take_requests(): a sha is a request for that commit; a branch is one only when it is a head of the repository +/// (in_heads), and then without the `..` rule; everything else is dropped. +pub fn lab_request_kind(t: string, from: usize, to: usize, in_heads: bool) -> u8 { + if (lab_is_sha(t, from, to)) { return LAB_REQ_SHA; } + if (lab_is_branch(t, from, to) and in_heads) { return LAB_REQ_BRANCH; } + return LAB_REQ_NONE; +} + +/// Where a route's last byte may be followed by nothing or by the one LF. +pub fn lab_route_end_ok(t: string, q: usize, n: usize) -> bool { + return q == n or (q + 1 == n and t[q] == 10); +} + +/// The parts of a route: its kind, the sha, the gate name or the file word, and the extension. Offsets into the path. +pub struct LabPathParts { kind: u8, sha_from: usize, name_from: usize, name_to: usize, ext_from: usize, ext_to: usize } + +fn lab_no_parts() -> LabPathParts { + return LabPathParts { kind: LAB_P_NONE, sha_from: 0, name_from: 0, name_to: 0, ext_from: 0, ext_to: 0 }; +} + +/// Is text[from..from + 40] 40 lower-case hex digits? +pub fn lab_hex40_at(t: string, from: usize, n: usize) -> bool { + if (from + LAB_SHA_LEN > n) { return false; } + var k : usize = 0; + while (k < LAB_SHA_LEN) { + if (lab_is_hex(t[from + k]) == false) { return false; } + k = k + 1; + } + return true; +} + +/// safe_path(): which route the path is and where its parts are. The query (from the first `?`) is cut off first. +pub fn lab_path_parts(path: string) -> LabPathParts { + var n : usize = path.len; + const qm : usize = lab_find_byte(path, 0, n, 63); + if (qm != LAB_NO) { n = qm; } + if (span_is(path, 0, n, "/latest.json")) { + return LabPathParts { kind: LAB_P_LATEST, sha_from: 0, name_from: 1, name_to: 7, ext_from: 8, ext_to: 12 }; + } + if (lab_lit_at(path, 0, n, "/runs/") and lab_hex40_at(path, 6, n)) { + const a : usize = 6 + LAB_SHA_LEN; + if (lab_lit_at(path, a, n, ".json") and lab_route_end_ok(path, a + 5, n)) { + return LabPathParts { kind: LAB_P_RUN, sha_from: 6, name_from: 0, name_to: 0, ext_from: a + 1, ext_to: a + 5 }; + } + if (lab_lit_at(path, a, n, "/")) { + var q : usize = a + 1; + while (q < n and ((path[q] >= 97 and path[q] <= 122) or path[q] == 45)) { + q = q + 1; + } + if (q > a + 1 and q - (a + 1) <= LAB_GATE_MAX and lab_lit_at(path, q, n, ".")) { + if (lab_lit_at(path, q + 1, n, "log") and lab_route_end_ok(path, q + 4, n)) { + return LabPathParts { kind: LAB_P_GATE, sha_from: 6, name_from: a + 1, name_to: q, ext_from: q + 1, ext_to: q + 4 }; + } + if (lab_lit_at(path, q + 1, n, "json") and lab_route_end_ok(path, q + 5, n)) { + return LabPathParts { kind: LAB_P_GATE, sha_from: 6, name_from: a + 1, name_to: q, ext_from: q + 1, ext_to: q + 5 }; + } + } + } + } + if (lab_lit_at(path, 0, n, "/seals/") and lab_hex40_at(path, 7, n)) { + const b : usize = 7 + LAB_SHA_LEN; + if (lab_lit_at(path, b, n, ".json") and lab_route_end_ok(path, b + 5, n)) { + return LabPathParts { kind: LAB_P_SEAL, sha_from: 7, name_from: 0, name_to: 0, ext_from: b + 1, ext_to: b + 5 }; + } + if (lab_lit_at(path, b, n, ".patch") and lab_route_end_ok(path, b + 6, n)) { + return LabPathParts { kind: LAB_P_SEAL, sha_from: 7, name_from: 0, name_to: 0, ext_from: b + 1, ext_to: b + 6 }; + } + } + return lab_no_parts(); +} + +/// safe_path(): the kind of route; LAB_P_NONE for a path nothing may read. +pub fn lab_path_kind(path: string) -> u8 { + return lab_path_parts(path).kind; +} + +/// safe_path(): write the file under www a route reads (runs/.json, ...) into dst at w, clipped at cap; the new +/// cursor. A path that is no route writes nothing. +pub fn lab_path_write(path: string, dst: []u8, cap: usize, w: usize) -> usize { + const p : LabPathParts = lab_path_parts(path); + if (p.kind == LAB_P_NONE) { return w; } + if (p.kind == LAB_P_LATEST) { return lab_put_lit(dst, cap, w, "latest.json"); } + var x : usize = lab_put_lit(dst, cap, w, "runs/"); + x = lab_put_span(dst, cap, x, path, p.sha_from, p.sha_from + LAB_SHA_LEN); + if (p.kind == LAB_P_RUN) { return lab_put_span(dst, cap, lab_put_lit(dst, cap, x, "."), path, p.ext_from, p.ext_to); } + if (p.kind == LAB_P_GATE) { + x = lab_put_span(dst, cap, lab_put_lit(dst, cap, x, "/"), path, p.name_from, p.name_to); + } else { + x = lab_put_lit(dst, cap, x, "/seals"); + } + return lab_put_span(dst, cap, lab_put_lit(dst, cap, x, "."), path, p.ext_from, p.ext_to); +} + +/// do_GET: the content type is application/json when the file the route reads ends in .json, else text/plain. +pub fn lab_path_is_json(path: string) -> bool { + const p : LabPathParts = lab_path_parts(path); + if (p.kind == LAB_P_NONE) { return false; } + return span_is(path, p.ext_from, p.ext_to, "json"); +} + +/// do_GET: what the request line asks for. /health and /health/ are the whole path, a query makes it no health check. +pub fn lab_route(path: string) -> u8 { + if (span_is(path, 0, path.len, "/health") or span_is(path, 0, path.len, "/health/")) { return LAB_R_HEALTH; } + if (span_is(path, 0, path.len, "/")) { return LAB_R_ROOT; } + if (lab_path_kind(path) != LAB_P_NONE) { return LAB_R_FILE; } + return LAB_R_NOT_FOUND; +} + +// ---- tests ---- +// The oracle is infra/t27c-lab/lab.py. Every case below is its answer (Python 3.14.6), pinned: first the asserts of its +// self_check(), one to one, then tables of inputs run through its own functions (SHA_RE, BRANCH_RE, safe_path, do_GET's +// routing, enqueue's and take_requests' guards). + +fn lab_test_sha(s: string) -> bool { + return lab_is_sha(s, 0, s.len); +} + +fn lab_test_branch(s: string) -> bool { + return lab_is_branch(s, 0, s.len); +} + +fn lab_test_fetchable(s: string) -> bool { + return lab_branch_fetchable(s, 0, s.len); +} + +fn lab_test_enqueueable(s: string) -> bool { + return lab_ref_enqueueable(s, 0, s.len); +} + +fn lab_test_request(s: string, in_heads: bool) -> u8 { + return lab_request_kind(s, 0, s.len, in_heads); +} + +/// The kind, the file the route reads and its content type, all three, for one path. +fn lab_test_path(path: string, kind: u8, rel: string, json: bool) -> bool { + var out : [128]u8 = undefined; + const w : usize = lab_path_write(path, &out, 128, 0); + if (w != rel.len or span_is(&out, 0, w, rel) == false) { return false; } + return lab_path_kind(path) == kind and lab_path_is_json(path) == json; +} + +// <<< pinned table: paths_self_check +test lab_py_self_check_safe_path_reads_the_four_routes { + assert lab_test_path("/latest.json", LAB_P_LATEST, "latest.json", true); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json", LAB_P_RUN, "runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json", true); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/suite.log", LAB_P_GATE, "runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/suite.log", false); + assert lab_test_path("/seals/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.patch", LAB_P_SEAL, "runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/seals.patch", false); +} + +test lab_py_self_check_safe_path_refuses_the_wrong_extension_and_every_traversal { + assert lab_test_path("/seals/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.log", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.patch", LAB_P_NONE, "", false); + assert lab_test_path("/../etc/passwd", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/../../x.log", LAB_P_NONE, "", false); + assert lab_test_path("/runs/abc.json", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Suite.log", LAB_P_NONE, "", false); + assert lab_test_path("/data/src/.git/config", LAB_P_NONE, "", false); +} + +test lab_py_self_check_branch_re { + assert lab_test_branch("claude/t27c-match-tail-expr") == true; + assert lab_test_branch("-x") == false; + assert lab_test_branch("a b") == false; +} + +// >>> pinned table: paths_self_check +// <<< pinned table: paths_sha +// SHA_RE.match(s): 40 lower-case hex digits; Python's $ also lets one LF follow. +test lab_py_table_sha_re { + assert lab_test_sha("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") == true; + assert lab_test_sha("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb") == true; + assert lab_test_sha("0123456789abcdef0123456789abcdef01234567") == true; + assert lab_test_sha("ffffffffffffffffffffffffffffffffffffffff") == true; + assert lab_test_sha("0000000000000000000000000000000000000000") == true; + assert lab_test_sha("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") == false; + assert lab_test_sha("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") == false; + assert lab_test_sha("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") == false; + assert lab_test_sha("") == false; + assert lab_test_sha("a") == false; + assert lab_test_sha("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA") == false; + assert lab_test_sha("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaA") == false; + assert lab_test_sha("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaag") == false; + assert lab_test_sha("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaG") == false; + assert lab_test_sha("gggggggggggggggggggggggggggggggggggggggg") == false; + assert lab_test_sha(" aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") == false; + assert lab_test_sha("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ") == false; + assert lab_test_sha("aaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaa") == false; + assert lab_test_sha("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n") == true; + assert lab_test_sha("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n\n") == false; + assert lab_test_sha("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n") == false; + assert lab_test_sha("\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") == false; + assert lab_test_sha("aaaaaaaaaaaaaaaaaaaa\naaaaaaaaaaaaaaaaaaa") == false; + assert lab_test_sha("\n") == false; + assert lab_test_sha("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\t") == false; + assert lab_test_sha("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaax") == false; + assert lab_test_sha("xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") == false; + assert lab_test_sha("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") == false; + assert lab_test_sha("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-") == false; + assert lab_test_sha("master") == false; + assert lab_test_sha("HEAD") == false; + assert lab_test_sha("9999999999999999999999999999999999999999") == true; + assert lab_test_sha("abcdefabcdefabcdefabcdefabcdefabcdefabcd") == true; + assert lab_test_sha("abcdefabcdefabcdefabcdefabcdefabcdefabc") == false; + assert lab_test_sha("abcdefgabcdefgabcdefgabcdefgabcdefgabcde") == false; +} + +// >>> pinned table: paths_sha +// <<< pinned table: paths_branch +// BRANCH_RE.match(b); checkout()'s guard (BRANCH_RE and no ..); enqueue()'s rule (a sha, or that guard). +test lab_py_table_branch_re { + assert lab_test_branch("master") == true; + assert lab_test_branch("main") == true; + assert lab_test_branch("claude/t27c-x") == true; + assert lab_test_branch("claude/lab-l1-t27c-11041") == true; + assert lab_test_branch("a") == true; + assert lab_test_branch("A") == true; + assert lab_test_branch("0") == true; + assert lab_test_branch("9z") == true; + assert lab_test_branch("a.b") == true; + assert lab_test_branch("a_b") == true; + assert lab_test_branch("a-b") == true; + assert lab_test_branch("a/b") == true; + assert lab_test_branch("-x") == false; + assert lab_test_branch(".x") == false; + assert lab_test_branch("_x") == false; + assert lab_test_branch("/x") == false; + assert lab_test_branch("x/") == true; + assert lab_test_branch("x.") == true; + assert lab_test_branch("x..y") == true; + assert lab_test_branch("x/../y") == true; + assert lab_test_branch("..") == false; + assert lab_test_branch("a..") == true; + assert lab_test_branch("..a") == false; + assert lab_test_branch("a/..") == true; + assert lab_test_branch("a b") == false; + assert lab_test_branch(" a") == false; + assert lab_test_branch("a ") == false; + assert lab_test_branch("a\tb") == false; + assert lab_test_branch("a\n") == true; + assert lab_test_branch("a\n\n") == false; + assert lab_test_branch("\na") == false; + assert lab_test_branch("a\nb") == false; + assert lab_test_branch("") == false; + assert lab_test_branch("\n") == false; + assert lab_test_branch("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") == true; + assert lab_test_branch("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") == false; + assert lab_test_branch("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n") == true; + assert lab_test_branch("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n") == true; + assert lab_test_branch("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n") == false; + assert lab_test_branch("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") == true; + assert lab_test_branch("a-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------") == true; + assert lab_test_branch("a--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------") == false; + assert lab_test_branch("refs/heads/master") == true; + assert lab_test_branch("refs/pull/1/head") == true; + assert lab_test_branch("feature#1") == false; + assert lab_test_branch("a+b") == false; + assert lab_test_branch("a:b") == false; + assert lab_test_branch("a~1") == false; + assert lab_test_branch("a^") == false; + assert lab_test_branch("a?") == false; + assert lab_test_branch("a*") == false; + assert lab_test_branch("a[0]") == false; + assert lab_test_branch("a@b") == false; + assert lab_test_branch("a,b") == false; + assert lab_test_branch("a;b") == false; + assert lab_test_branch("a$b") == false; + assert lab_test_branch("a%b") == false; + assert lab_test_branch("a\\b") == false; + assert lab_test_branch("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx") == true; + assert lab_test_branch("ab12cd") == true; + assert lab_test_branch("Release/1.2.3") == true; + assert lab_test_branch("v1.0-rc.1") == true; + assert lab_test_branch("t27c") == true; + assert lab_test_branch("t27c-lab") == true; + assert lab_test_branch("claude/t27c-*") == false; +} + +test lab_py_table_checkout_guard { + assert lab_test_fetchable("master") == true; + assert lab_test_fetchable("main") == true; + assert lab_test_fetchable("claude/t27c-x") == true; + assert lab_test_fetchable("claude/lab-l1-t27c-11041") == true; + assert lab_test_fetchable("a") == true; + assert lab_test_fetchable("A") == true; + assert lab_test_fetchable("0") == true; + assert lab_test_fetchable("9z") == true; + assert lab_test_fetchable("a.b") == true; + assert lab_test_fetchable("a_b") == true; + assert lab_test_fetchable("a-b") == true; + assert lab_test_fetchable("a/b") == true; + assert lab_test_fetchable("-x") == false; + assert lab_test_fetchable(".x") == false; + assert lab_test_fetchable("_x") == false; + assert lab_test_fetchable("/x") == false; + assert lab_test_fetchable("x/") == true; + assert lab_test_fetchable("x.") == true; + assert lab_test_fetchable("x..y") == false; + assert lab_test_fetchable("x/../y") == false; + assert lab_test_fetchable("..") == false; + assert lab_test_fetchable("a..") == false; + assert lab_test_fetchable("..a") == false; + assert lab_test_fetchable("a/..") == false; + assert lab_test_fetchable("a b") == false; + assert lab_test_fetchable(" a") == false; + assert lab_test_fetchable("a ") == false; + assert lab_test_fetchable("a\tb") == false; + assert lab_test_fetchable("a\n") == true; + assert lab_test_fetchable("a\n\n") == false; + assert lab_test_fetchable("\na") == false; + assert lab_test_fetchable("a\nb") == false; + assert lab_test_fetchable("") == false; + assert lab_test_fetchable("\n") == false; + assert lab_test_fetchable("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") == true; + assert lab_test_fetchable("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") == false; + assert lab_test_fetchable("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n") == true; + assert lab_test_fetchable("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n") == true; + assert lab_test_fetchable("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n") == false; + assert lab_test_fetchable("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") == true; + assert lab_test_fetchable("a-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------") == true; + assert lab_test_fetchable("a--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------") == false; + assert lab_test_fetchable("refs/heads/master") == true; + assert lab_test_fetchable("refs/pull/1/head") == true; + assert lab_test_fetchable("feature#1") == false; + assert lab_test_fetchable("a+b") == false; + assert lab_test_fetchable("a:b") == false; + assert lab_test_fetchable("a~1") == false; + assert lab_test_fetchable("a^") == false; + assert lab_test_fetchable("a?") == false; + assert lab_test_fetchable("a*") == false; + assert lab_test_fetchable("a[0]") == false; + assert lab_test_fetchable("a@b") == false; + assert lab_test_fetchable("a,b") == false; + assert lab_test_fetchable("a;b") == false; + assert lab_test_fetchable("a$b") == false; + assert lab_test_fetchable("a%b") == false; + assert lab_test_fetchable("a\\b") == false; + assert lab_test_fetchable("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx") == true; + assert lab_test_fetchable("ab12cd") == true; + assert lab_test_fetchable("Release/1.2.3") == true; + assert lab_test_fetchable("v1.0-rc.1") == true; + assert lab_test_fetchable("t27c") == true; + assert lab_test_fetchable("t27c-lab") == true; + assert lab_test_fetchable("claude/t27c-*") == false; +} + +test lab_py_table_enqueue_rule { + assert lab_test_enqueueable("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") == true; + assert lab_test_enqueueable("0123456789abcdef0123456789abcdef01234567") == true; + assert lab_test_enqueueable("0000000000000000000000000000000000000000") == true; + assert lab_test_enqueueable("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") == true; + assert lab_test_enqueueable("") == false; + assert lab_test_enqueueable("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA") == true; + assert lab_test_enqueueable("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaag") == true; + assert lab_test_enqueueable("gggggggggggggggggggggggggggggggggggggggg") == true; + assert lab_test_enqueueable("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ") == false; + assert lab_test_enqueueable("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n") == true; + assert lab_test_enqueueable("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n") == true; + assert lab_test_enqueueable("aaaaaaaaaaaaaaaaaaaa\naaaaaaaaaaaaaaaaaaa") == false; + assert lab_test_enqueueable("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\t") == false; + assert lab_test_enqueueable("xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") == true; + assert lab_test_enqueueable("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-") == true; + assert lab_test_enqueueable("HEAD") == true; + assert lab_test_enqueueable("abcdefabcdefabcdefabcdefabcdefabcdefabcd") == true; + assert lab_test_enqueueable("abcdefgabcdefgabcdefgabcdefgabcdefgabcde") == true; + assert lab_test_enqueueable("main") == true; + assert lab_test_enqueueable("claude/lab-l1-t27c-11041") == true; + assert lab_test_enqueueable("A") == true; + assert lab_test_enqueueable("9z") == true; + assert lab_test_enqueueable("a_b") == true; + assert lab_test_enqueueable("a/b") == true; + assert lab_test_enqueueable(".x") == false; + assert lab_test_enqueueable("/x") == false; + assert lab_test_enqueueable("x.") == true; + assert lab_test_enqueueable("x/../y") == false; + assert lab_test_enqueueable("a..") == false; + assert lab_test_enqueueable("a/..") == false; + assert lab_test_enqueueable(" a") == false; + assert lab_test_enqueueable("a\tb") == false; + assert lab_test_enqueueable("a\n\n") == false; + assert lab_test_enqueueable("a\nb") == false; + assert lab_test_enqueueable("\n") == false; + assert lab_test_enqueueable("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") == false; + assert lab_test_enqueueable("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n") == true; + assert lab_test_enqueueable("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") == true; + assert lab_test_enqueueable("a--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------") == false; + assert lab_test_enqueueable("refs/pull/1/head") == true; + assert lab_test_enqueueable("a+b") == false; + assert lab_test_enqueueable("a~1") == false; + assert lab_test_enqueueable("a?") == false; + assert lab_test_enqueueable("a[0]") == false; + assert lab_test_enqueueable("a,b") == false; + assert lab_test_enqueueable("a$b") == false; + assert lab_test_enqueueable("a\\b") == false; + assert lab_test_enqueueable("ab12cd") == true; + assert lab_test_enqueueable("v1.0-rc.1") == true; + assert lab_test_enqueueable("t27c-lab") == true; +} + +test lab_py_table_take_requests_kind { + assert lab_test_request("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", true) == LAB_REQ_SHA; + assert lab_test_request("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", false) == LAB_REQ_SHA; + assert lab_test_request("0000000000000000000000000000000000000000", true) == LAB_REQ_SHA; + assert lab_test_request("0000000000000000000000000000000000000000", false) == LAB_REQ_SHA; + assert lab_test_request("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", true) == LAB_REQ_BRANCH; + assert lab_test_request("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", false) == LAB_REQ_NONE; + assert lab_test_request("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", true) == LAB_REQ_BRANCH; + assert lab_test_request("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", false) == LAB_REQ_NONE; + assert lab_test_request("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaG", true) == LAB_REQ_BRANCH; + assert lab_test_request("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaG", false) == LAB_REQ_NONE; + assert lab_test_request("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ", true) == LAB_REQ_NONE; + assert lab_test_request("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ", false) == LAB_REQ_NONE; + assert lab_test_request("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n\n", true) == LAB_REQ_NONE; + assert lab_test_request("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n\n", false) == LAB_REQ_NONE; + assert lab_test_request("aaaaaaaaaaaaaaaaaaaa\naaaaaaaaaaaaaaaaaaa", true) == LAB_REQ_NONE; + assert lab_test_request("aaaaaaaaaaaaaaaaaaaa\naaaaaaaaaaaaaaaaaaa", false) == LAB_REQ_NONE; + assert lab_test_request("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaax", true) == LAB_REQ_BRANCH; + assert lab_test_request("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaax", false) == LAB_REQ_NONE; + assert lab_test_request("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-", true) == LAB_REQ_BRANCH; + assert lab_test_request("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-", false) == LAB_REQ_NONE; + assert lab_test_request("9999999999999999999999999999999999999999", true) == LAB_REQ_SHA; + assert lab_test_request("9999999999999999999999999999999999999999", false) == LAB_REQ_SHA; + assert lab_test_request("abcdefgabcdefgabcdefgabcdefgabcdefgabcde", true) == LAB_REQ_BRANCH; + assert lab_test_request("abcdefgabcdefgabcdefgabcdefgabcdefgabcde", false) == LAB_REQ_NONE; + assert lab_test_request("claude/t27c-x", true) == LAB_REQ_BRANCH; + assert lab_test_request("claude/t27c-x", false) == LAB_REQ_NONE; + assert lab_test_request("A", true) == LAB_REQ_BRANCH; + assert lab_test_request("A", false) == LAB_REQ_NONE; + assert lab_test_request("a.b", true) == LAB_REQ_BRANCH; + assert lab_test_request("a.b", false) == LAB_REQ_NONE; + assert lab_test_request("a/b", true) == LAB_REQ_BRANCH; + assert lab_test_request("a/b", false) == LAB_REQ_NONE; + assert lab_test_request("_x", true) == LAB_REQ_NONE; + assert lab_test_request("_x", false) == LAB_REQ_NONE; + assert lab_test_request("x.", true) == LAB_REQ_BRANCH; + assert lab_test_request("x.", false) == LAB_REQ_NONE; + assert lab_test_request("..", true) == LAB_REQ_NONE; + assert lab_test_request("..", false) == LAB_REQ_NONE; + assert lab_test_request("a/..", true) == LAB_REQ_BRANCH; + assert lab_test_request("a/..", false) == LAB_REQ_NONE; + assert lab_test_request("a ", true) == LAB_REQ_NONE; + assert lab_test_request("a ", false) == LAB_REQ_NONE; + assert lab_test_request("a\n\n", true) == LAB_REQ_NONE; + assert lab_test_request("a\n\n", false) == LAB_REQ_NONE; + assert lab_test_request("", true) == LAB_REQ_NONE; + assert lab_test_request("", false) == LAB_REQ_NONE; + assert lab_test_request("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", true) == LAB_REQ_NONE; + assert lab_test_request("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", false) == LAB_REQ_NONE; + assert lab_test_request("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", true) == LAB_REQ_NONE; + assert lab_test_request("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", false) == LAB_REQ_NONE; + assert lab_test_request("a--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------", true) == LAB_REQ_NONE; + assert lab_test_request("a--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------", false) == LAB_REQ_NONE; + assert lab_test_request("feature#1", true) == LAB_REQ_NONE; + assert lab_test_request("feature#1", false) == LAB_REQ_NONE; + assert lab_test_request("a~1", true) == LAB_REQ_NONE; + assert lab_test_request("a~1", false) == LAB_REQ_NONE; + assert lab_test_request("a*", true) == LAB_REQ_NONE; + assert lab_test_request("a*", false) == LAB_REQ_NONE; + assert lab_test_request("a,b", true) == LAB_REQ_NONE; + assert lab_test_request("a,b", false) == LAB_REQ_NONE; + assert lab_test_request("a%b", true) == LAB_REQ_NONE; + assert lab_test_request("a%b", false) == LAB_REQ_NONE; + assert lab_test_request("ab12cd", true) == LAB_REQ_BRANCH; + assert lab_test_request("ab12cd", false) == LAB_REQ_NONE; + assert lab_test_request("t27c", true) == LAB_REQ_BRANCH; + assert lab_test_request("t27c", false) == LAB_REQ_NONE; +} + +// >>> pinned table: paths_branch +// <<< pinned table: paths_routes +// safe_path(): every path of the table, the file it reads, its route and its content type (rel.endswith('.json')). +test lab_py_table_safe_path { + assert lab_test_path("/latest.json", LAB_P_LATEST, "latest.json", true); + assert lab_test_path("/latest.json?x=1", LAB_P_LATEST, "latest.json", true); + assert lab_test_path("/latest.json?", LAB_P_LATEST, "latest.json", true); + assert lab_test_path("/latest.json/", LAB_P_NONE, "", false); + assert lab_test_path("/latest.json\n", LAB_P_NONE, "", false); + assert lab_test_path("/Latest.json", LAB_P_NONE, "", false); + assert lab_test_path("latest.json", LAB_P_NONE, "", false); + assert lab_test_path("/latest.jsonx", LAB_P_NONE, "", false); + assert lab_test_path("//latest.json", LAB_P_NONE, "", false); + assert lab_test_path("/latest.json#x", LAB_P_NONE, "", false); + assert lab_test_path("/latest", LAB_P_NONE, "", false); + assert lab_test_path("/latest.json.", LAB_P_NONE, "", false); + assert lab_test_path("/latest.json%00", LAB_P_NONE, "", false); + assert lab_test_path("/latest.json?/runs/", LAB_P_LATEST, "latest.json", true); + assert lab_test_path("/latest.json\n?x", LAB_P_NONE, "", false); + assert lab_test_path("", LAB_P_NONE, "", false); + assert lab_test_path("/", LAB_P_NONE, "", false); + assert lab_test_path("/runs", LAB_P_NONE, "", false); + assert lab_test_path("/runs/", LAB_P_NONE, "", false); + assert lab_test_path("/runs//x", LAB_P_NONE, "", false); + assert lab_test_path("/seals/", LAB_P_NONE, "", false); + assert lab_test_path("/seals", LAB_P_NONE, "", false); + assert lab_test_path("/../etc/passwd", LAB_P_NONE, "", false); + assert lab_test_path("/runs/../x", LAB_P_NONE, "", false); + assert lab_test_path("/data/src/.git/config", LAB_P_NONE, "", false); + assert lab_test_path("/healthz", LAB_P_NONE, "", false); + assert lab_test_path("/health", LAB_P_NONE, "", false); + assert lab_test_path("/latest.JSON", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json", LAB_P_RUN, "runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json", true); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json?x", LAB_P_RUN, "runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json", true); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json\n", LAB_P_RUN, "runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json", true); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json\n\n", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.JSON", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json/", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.log", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.patch", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.js", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/.json", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/.log", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/suite.log", LAB_P_GATE, "runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/suite.log", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/suite.json", LAB_P_GATE, "runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/suite.json", true); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/suite.patch", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/suite.LOG", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/suite.log\n", LAB_P_GATE, "runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/suite.log", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/suite.log?x=1", LAB_P_GATE, "runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/suite.log", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/suite", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/suite.", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/seal-currency.log", LAB_P_GATE, "runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/seal-currency.log", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/specs-generate.json", LAB_P_GATE, "runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/specs-generate.json", true); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/seals.json", LAB_P_GATE, "runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/seals.json", true); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/seals.patch", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Suite.log", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/suite2.log", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/suite_x.log", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/a.b.log", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/a/b.log", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/../x.log", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/../../x.log", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/-.log", LAB_P_GATE, "runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/-.log", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/--.log", LAB_P_GATE, "runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/--.log", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa//x.log", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/x.log.log", LAB_P_NONE, "", false); + assert lab_test_path("/seals/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json", LAB_P_SEAL, "runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/seals.json", true); + assert lab_test_path("/seals/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.patch", LAB_P_SEAL, "runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/seals.patch", false); + assert lab_test_path("/seals/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.log", LAB_P_NONE, "", false); + assert lab_test_path("/seals/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json\n", LAB_P_SEAL, "runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/seals.json", true); + assert lab_test_path("/seals/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.patch?x", LAB_P_SEAL, "runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/seals.patch", false); + assert lab_test_path("/seals/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", LAB_P_NONE, "", false); + assert lab_test_path("/seals/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.", LAB_P_NONE, "", false); + assert lab_test_path("/seals/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/x.json", LAB_P_NONE, "", false); + assert lab_test_path("/seals/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.JSON", LAB_P_NONE, "", false); + assert lab_test_path("/seals/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.patch\n\n", LAB_P_NONE, "", false); + assert lab_test_path("/RUNS/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json", LAB_P_NONE, "", false); + assert lab_test_path("/Runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json", LAB_P_NONE, "", false); + assert lab_test_path("//runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json", LAB_P_NONE, "", false); + assert lab_test_path("runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json", LAB_P_NONE, "", false); + assert lab_test_path(" /runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json ", LAB_P_NONE, "", false); + assert lab_test_path("/runs/0123456789abcdef0123456789abcdef01234567.json", LAB_P_RUN, "runs/0123456789abcdef0123456789abcdef01234567.json", true); + assert lab_test_path("/runs/0123456789abcdef0123456789abcdef01234567.json?x", LAB_P_RUN, "runs/0123456789abcdef0123456789abcdef01234567.json", true); + assert lab_test_path("/runs/0123456789abcdef0123456789abcdef01234567.json\n", LAB_P_RUN, "runs/0123456789abcdef0123456789abcdef01234567.json", true); + assert lab_test_path("/runs/0123456789abcdef0123456789abcdef01234567.json\n\n", LAB_P_NONE, "", false); + assert lab_test_path("/runs/0123456789abcdef0123456789abcdef01234567.JSON", LAB_P_NONE, "", false); + assert lab_test_path("/runs/0123456789abcdef0123456789abcdef01234567.json/", LAB_P_NONE, "", false); + assert lab_test_path("/runs/0123456789abcdef0123456789abcdef01234567/suite.log", LAB_P_GATE, "runs/0123456789abcdef0123456789abcdef01234567/suite.log", false); + assert lab_test_path("/seals/0123456789abcdef0123456789abcdef01234567.json", LAB_P_SEAL, "runs/0123456789abcdef0123456789abcdef01234567/seals.json", true); + assert lab_test_path("/seals/0123456789abcdef0123456789abcdef01234567.json\n", LAB_P_SEAL, "runs/0123456789abcdef0123456789abcdef01234567/seals.json", true); + assert lab_test_path("/runs/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.json", LAB_P_NONE, "", false); + assert lab_test_path("/runs/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.json?x", LAB_P_NONE, "", false); + assert lab_test_path("/runs/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.json\n", LAB_P_NONE, "", false); + assert lab_test_path("/runs/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.json\n\n", LAB_P_NONE, "", false); + assert lab_test_path("/runs/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.JSON", LAB_P_NONE, "", false); + assert lab_test_path("/runs/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.json/", LAB_P_NONE, "", false); + assert lab_test_path("/runs/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/suite.log", LAB_P_NONE, "", false); + assert lab_test_path("/seals/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.json", LAB_P_NONE, "", false); + assert lab_test_path("/seals/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.json\n", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json?x", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json\n", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json\n\n", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.JSON", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json/", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/suite.log", LAB_P_NONE, "", false); + assert lab_test_path("/seals/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json", LAB_P_NONE, "", false); + assert lab_test_path("/seals/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json\n", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json?x", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json\n", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json\n\n", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.JSON", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json/", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/suite.log", LAB_P_NONE, "", false); + assert lab_test_path("/seals/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json", LAB_P_NONE, "", false); + assert lab_test_path("/seals/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json\n", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.log", LAB_P_GATE, "runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.log", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.json", LAB_P_GATE, "runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.json", true); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.txt", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.log", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.json", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.txt", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-b.log", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-b.json", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-b.txt", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/z.log", LAB_P_GATE, "runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/z.log", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/z.json", LAB_P_GATE, "runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/z.json", true); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/z.txt", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/build.log", LAB_P_GATE, "runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/build.log", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/build.json", LAB_P_GATE, "runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/build.json", true); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/build.txt", LAB_P_NONE, "", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/misread.log", LAB_P_GATE, "runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/misread.log", false); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/misread.json", LAB_P_GATE, "runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/misread.json", true); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/misread.txt", LAB_P_NONE, "", false); +} + +// >>> pinned table: paths_routes +// <<< pinned table: paths_http +// do_GET: /health and /health/ whole, / the banner, a route (its file may still be missing: 404), else 404. +test lab_py_table_routing { + assert lab_route("/health") == LAB_R_HEALTH; + assert lab_route("/health/") == LAB_R_HEALTH; + assert lab_route("/health//") == LAB_R_NOT_FOUND; + assert lab_route("/health?x") == LAB_R_NOT_FOUND; + assert lab_route("/health/?x") == LAB_R_NOT_FOUND; + assert lab_route("/Health") == LAB_R_NOT_FOUND; + assert lab_route("/healthz") == LAB_R_NOT_FOUND; + assert lab_route("/heal") == LAB_R_NOT_FOUND; + assert lab_route("health") == LAB_R_NOT_FOUND; + assert lab_route("/") == LAB_R_ROOT; + assert lab_route("//") == LAB_R_NOT_FOUND; + assert lab_route("/?") == LAB_R_NOT_FOUND; + assert lab_route("/?x=1") == LAB_R_NOT_FOUND; + assert lab_route("/ ") == LAB_R_NOT_FOUND; + assert lab_route("/index.html") == LAB_R_NOT_FOUND; + assert lab_route("/latest.json") == LAB_R_FILE; + assert lab_route("/latest.json?x") == LAB_R_FILE; + assert lab_route("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json") == LAB_R_FILE; + assert lab_route("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/suite.log") == LAB_R_FILE; + assert lab_route("/seals/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.patch") == LAB_R_FILE; + assert lab_route("/seals/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.log") == LAB_R_NOT_FOUND; + assert lab_route("/runs/") == LAB_R_NOT_FOUND; + assert lab_route("") == LAB_R_NOT_FOUND; + assert lab_route("/a") == LAB_R_NOT_FOUND; + assert lab_route("/runs/x.json") == LAB_R_NOT_FOUND; + assert lab_route("/health\n") == LAB_R_NOT_FOUND; + assert lab_route("/\n") == LAB_R_NOT_FOUND; + assert lab_route("/health/\n") == LAB_R_NOT_FOUND; +} + +// >>> pinned table: paths_http + +// Scenario (the oracle is the specification): Python's `$` lets ONE newline follow a match. A sha, a branch and a route all +// accept it and a second one is no match; /latest.json is compared whole, so it takes none; the route written carries no newline. +test the_one_newline_a_dollar_lets_through { + assert lab_test_sha("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n") == true; + assert lab_test_sha("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n\n") == false; + assert lab_test_branch("master\n") == true; + assert lab_test_branch("master\n\n") == false; + assert lab_test_branch("\n") == false; + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json\n", LAB_P_RUN, "runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json", true); + assert lab_test_path("/runs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json\n\n", LAB_P_NONE, "", false); + assert lab_test_path("/latest.json\n", LAB_P_NONE, "", false); + assert lab_dollar_end("ab\n", 0, 3) == 2; + assert lab_dollar_end("ab\n\n", 0, 4) == 3; + assert lab_dollar_end("ab", 0, 2) == 2; + assert lab_dollar_end("", 0, 0) == 0; +} + +// Scenario: the parts of a route are offsets into the path, and the query is cut off before anything is read. +test the_parts_of_a_route_are_offsets_into_the_path { + const g : string = "/runs/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/suite.log?x=1"; + const p : LabPathParts = lab_path_parts(g); + assert p.kind == LAB_P_GATE and p.sha_from == 6 and p.name_from == 47 and p.name_to == 52 and p.ext_from == 53 and p.ext_to == 56; + const s : LabPathParts = lab_path_parts("/seals/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.patch"); + assert s.kind == LAB_P_SEAL and s.sha_from == 7 and s.ext_from == 48 and s.ext_to == 53; + const l : LabPathParts = lab_path_parts("/latest.json?x"); + assert l.kind == LAB_P_LATEST and l.ext_from == 8 and l.ext_to == 12; + assert lab_path_parts("/latest.json?x").kind == lab_path_parts("/latest.json").kind; + assert lab_path_parts("/runs/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.json?/../x").kind == LAB_P_RUN; + assert lab_path_parts("/runs/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/../x.log").kind == LAB_P_NONE; + assert LAB_SHA_LEN == 40 and LAB_BRANCH_MAX == 200 and LAB_GATE_MAX == 40; +} diff --git a/specs/lab/plan.t27 b/specs/lab/plan.t27 new file mode 100644 index 0000000000..a2b0fe391a --- /dev/null +++ b/specs/lab/plan.t27 @@ -0,0 +1,980 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/lab/plan.t27 -- which commits the t27c lab runs next, decided from `git ls-remote` (gHashTag/t27#11041, epic #11040) +; The t27c lab (infra/t27c-lab/lab.py) polls the repository's heads, keeps the branches its T27_WATCH globs match, and queues +; every commit it has not run and has not queued, one entry per commit with all the branches that point at it. This file is +; that decision as pure functions of text, one per Python function, the Python being the oracle until the cutover: +; watched(branch, globs) lab_watched the index of the first glob a branch matches (fnmatch.fnmatchcase) +; parse_heads(ls_remote) lab_parse_heads `git ls-remote --heads` text -> the heads, as text +; plan(heads, globs, done, queued) lab_plan the new work, in glob order then branch name +; stale_dropped(queue, heads) lab_stale_dropped the queue minus commits no watched branch points at, unless asked for +; "master" in item["branches"] lab_item_has_branch (run_item(): whether the seal refresh of a master commit is due) +; It names no host: the plan of the next gates is the same wherever they run (one lab, or any node that takes a lab job, #11043). +; THE TEXTS. A dict, a set and a list are text here, so a caller that holds none of Python's types (the runtime of #11040 +; is generated from specs) passes what it read: +; globs the T27_WATCH text itself: globs separated by `,`, each stripped, an empty one dropped (the lab builds its list +; that way, so a list the lab can hold is a text this reads; a glob has no comma in it). Its index is the position +; among the non-empty ones. +; heads one head per line, ` TAB `, in the order a dict holds them, a branch on one line only (a dict has a key +; once). lab_parse_heads writes it; a branch name has no TAB or line break (git refuses them), so the text round-trips. +; done, queued a set of shas, one per line. A sha is in the set when a line is byte for byte that sha. +; queue one entry per line, `` or ` TAB asked` (an entry someone asked for by hand). +; plan one item per line, ` TAB TAB ...`, the branches in order. +; The writers return the new cursor of the caller's buffer and count a byte that does not fit without writing it. The +; plan is LAB_NO when a glob of the watch text is one this port refuses (below). +; FNMATCH. `fnmatch.fnmatchcase(name, glob)` is read off Python 3.14's translate(): `*` matches any run of characters +; (`/` and a line break too), `?` one character, `[seq]` one of the characters of seq, `[!seq]` any other, `a-z` a range, +; a `]` first in seq is itself, a `-` first or last is itself, an unclosed `[` is itself, and every other character is +; itself. A character is a code point, not a byte. Not ported: a range that runs backwards (`[z-a]`). Python turns it into +; a class that drops both ends, and a second range after it can change which ends; the lab's globs (`master`, +; `claude/t27c-*`) have none, so a glob with one is REFUSED (lab_fnmatch returns LAB_FN_REFUSED, lab_watched LAB_WATCH_BAD, +; lab_plan LAB_NO) rather than answered differently from Python. A refused glob is a configuration error to report. +; DICT ORDER AND DUPLICATES. A dict keeps a key where it first appeared and the value that came last, so a branch listed +; twice by ls-remote (git does not do it) is read once, in its first place, with its last sha. plan() does not depend on the +; order of its heads, only on which branch points at which sha. +; ASCII only (L3) +; phi^2 + 1/phi^2 = 3 | TRINITY + +module LabPlan; + +use tri::lab::receipt::span_is; +use lab::text::LAB_NO; +use lab::text::lab_line_end; +use lab::text::lab_line_next; +use lab::text::lab_find_byte; +use lab::text::lab_strip_from; +use lab::text::lab_strip_to; +use lab::text::lab_char_len; +use lab::text::lab_char_val; +use lab::text::lab_same_span; +use lab::text::lab_set_has; +use lab::text::lab_span_cmp; +use lab::text::lab_lit_at; +use lab::text::lab_put_span; +use lab::text::lab_put_lit; +use lab::paths::lab_is_sha; + +/// What a glob says of a name. +pub const LAB_FN_NO : u8 = 0; +pub const LAB_FN_YES : u8 = 1; +pub const LAB_FN_REFUSED : u8 = 2; + +/// watched(): no glob matched. +pub const LAB_WATCH_NONE : usize = 4294967295; +/// watched(): a glob of the text is one this port refuses. +pub const LAB_WATCH_BAD : usize = 4294967294; + +/// The prefix of a branch's ref in `git ls-remote --heads`. +pub const LAB_HEADS_PREFIX : string = "refs/heads/"; + +/// Where the class that opens at pat[p] (a `[`) closes: the offset of its `]`, or LAB_NO when the `[` is a plain character. +pub fn lab_class_close(pat: string, p: usize, pt: usize) -> usize { + var j : usize = p + 1; + if (j < pt and pat[j] == 33) { j = j + 1; } + if (j < pt and pat[j] == 93) { j = j + 1; } + while (j < pt and pat[j] != 93) { + j = j + 1; + } + if (j >= pt) { return LAB_NO; } + return j; +} + +/// The offset where the characters of a class begin: past the `[` and a `!`. +pub fn lab_class_first(pat: string, p: usize) -> usize { + if (pat[p + 1] == 33) { return p + 2; } + return p + 1; +} + +/// Does the class pat[p..j] hold a range that runs backwards (the first end above the last)? +pub fn lab_class_backwards(pat: string, p: usize, j: usize) -> bool { + var k : usize = lab_class_first(pat, p); + while (k < j) { + const k1 : usize = k + lab_char_len(pat, k, j); + if (k1 + 1 < j and pat[k1] == 45) { + if (lab_char_val(pat, k, j) > lab_char_val(pat, k1 + 1, j)) { return true; } + k = k1 + 1 + lab_char_len(pat, k1 + 1, j); + } else { + k = k1; + } + } + return false; +} + +/// Does the class pat[p..j] take the character whose code point is c? (A class with no backwards range.) +pub fn lab_class_takes(pat: string, p: usize, j: usize, c: u32) -> bool { + var hit : bool = false; + var k : usize = lab_class_first(pat, p); + while (k < j) { + const c0 : u32 = lab_char_val(pat, k, j); + const k1 : usize = k + lab_char_len(pat, k, j); + if (k1 + 1 < j and pat[k1] == 45) { + const c1 : u32 = lab_char_val(pat, k1 + 1, j); + if (c >= c0 and c <= c1) { hit = true; } + k = k1 + 1 + lab_char_len(pat, k1 + 1, j); + } else { + if (c == c0) { hit = true; } + k = k1; + } + } + if (pat[p + 1] == 33) { return hit == false; } + return hit; +} + +/// Is every class of the glob pat[pf..pt] one this port reads? +pub fn lab_glob_ok(pat: string, pf: usize, pt: usize) -> bool { + var p : usize = pf; + while (p < pt) { + var step : usize = lab_char_len(pat, p, pt); + if (pat[p] == 91) { + const j : usize = lab_class_close(pat, p, pt); + if (j != LAB_NO) { + if (lab_class_backwards(pat, p, j)) { return false; } + step = j + 1 - p; + } + } + p = p + step; + } + return true; +} + +/// How many bytes of the glob the element at pat[p] takes when it matches the character at s[i]; 0 when it does not. +pub fn lab_glob_step(pat: string, p: usize, pt: usize, s: string, i: usize, st: usize) -> usize { + const c : u8 = pat[p]; + if (c == 63) { return 1; } + if (c == 91) { + const j : usize = lab_class_close(pat, p, pt); + if (j != LAB_NO) { + if (lab_class_takes(pat, p, j, lab_char_val(s, i, st))) { return j + 1 - p; } + return 0; + } + } + const n : usize = lab_char_len(pat, p, pt); + if (lab_char_len(s, i, st) == n and lab_same_span(pat, p, p + n, s, i, i + n)) { return n; } + return 0; +} + +/// Does the glob pat[pf..pt] match all of s[sf..st]? (A glob lab_glob_ok accepts.) `*` is tried at the earliest place first and +/// moved right only when what follows fails, which finds a match exactly when there is one. +pub fn lab_glob_match(pat: string, pf: usize, pt: usize, s: string, sf: usize, st: usize) -> bool { + var p : usize = pf; + var i : usize = sf; + var star : usize = LAB_NO; + var mark : usize = sf; + var state : u8 = 0; + while (state == 0) { + if (p < pt and pat[p] == 42) { + while (p < pt and pat[p] == 42) { + p = p + 1; + } + star = p; + mark = i; + } else { + var stepped : bool = false; + if (p < pt and i < st) { + const step : usize = lab_glob_step(pat, p, pt, s, i, st); + if (step > 0) { + p = p + step; + i = i + lab_char_len(s, i, st); + stepped = true; + } + } + if (stepped == false) { + if (p == pt and i == st) { + state = 1; + } else { + if (star != LAB_NO and mark < st) { + mark = mark + lab_char_len(s, mark, st); + i = mark; + p = star; + } else { + state = 2; + } + } + } + } + } + return state == 1; +} + +/// fnmatch.fnmatchcase(s[sf..st], pat[pf..pt]): YES, NO, or REFUSED for a glob this port does not read. +pub fn lab_fnmatch(pat: string, pf: usize, pt: usize, s: string, sf: usize, st: usize) -> u8 { + if (lab_glob_ok(pat, pf, pt) == false) { return LAB_FN_REFUSED; } + if (lab_glob_match(pat, pf, pt, s, sf, st)) { return LAB_FN_YES; } + return LAB_FN_NO; +} + +/// Does every glob of the watch text read? (None is refused.) +pub fn lab_watch_ok(watch: string) -> bool { + var s : usize = 0; + while (s <= watch.len) { + var e : usize = lab_find_byte(watch, s, watch.len, 44); + if (e == LAB_NO) { e = watch.len; } + const f : usize = lab_strip_from(watch, s, e); + const t : usize = lab_strip_to(watch, f, e); + if (f < t and lab_glob_ok(watch, f, t) == false) { return false; } + s = e + 1; + } + return true; +} + +/// watched(branch, globs): the index of the first glob the branch matches, LAB_WATCH_NONE when none does, LAB_WATCH_BAD when a +/// glob it had to try is refused. The globs are the watch text; the branch is name[nf..nt]. +pub fn lab_watched(name: string, nf: usize, nt: usize, watch: string) -> usize { + var idx : usize = 0; + var s : usize = 0; + while (s <= watch.len) { + var e : usize = lab_find_byte(watch, s, watch.len, 44); + if (e == LAB_NO) { e = watch.len; } + const f : usize = lab_strip_from(watch, s, e); + const t : usize = lab_strip_to(watch, f, e); + if (f < t) { + const r : u8 = lab_fnmatch(watch, f, t, name, nf, nt); + if (r == LAB_FN_YES) { return idx; } + if (r == LAB_FN_REFUSED) { return LAB_WATCH_BAD; } + idx = idx + 1; + } + s = e + 1; + } + return LAB_WATCH_NONE; +} + +/// The offset in `ls` of the branch of the line ls[s..e] when it is a head, LAB_NO when the line is not one: exactly one TAB, a +/// 40-hex sha before it, and `refs/heads/` after it. The sha is ls[s..branch - 12], the branch is ls[branch..e]. +pub fn lab_ls_branch(ls: string, s: usize, e: usize) -> usize { + const tab : usize = lab_find_byte(ls, s, e, 9); + if (tab == LAB_NO) { return LAB_NO; } + if (lab_find_byte(ls, tab + 1, e, 9) != LAB_NO) { return LAB_NO; } + if (lab_is_sha(ls, s, tab) == false) { return LAB_NO; } + if (lab_lit_at(ls, tab + 1, e, LAB_HEADS_PREFIX) == false) { return LAB_NO; } + return tab + 1 + LAB_HEADS_PREFIX.len; +} + +/// parse_heads(ls_remote): the heads of `git ls-remote --heads` text, one ` TAB ` line each, as a dict holds them +/// (a branch in its first place with its last sha). Writes into dst at w, clipped at cap; the new cursor. +pub fn lab_parse_heads(ls: string, dst: []u8, cap: usize, w: usize) -> usize { + var x : usize = w; + var s : usize = 0; + while (s < ls.len) { + const e : usize = lab_line_end(ls, s); + const b : usize = lab_ls_branch(ls, s, e); + if (b != LAB_NO) { + var first : bool = true; + var last : usize = s; + var le : usize = e; + var k : usize = 0; + while (k < ls.len) { + const ke : usize = lab_line_end(ls, k); + const kb : usize = lab_ls_branch(ls, k, ke); + if (kb != LAB_NO and lab_same_span(ls, kb, ke, ls, b, e)) { + if (k < s) { first = false; } + if (k >= s) { last = k; le = ke; } + } + k = lab_line_next(ls, ke); + } + if (first) { + const lb : usize = lab_ls_branch(ls, last, le); + x = lab_put_span(dst, cap, x, ls, b, e); + x = lab_put_lit(dst, cap, x, "\t"); + x = lab_put_span(dst, cap, x, ls, last, lb - 1 - LAB_HEADS_PREFIX.len); + x = lab_put_lit(dst, cap, x, "\n"); + } + } + s = lab_line_next(ls, e); + } + return x; +} + +/// The watch rank of the head on line heads[s..e] when it is new work: the branch is watched, its sha is neither done nor +/// queued. LAB_NO otherwise, or when the line has no TAB. +pub fn lab_head_rank(heads: string, s: usize, e: usize, watch: string, done: string, queued: string) -> usize { + const tab : usize = lab_find_byte(heads, s, e, 9); + if (tab == LAB_NO) { return LAB_NO; } + const rank : usize = lab_watched(heads, s, tab, watch); + if (rank == LAB_WATCH_NONE or rank == LAB_WATCH_BAD) { return LAB_NO; } + if (lab_set_has(done, heads, tab + 1, e) or lab_set_has(queued, heads, tab + 1, e)) { return LAB_NO; } + return rank; +} + +/// The lowest watch rank among the new heads that point at the sha heads[sf..st]; LAB_NO when none is new work. +pub fn lab_group_rank(heads: string, sf: usize, st: usize, watch: string, done: string, queued: string) -> usize { + var best : usize = LAB_NO; + var s : usize = 0; + while (s < heads.len) { + const e : usize = lab_line_end(heads, s); + const tab : usize = lab_find_byte(heads, s, e, 9); + if (tab != LAB_NO and lab_same_span(heads, tab + 1, e, heads, sf, st)) { + const r : usize = lab_head_rank(heads, s, e, watch, done, queued); + if (r != LAB_NO and (best == LAB_NO or r < best)) { best = r; } + } + s = lab_line_next(heads, e); + } + return best; +} + +/// Is the head on line heads[s..e] (new work) the one whose branch sorts first among the new heads of its sha? +pub fn lab_group_first(heads: string, s: usize, e: usize, watch: string, done: string, queued: string) -> bool { + const tab : usize = lab_find_byte(heads, s, e, 9); + var k : usize = 0; + while (k < heads.len) { + const ke : usize = lab_line_end(heads, k); + const kt : usize = lab_find_byte(heads, k, ke, 9); + if (kt != LAB_NO and k != s and lab_same_span(heads, kt + 1, ke, heads, tab + 1, e)) { + if (lab_head_rank(heads, k, ke, watch, done, queued) != LAB_NO and lab_span_cmp(heads, k, kt, heads, s, tab) < 0) { return false; } + } + k = lab_line_next(heads, ke); + } + return true; +} + +/// Does item (rank a, first branch on line a) come before item (rank b, line b) in the plan: the lower rank, then the lower branch? +pub fn lab_item_before(heads: string, a_rank: usize, a_s: usize, b_rank: usize, b_s: usize) -> bool { + if (a_rank != b_rank) { return a_rank < b_rank; } + const a_t : usize = lab_find_byte(heads, a_s, heads.len, 9); + const b_t : usize = lab_find_byte(heads, b_s, heads.len, 9); + return lab_span_cmp(heads, a_s, a_t, heads, b_s, b_t) < 0; +} + +/// The line of the item that comes right after the one on line `last` (the first item when have_last is false): LAB_NO at the end. +pub fn lab_plan_next(heads: string, watch: string, done: string, queued: string, have_last: bool, last: usize) -> usize { + var last_rank : usize = 0; + if (have_last) { + const lt : usize = lab_find_byte(heads, last, heads.len, 9); + const le : usize = lab_line_end(heads, last); + last_rank = lab_group_rank(heads, lt + 1, le, watch, done, queued); + } + var best : usize = LAB_NO; + var best_rank : usize = 0; + var s : usize = 0; + while (s < heads.len) { + const e : usize = lab_line_end(heads, s); + if (lab_head_rank(heads, s, e, watch, done, queued) != LAB_NO and lab_group_first(heads, s, e, watch, done, queued)) { + const tab : usize = lab_find_byte(heads, s, e, 9); + const rank : usize = lab_group_rank(heads, tab + 1, e, watch, done, queued); + var later : bool = true; + if (have_last) { later = lab_item_before(heads, last_rank, last, rank, s); } + if (later and (best == LAB_NO or lab_item_before(heads, rank, s, best_rank, best))) { + best = s; + best_rank = rank; + } + } + s = lab_line_next(heads, e); + } + return best; +} + +/// The line of the branch of the item (the sha heads[sf..st]) that follows the one on line `prev`, in branch order: LAB_NO at the end. +pub fn lab_item_branch_next(heads: string, sf: usize, st: usize, watch: string, done: string, queued: string, have_prev: bool, prev: usize) -> usize { + var prev_t : usize = 0; + if (have_prev) { prev_t = lab_find_byte(heads, prev, heads.len, 9); } + var best : usize = LAB_NO; + var best_t : usize = 0; + var s : usize = 0; + while (s < heads.len) { + const e : usize = lab_line_end(heads, s); + const tab : usize = lab_find_byte(heads, s, e, 9); + if (tab != LAB_NO and lab_same_span(heads, tab + 1, e, heads, sf, st) and lab_head_rank(heads, s, e, watch, done, queued) != LAB_NO) { + var after : bool = true; + if (have_prev) { after = lab_span_cmp(heads, prev, prev_t, heads, s, tab) < 0; } + if (after and (best == LAB_NO or lab_span_cmp(heads, s, tab, heads, best, best_t) < 0)) { + best = s; + best_t = tab; + } + } + s = lab_line_next(heads, e); + } + return best; +} + +/// plan(heads, globs, done, queued): one line per new commit, ` TAB TAB ...`, in watch order and then branch +/// order, into dst at w, clipped at cap; the new cursor. LAB_NO when the watch text has a glob this port refuses. +pub fn lab_plan(heads: string, watch: string, done: string, queued: string, dst: []u8, cap: usize, w: usize) -> usize { + if (lab_watch_ok(watch) == false) { return LAB_NO; } + var x : usize = w; + var have : bool = false; + var cur : usize = 0; + while (true) { + const s : usize = lab_plan_next(heads, watch, done, queued, have, cur); + if (s == LAB_NO) { return x; } + const e : usize = lab_line_end(heads, s); + const tab : usize = lab_find_byte(heads, s, e, 9); + x = lab_put_span(dst, cap, x, heads, tab + 1, e); + var have_b : bool = false; + var b : usize = 0; + while (true) { + const nb : usize = lab_item_branch_next(heads, tab + 1, e, watch, done, queued, have_b, b); + if (nb == LAB_NO) { break; } + const nt : usize = lab_find_byte(heads, nb, heads.len, 9); + x = lab_put_lit(dst, cap, x, "\t"); + x = lab_put_span(dst, cap, x, heads, nb, nt); + have_b = true; + b = nb; + } + x = lab_put_lit(dst, cap, x, "\n"); + have = true; + cur = s; + } + return x; +} + +/// run_item(): `"master" in item["branches"]` for a plan line item[s..e] (` TAB TAB ...`): is `branch` one of +/// the branches after the sha? +pub fn lab_item_has_branch(item: string, s: usize, e: usize, branch: string) -> bool { + var f : usize = lab_find_byte(item, s, e, 9); + while (f != LAB_NO and f < e) { + var t : usize = lab_find_byte(item, f + 1, e, 9); + if (t == LAB_NO) { t = e; } + if (span_is(item, f + 1, t, branch)) { return true; } + f = lab_find_byte(item, t, e, 9); + } + return false; +} + +/// stale_dropped(queue, heads): the queue's lines whose sha some head points at, or that were asked for by hand (` TAB asked`), +/// in order, one per line, into dst at w, clipped at cap; the new cursor. +pub fn lab_stale_dropped(queue: string, heads: string, dst: []u8, cap: usize, w: usize) -> usize { + var x : usize = w; + var s : usize = 0; + while (s < queue.len) { + const e : usize = lab_line_end(queue, s); + var sha_to : usize = lab_find_byte(queue, s, e, 9); + var asked : bool = false; + if (sha_to == LAB_NO) { + sha_to = e; + } else { + asked = span_is(queue, sha_to + 1, e, "asked"); + } + var live : bool = false; + var h : usize = 0; + while (h < heads.len and live == false) { + const he : usize = lab_line_end(heads, h); + const ht : usize = lab_find_byte(heads, h, he, 9); + if (ht != LAB_NO and lab_same_span(heads, ht + 1, he, queue, s, sha_to)) { live = true; } + h = lab_line_next(heads, he); + } + if (asked or live) { + x = lab_put_span(dst, cap, x, queue, s, e); + x = lab_put_lit(dst, cap, x, "\n"); + } + s = lab_line_next(queue, e); + } + return x; +} + +// ---- tests ---- +// The oracle is infra/t27c-lab/lab.py. Every case below is its answer (Python 3.14.6), pinned: first the asserts of its +// self_check(), one to one, then tables of inputs run through its own functions (fnmatch.fnmatchcase as `watched` calls it, +// parse_heads, plan, stale_dropped). + +/// The names the fnmatch rows are read against, one per line (the empty one is the empty line). +const LAB_TEST_NAMES : string = "master\nmain\nclaude/t27c-x\nclaude/t27c-\nclaude/lab\nclaude/a-b\nclaude/gen-zig-discard-call\na\nab\nabc\naxb\naxxb\nb\nx\nxx\n\n-\n]\n[\n[a\na]\n^\n\\\na\\b\nt27\nt270\nt27c\na/b/c\na/b\n/\n//\n a\na \n!\n!a\na-b\nc-e\ne\nd"; + +/// One glob against every name of LAB_TEST_NAMES: the answers as a row of 1 (match) and 0 (no match). +fn lab_test_fn_row(pat: string, want: string) -> bool { + var s : usize = 0; + var k : usize = 0; + while (s < LAB_TEST_NAMES.len) { + const e : usize = lab_line_end(LAB_TEST_NAMES, s); + const r : u8 = lab_fnmatch(pat, 0, pat.len, LAB_TEST_NAMES, s, e); + if (k >= want.len) { return false; } + if (r == LAB_FN_YES and want[k] != 49) { return false; } + if (r == LAB_FN_NO and want[k] != 48) { return false; } + if (r == LAB_FN_REFUSED) { return false; } + k = k + 1; + s = lab_line_next(LAB_TEST_NAMES, e); + } + return k == want.len; +} + +/// One watch text against every name: the index of the glob each matches as a digit, n for none. +fn lab_test_watch_row(watch: string, want: string) -> bool { + var s : usize = 0; + var k : usize = 0; + while (s < LAB_TEST_NAMES.len) { + const e : usize = lab_line_end(LAB_TEST_NAMES, s); + const r : usize = lab_watched(LAB_TEST_NAMES, s, e, watch); + if (k >= want.len) { return false; } + if (r == LAB_WATCH_NONE and want[k] != 110) { return false; } + if (r < 10 and want[k] != 48 + (r as u8)) { return false; } + if (r != LAB_WATCH_NONE and r >= 10) { return false; } + k = k + 1; + s = lab_line_next(LAB_TEST_NAMES, e); + } + return k == want.len; +} + +fn lab_test_watched(name: string, watch: string) -> usize { + return lab_watched(name, 0, name.len, watch); +} + +fn lab_test_heads(ls: string, want: string) -> bool { + var out : [2048]u8 = undefined; + const w : usize = lab_parse_heads(ls, &out, 2048, 0); + return w == want.len and span_is(&out, 0, w, want); +} + +fn lab_test_plan(heads: string, watch: string, done: string, queued: string, want: string) -> bool { + var out : [2048]u8 = undefined; + const w : usize = lab_plan(heads, watch, done, queued, &out, 2048, 0); + return w == want.len and span_is(&out, 0, w, want); +} + +fn lab_test_item_has(item: string, branch: string, want: bool) -> bool { + return lab_item_has_branch(item, 0, item.len, branch) == want; +} + +fn lab_test_stale(queue: string, heads: string, want: string) -> bool { + var out : [2048]u8 = undefined; + const w : usize = lab_stale_dropped(queue, heads, &out, 2048, 0); + return w == want.len and span_is(&out, 0, w, want); +} + +// <<< pinned table: plan_self_check +test lab_py_self_check_watched_is_the_index_of_the_first_glob { + assert lab_test_watched("master", "master,claude/t27c-*") == 0; + assert lab_test_watched("claude/t27c-match-tail-expr", "master,claude/t27c-*") == 1; + assert lab_test_watched("claude/gen-zig-discard-call", "master,claude/t27c-*") == LAB_WATCH_NONE; + assert lab_test_watched("refs/pull/1/head", "master,claude/t27c-*") == LAB_WATCH_NONE; +} + +test lab_py_self_check_parse_heads_keeps_heads_with_a_sha_only { + assert lab_test_heads("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\trefs/heads/master\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\trefs/heads/claude/t27c-x\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\trefs/pull/9/head\nnot-a-sha\trefs/heads/y\n", "master\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nclaude/t27c-x\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n"); +} + +test lab_py_self_check_plan_groups_by_sha_in_glob_order { + assert lab_test_plan("claude/t27c-x\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nclaude/t27c-y\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nmaster\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", "master,claude/t27c-*", "", "", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\tmaster\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tclaude/t27c-x\tclaude/t27c-y\n"); + assert lab_test_plan("master\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nclaude/t27c-x\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", "master,claude/t27c-*", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", ""); +} + +test lab_py_self_check_stale_dropped_keeps_live_and_asked_entries { + assert lab_test_stale("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ncccccccccccccccccccccccccccccccccccccccc\ncccccccccccccccccccccccccccccccccccccccc\tasked\n", "master\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ncccccccccccccccccccccccccccccccccccccccc\tasked\n"); +} + +// >>> pinned table: plan_self_check +// <<< pinned table: plan_fnmatch +// fnmatch.fnmatchcase(name, glob) for every name of LAB_TEST_NAMES, one row per glob (1 = match, 0 = none). +test lab_py_table_fnmatch_rows { + assert lab_test_fn_row("master", "100000000000000000000000000000000000000"); + assert lab_test_fn_row("claude/t27c-*", "001100000000000000000000000000000000000"); + assert lab_test_fn_row("*", "111111111111111111111111111111111111111"); + assert lab_test_fn_row("**", "111111111111111111111111111111111111111"); + assert lab_test_fn_row("?", "000000010000110011100110000001000100011"); + assert lab_test_fn_row("??", "000000001000001000011000000000111010000"); + assert lab_test_fn_row("m?ster", "100000000000000000000000000000000000000"); + assert lab_test_fn_row("?aster", "100000000000000000000000000000000000000"); + assert lab_test_fn_row("[abc]*", "001111111111100000001001000110001001100"); + assert lab_test_fn_row("[!abc]*", "110000000000011011110110111001110110011"); + assert lab_test_fn_row("[a-c]", "000000010000100000000000000000000000000"); + assert lab_test_fn_row("[a-]", "000000010000000010000000000000000000000"); + assert lab_test_fn_row("[a-]x", "000000000000000000000000000000000000000"); + assert lab_test_fn_row("[]a]", "000000010000000001000000000000000000000"); + assert lab_test_fn_row("[]", "000000000000000000000000000000000000000"); + assert lab_test_fn_row("[!]", "000000000000000000000000000000000000000"); + assert lab_test_fn_row("a[", "000000000000000000000000000000000000000"); + assert lab_test_fn_row("[a", "000000000000000000010000000000000000000"); + assert lab_test_fn_row("[[]", "000000000000000000100000000000000000000"); + assert lab_test_fn_row("[[a]", "000000010000000000100000000000000000000"); + assert lab_test_fn_row("[a^]", "000000010000000000000100000000000000000"); + assert lab_test_fn_row("[!^]", "000000010000110011100010000001000100011"); + assert lab_test_fn_row("[*]", "000000000000000000000000000000000000000"); + assert lab_test_fn_row("[?]", "000000000000000000000000000000000000000"); + assert lab_test_fn_row("[a-ce]", "000000010000100000000000000000000000010"); + assert lab_test_fn_row("[--/]", "000000000000000010000000000001000000000"); + assert lab_test_fn_row("[!-a]", "000000000000110001100110000001000100011"); + assert lab_test_fn_row("[a-a]", "000000010000000000000000000000000000000"); + assert lab_test_fn_row("t27[0-9]*", "000000000000000000000000010000000000000"); + assert lab_test_fn_row("t27[0-9]", "000000000000000000000000010000000000000"); + assert lab_test_fn_row("t27[a-z]", "000000000000000000000000001000000000000"); + assert lab_test_fn_row("claude/[a-z]*-*", "001101100000000000000000000000000000000"); + assert lab_test_fn_row("*/*", "001111100000000000000000000111100000000"); + assert lab_test_fn_row("a*b*c", "000000000100000000000000000100000000000"); + assert lab_test_fn_row("*a", "000000010000000000010000000000010010000"); + assert lab_test_fn_row("a*", "000000011111000000001001000110001001000"); + assert lab_test_fn_row("*b*", "000011001111100000000001000110000001000"); + assert lab_test_fn_row("a?b", "000000000010000000000001000010000001000"); + assert lab_test_fn_row("a*?b", "000000000011000000000001000010000001000"); + assert lab_test_fn_row("*?", "111111111111111011111111111111111111111"); + assert lab_test_fn_row("*?*", "111111111111111011111111111111111111111"); + assert lab_test_fn_row("x*x*x*", "000000000000000000000000000000000000000"); + assert lab_test_fn_row("*x*x*", "000000000001001000000000000000000000000"); + assert lab_test_fn_row("\\", "000000000000000000000010000000000000000"); + assert lab_test_fn_row("[\\]", "000000000000000000000010000000000000000"); + assert lab_test_fn_row("[\\a]", "000000010000000000000010000000000000000"); + assert lab_test_fn_row("[a-c\\]", "000000010000100000000010000000000000000"); + assert lab_test_fn_row("!", "000000000000000000000000000000000100000"); + assert lab_test_fn_row("[!]*", "000000000000000000000000000000000000000"); + assert lab_test_fn_row("[!!]", "000000010000110011100110000001000000011"); + assert lab_test_fn_row("[!a-c]*", "110000000000011011110110111001110110011"); + assert lab_test_fn_row("[a-c!]", "000000010000100000000000000000000100000"); + assert lab_test_fn_row("//", "000000000000000000000000000000100000000"); + assert lab_test_fn_row("*/", "000000000000000000000000000001100000000"); + assert lab_test_fn_row("?/?/?", "000000000000000000000000000100000000000"); + assert lab_test_fn_row("a/b/*", "000000000000000000000000000100000000000"); + assert lab_test_fn_row("[ab]*[bc]", "000000001111000000000001000110000001000"); + assert lab_test_fn_row("ab*", "000000001100000000000000000000000000000"); + assert lab_test_fn_row("a[b]", "000000001000000000000000000000000000000"); + assert lab_test_fn_row("a[b-b]", "000000001000000000000000000000000000000"); + assert lab_test_fn_row("-", "000000000000000010000000000000000000000"); + assert lab_test_fn_row("[-]", "000000000000000010000000000000000000000"); + assert lab_test_fn_row("[--]", "000000000000000010000000000000000000000"); + assert lab_test_fn_row("[---]", "000000000000000010000000000000000000000"); + assert lab_test_fn_row("[ae]", "000000010000000000000000000000000000010"); + assert lab_test_fn_row("[c-e]", "000000000000000000000000000000000000011"); + assert lab_test_fn_row("[ -a]", "000000010000000011100110000001000100000"); + assert lab_test_fn_row("[ ]a", "000000000000000000000000000000010000000"); + assert lab_test_fn_row("*!", "000000000000000000000000000000000100000"); + assert lab_test_fn_row("[]]", "000000000000000001000000000000000000000"); + assert lab_test_fn_row("[]-a]", "000000010000000001000100000000000000000"); + assert lab_test_fn_row("[]-]", "000000000000000011000000000000000000000"); + assert lab_test_fn_row("[a-b-c]", "000000010000100010000000000000000000000"); + assert lab_test_fn_row("[ab-c]", "000000010000100000000000000000000000000"); + assert lab_test_fn_row("[a-b]-[b-c]", "000000000000000000000000000000000001000"); + assert lab_test_fn_row("a[]b]", "000000001000000000001000000000000000000"); + assert lab_test_fn_row("[abc", "000000000000000000000000000000000000000"); + assert lab_test_fn_row("abc[", "000000000000000000000000000000000000000"); + assert lab_test_fn_row("a**", "000000011111000000001001000110001001000"); + assert lab_test_fn_row("**a", "000000010000000000010000000000010010000"); +} + +// >>> pinned table: plan_fnmatch +// <<< pinned table: plan_watched +// watched(name, globs) for every name of LAB_TEST_NAMES, one row per watch text (the index as a digit, n for none). +test lab_py_table_watched_rows { + assert lab_test_watch_row("master", "0nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn"); + assert lab_test_watch_row("master,claude/t27c-*", "0n11nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn"); + assert lab_test_watch_row(" master , claude/t27c-* ", "0n11nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn"); + assert lab_test_watch_row("claude/*,master", "1n00000nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn"); + assert lab_test_watch_row(",master", "0nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn"); + assert lab_test_watch_row("master,", "0nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn"); + assert lab_test_watch_row(",,", "nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn"); + assert lab_test_watch_row("", "nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn"); + assert lab_test_watch_row(" ", "nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn"); + assert lab_test_watch_row("a,b,c", "nnnnnnn0nnnn1nnnnnnnnnnnnnnnnnnnnnnnnnn"); + assert lab_test_watch_row("*", "000000000000000000000000000000000000000"); + assert lab_test_watch_row("*,master", "000000000000000000000000000000000000000"); + assert lab_test_watch_row("x,[a-c]*,?", "nn111111111110nn222n1221nnn112nn12n1122"); + assert lab_test_watch_row("claude/t27c-*,claude/*,*", "220011122222222222222222222222222222222"); + assert lab_test_watch_row("master, ,main,claude/*", "0122222nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn"); + assert lab_test_watch_row("master,main,claude/*,t27*,a*,b,x,xx,-,]", "012222244444567n89nn4nn433344nnn4nn4nnn"); + assert lab_test_watch_row("main , master", "10nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn"); + assert lab_test_watch_row("[abc]*,a*", "nn00000000000nnnnnnn0nn0nnn00nnn0nn00nn"); + assert lab_test_watch_row("a,,,b", "nnnnnnn0nnnn1nnnnnnnnnnnnnnnnnnnnnnnnnn"); + assert lab_test_watch_row(",a", "nnnnnnn0nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn"); + assert lab_test_watch_row("t27?,t27", "nnnnnnnnnnnnnnnnnnnnnnnn100nnnnnnnnnnnn"); + assert lab_test_watch_row("!*,*", "111111111111111111111111111111111001111"); + assert lab_test_watch_row("claude/t27c-,claude/t27c-*", "nn10nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn"); + assert lab_test_watch_row("e,d,c-e", "nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn201"); + assert lab_test_watch_row("a b", "nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn"); + assert lab_test_watch_row(" a ,a", "nnnnnnn0nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn"); + assert lab_test_watch_row("?,??,???", "nnnnnnn0122n001n000110022nnn20111012200"); + assert lab_test_watch_row("*/*,a/b", "nn00000nnnnnnnnnnnnnnnnnnnn0000nnnnnnnn"); +} + +// >>> pinned table: plan_watched +// <<< pinned table: plan_parse_heads +// parse_heads(ls_remote) for ls-remote text of every shape the lab can be given and some it cannot. +test lab_py_table_parse_heads { + assert lab_test_heads("", ""); + assert lab_test_heads("\n", ""); + assert lab_test_heads("\n\n\n", ""); + assert lab_test_heads("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\trefs/heads/master", "master\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"); + assert lab_test_heads("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\trefs/heads/master\n", "master\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"); + assert lab_test_heads("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\trefs/heads/master\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\trefs/heads/master\n", "master\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n"); + assert lab_test_heads("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\trefs/heads/master\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\trefs/heads/x\ncccccccccccccccccccccccccccccccccccccccc\trefs/heads/master\n", "master\tcccccccccccccccccccccccccccccccccccccccc\nx\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n"); + assert lab_test_heads("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\trefs/heads/x\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\trefs/heads/y\ncccccccccccccccccccccccccccccccccccccccc\trefs/heads/x\ndddddddddddddddddddddddddddddddddddddddd\trefs/heads/y\n", "x\tcccccccccccccccccccccccccccccccccccccccc\ny\tdddddddddddddddddddddddddddddddddddddddd\n"); + assert lab_test_heads("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\trefs/heads/\n", "\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"); + assert lab_test_heads("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\trefs/heads/\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\trefs/heads/\n", "\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n"); + assert lab_test_heads("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa refs/heads/master\n", ""); + assert lab_test_heads("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\t\trefs/heads/master\n", ""); + assert lab_test_heads("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\trefs/heads/master\textra\n", ""); + assert lab_test_heads("\trefs/heads/master\n", ""); + assert lab_test_heads("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", ""); + assert lab_test_heads("refs/heads/master\n", ""); + assert lab_test_heads("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\trefs/heads/master\n\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\trefs/heads/x\n\n", "master\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nx\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n"); + assert lab_test_heads("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\trefs/heads/master\n \nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\trefs/heads/x\n", "master\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nx\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n"); + assert lab_test_heads("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\tREFS/HEADS/master\n", ""); + assert lab_test_heads("cccccccccccccccccccccccccccccccccccccccc\tHEAD\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\trefs/pull/9/merge\n\trefs/pull/9/merge\nffffffffffffffffffffffffffffffffffffffff\trefs/remotes/origin/x\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\t", ""); + assert lab_test_heads("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\trefs/heads/master \n aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\trefs/heads/claude/t27c-x\ncccccccccccccccccccccccccccccccccccccccc\trefs/heads/master\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \trefs/pull/9/merge\ncccccccccccccccccccccccccccccccccccccccc\trefs/heads/master \n", "master\tcccccccccccccccccccccccccccccccccccccccc\nmaster \tcccccccccccccccccccccccccccccccccccccccc\n"); + assert lab_test_heads("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\trefs/heads/\ngggggggggggggggggggggggggggggggggggggggg\trefs/heads/claude/t27c-x\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\trefs/heads/y\n0123456789abcdef0123456789abcdef01234567\trefs/heads/a b\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\trefs/heads/refs/heads/x", "y\teeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\na b\t0123456789abcdef0123456789abcdef01234567\nrefs/heads/x\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"); + assert lab_test_heads("ffffffffffffffffffffffffffffffffffffffff\trefs/heads/a b\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\tREFS/HEADS/x\ncccccccccccccccccccccccccccccccccccccccc\trefs/heads/..\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \trefs/head/x\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\trefs/pull/9/merge\n", "a b\tffffffffffffffffffffffffffffffffffffffff\n..\tcccccccccccccccccccccccccccccccccccccccc\n"); + assert lab_test_heads("not-a-sha\theads/master\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\t\n", ""); + assert lab_test_heads("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\trefs/remotes/origin/x\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\tHEAD\n", ""); + assert lab_test_heads("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\tHEAD\ncccccccccccccccccccccccccccccccccccccccc\theads/master\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\tHEAD\nffffffffffffffffffffffffffffffffffffffff\t\n", ""); + assert lab_test_heads("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\trefs/heads/claude/t27c-x\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tHEAD\nffffffffffffffffffffffffffffffffffffffff\trefs/heads/\ndddddddddddddddddddddddddddddddddddddddd\trefs/heads/feature/x-1\n0123456789abcdef0123456789abcdef01234567\trefs/heads/master\n", "claude/t27c-x\teeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\n\tffffffffffffffffffffffffffffffffffffffff\nfeature/x-1\tdddddddddddddddddddddddddddddddddddddddd\nmaster\t0123456789abcdef0123456789abcdef01234567\n"); + assert lab_test_heads("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\trefs/heads/master \ndddddddddddddddddddddddddddddddddddddddd\trefs/tags/v1\n\trefs/remotes/origin/x\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\trefs/head/x\nnot-a-sha\trefs/heads/refs/heads/x\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\tHEAD\n", "master \taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"); + assert lab_test_heads("dddddddddddddddddddddddddddddddddddddddd\trefs/heads/\ncccccccccccccccccccccccccccccccccccccccc\trefs/pull/9/merge\ndddddddddddddddddddddddddddddddddddddddd\trefs/heads/refs/heads/x\n", "\tdddddddddddddddddddddddddddddddddddddddd\nrefs/heads/x\tdddddddddddddddddddddddddddddddddddddddd\n"); + assert lab_test_heads("dddddddddddddddddddddddddddddddddddddddd\tHEAD\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\trefs/heads/a b\ncccccccccccccccccccccccccccccccccccccccc\tREFS/HEADS/x\n", "a b\teeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\n"); + assert lab_test_heads("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\trefs/heads/master \n", "master \teeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\n"); + assert lab_test_heads("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\trefs/heads/\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\trefs/head/x", "\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n"); + assert lab_test_heads("0123456789abcdef0123456789abcdef01234567\trefs/heads/master\n", "master\t0123456789abcdef0123456789abcdef01234567\n"); + assert lab_test_heads("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \trefs/heads/master \ncccccccccccccccccccccccccccccccccccccccc\trefs/heads/a\tb\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\trefs/heads/master \n", "master \teeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\n"); + assert lab_test_heads("cccccccccccccccccccccccccccccccccccccccc\theads/master\n0123456789abcdef0123456789abcdef01234567\trefs/tags/v1\ncccccccccccccccccccccccccccccccccccccccc\trefs/pull/9/head\n0123456789abcdef0123456789abcdef01234567\trefs/heads\n aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\t\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\trefs/heads/master \n", "master \tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n"); + assert lab_test_heads("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\trefs/heads/a b\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\t\ndddddddddddddddddddddddddddddddddddddddd\trefs/heads/\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tHEAD\nffffffffffffffffffffffffffffffffffffffff\t\n\theads/master", "a b\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n\tdddddddddddddddddddddddddddddddddddddddd\n"); + assert lab_test_heads("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\t\n0123456789abcdef0123456789abcdef01234567\t\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \trefs/tags/v1\n", ""); + assert lab_test_heads("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\trefs/heads/master \nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\trefs/pull/9/head\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\tHEAD\n0123456789abcdef0123456789abcdef01234567\trefs/heads/refs/heads/x\ndddddddddddddddddddddddddddddddddddddddd\trefs/pull/9/merge\n", "master \teeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\nrefs/heads/x\t0123456789abcdef0123456789abcdef01234567\n"); +} + +// >>> pinned table: plan_parse_heads +// <<< pinned table: plan_plan +// plan(heads, globs, done, queued): new work, one entry per sha, its branches sorted, in glob order then branch name. +test lab_py_table_plan { + assert lab_test_plan("master\tcccccccccccccccccccccccccccccccccccccccc\nclaude/lab\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nrefs/pull/1/head\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nclaude/t27c-a\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", "claude/*", "", "", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\tclaude/lab\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tclaude/t27c-a\n"); + assert lab_test_plan("master\tcccccccccccccccccccccccccccccccccccccccc\nrefs/pull/1/head\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nclaude/t27c-x\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nmain\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nclaude/t27c-a\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", "master", "dddddddddddddddddddddddddddddddddddddddd\n", "", "cccccccccccccccccccccccccccccccccccccccc\tmaster\n"); + assert lab_test_plan("", "claude/t27c-*,master", "cccccccccccccccccccccccccccccccccccccccc\ndddddddddddddddddddddddddddddddddddddddd\n", "dddddddddddddddddddddddddddddddddddddddd\n", ""); + assert lab_test_plan("main\tdddddddddddddddddddddddddddddddddddddddd\nclaude/t27c-x\tcccccccccccccccccccccccccccccccccccccccc\nrefs/pull/1/head\tcccccccccccccccccccccccccccccccccccccccc\nclaude/t27c-y\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nfeature/a\tcccccccccccccccccccccccccccccccccccccccc\n", "*", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ndddddddddddddddddddddddddddddddddddddddd\n", "", "cccccccccccccccccccccccccccccccccccccccc\tclaude/t27c-x\tfeature/a\trefs/pull/1/head\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tclaude/t27c-y\n"); + assert lab_test_plan("dev\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nfeature/a\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nclaude/lab\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nmain\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", "claude/t27c-*", "cccccccccccccccccccccccccccccccccccccccc\n", "", ""); + assert lab_test_plan("feature/a\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nclaude/t27c-x\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", "dev,feature/*", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\ndddddddddddddddddddddddddddddddddddddddd\n", "cccccccccccccccccccccccccccccccccccccccc\n", ""); + assert lab_test_plan("master\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nclaude/gen-zig\tdddddddddddddddddddddddddddddddddddddddd\nrefs/pull/1/head\tdddddddddddddddddddddddddddddddddddddddd\n", "[a-c]*,master", "", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", "dddddddddddddddddddddddddddddddddddddddd\tclaude/gen-zig\n"); + assert lab_test_plan("refs/pull/1/head\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nmaster\tdddddddddddddddddddddddddddddddddddddddd\ndev\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nclaude/t27c-y\tcccccccccccccccccccccccccccccccccccccccc\nmain\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nclaude/t27c-b\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", "claude/t27c-*,master", "cccccccccccccccccccccccccccccccccccccccc\n", "dddddddddddddddddddddddddddddddddddddddd\n", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tclaude/t27c-b\n"); + assert lab_test_plan("claude/t27c-y\tcccccccccccccccccccccccccccccccccccccccc\nclaude/t27c-a\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nrefs/pull/1/head\tcccccccccccccccccccccccccccccccccccccccc\n", "dev,feature/*", "", "cccccccccccccccccccccccccccccccccccccccc\n", ""); + assert lab_test_plan("main\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nclaude/t27c-y\tcccccccccccccccccccccccccccccccccccccccc\nmaster\tdddddddddddddddddddddddddddddddddddddddd\nclaude/t27c-x\tdddddddddddddddddddddddddddddddddddddddd\n", "[a-c]*,master", "dddddddddddddddddddddddddddddddddddddddd\n", "", "cccccccccccccccccccccccccccccccccccccccc\tclaude/t27c-y\n"); + assert lab_test_plan("claude/lab\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nclaude/t27c-b\tdddddddddddddddddddddddddddddddddddddddd\nmaster\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", "dev,feature/*", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ncccccccccccccccccccccccccccccccccccccccc\n", "", ""); + assert lab_test_plan("claude/t27c-b\tdddddddddddddddddddddddddddddddddddddddd\nrefs/pull/1/head\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ndev\tdddddddddddddddddddddddddddddddddddddddd\n", "claude/*", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", "dddddddddddddddddddddddddddddddddddddddd\tclaude/t27c-b\n"); + assert lab_test_plan("", "[a-c]*,master", "", "", ""); + assert lab_test_plan("", "master,main,claude/*", "cccccccccccccccccccccccccccccccccccccccc\n", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", ""); + assert lab_test_plan("", "master", "cccccccccccccccccccccccccccccccccccccccc\n", "", ""); + assert lab_test_plan("claude/t27c-b\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nclaude/lab\tdddddddddddddddddddddddddddddddddddddddd\nclaude/t27c-z\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", "*", "", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ndddddddddddddddddddddddddddddddddddddddd\n", ""); + assert lab_test_plan("claude/t27c-a\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nfeature/a\tcccccccccccccccccccccccccccccccccccccccc\nclaude/t27c-x\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nclaude/t27c-z\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nrefs/pull/1/head\tdddddddddddddddddddddddddddddddddddddddd\n", "dev,feature/*", "cccccccccccccccccccccccccccccccccccccccc\n", "", ""); + assert lab_test_plan("", "claude/t27c-*,master", "", "cccccccccccccccccccccccccccccccccccccccc\n", ""); + assert lab_test_plan("", "master,claude/t27c-*", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\ncccccccccccccccccccccccccccccccccccccccc\n", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", ""); + assert lab_test_plan("claude/lab\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", "master", "cccccccccccccccccccccccccccccccccccccccc\n", "", ""); + assert lab_test_plan("dev\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nclaude/lab\tdddddddddddddddddddddddddddddddddddddddd\n", "master,main,claude/*", "", "dddddddddddddddddddddddddddddddddddddddd\n", ""); + assert lab_test_plan("", "dev,feature/*", "dddddddddddddddddddddddddddddddddddddddd\n", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ndddddddddddddddddddddddddddddddddddddddd\n", ""); + assert lab_test_plan("claude/t27c-x\tcccccccccccccccccccccccccccccccccccccccc\nfeature/a\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nclaude/t27c-b\tdddddddddddddddddddddddddddddddddddddddd\nmaster\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nrefs/pull/1/head\tdddddddddddddddddddddddddddddddddddddddd\n", "claude/*", "", "", "dddddddddddddddddddddddddddddddddddddddd\tclaude/t27c-b\ncccccccccccccccccccccccccccccccccccccccc\tclaude/t27c-x\n"); + assert lab_test_plan("claude/t27c-z\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", "claude/*", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\ncccccccccccccccccccccccccccccccccccccccc\ndddddddddddddddddddddddddddddddddddddddd\n", "dddddddddddddddddddddddddddddddddddddddd\n", ""); + assert lab_test_plan("claude/t27c-z\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nclaude/t27c-y\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nmaster\tcccccccccccccccccccccccccccccccccccccccc\n", "master,claude/t27c-*", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ncccccccccccccccccccccccccccccccccccccccc\n", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ndddddddddddddddddddddddddddddddddddddddd\n", ""); + assert lab_test_plan("main\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nclaude/lab\tdddddddddddddddddddddddddddddddddddddddd\n", "dev,feature/*", "", "dddddddddddddddddddddddddddddddddddddddd\n", ""); + assert lab_test_plan("dev\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nclaude/t27c-z\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nclaude/t27c-b\tdddddddddddddddddddddddddddddddddddddddd\nclaude/lab\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nmaster\tcccccccccccccccccccccccccccccccccccccccc\nrefs/pull/1/head\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", "", "cccccccccccccccccccccccccccccccccccccccc\n", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\ndddddddddddddddddddddddddddddddddddddddd\n", ""); + assert lab_test_plan("claude/t27c-z\tdddddddddddddddddddddddddddddddddddddddd\nclaude/t27c-x\tcccccccccccccccccccccccccccccccccccccccc\nrefs/pull/1/head\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", "claude/t27c-*,master", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ndddddddddddddddddddddddddddddddddddddddd\n", "", "cccccccccccccccccccccccccccccccccccccccc\tclaude/t27c-x\n"); + assert lab_test_plan("claude/t27c-z\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\ndev\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nclaude/t27c-a\tdddddddddddddddddddddddddddddddddddddddd\nclaude/lab\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nmain\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nclaude/gen-zig\tcccccccccccccccccccccccccccccccccccccccc\n", "", "dddddddddddddddddddddddddddddddddddddddd\n", "", ""); + assert lab_test_plan("dev\tdddddddddddddddddddddddddddddddddddddddd\nclaude/t27c-z\tdddddddddddddddddddddddddddddddddddddddd\nclaude/lab\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nfeature/a\tdddddddddddddddddddddddddddddddddddddddd\nclaude/t27c-x\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", "claude/*", "", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", "dddddddddddddddddddddddddddddddddddddddd\tclaude/t27c-z\n"); + assert lab_test_plan("main\tdddddddddddddddddddddddddddddddddddddddd\nfeature/a\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ndev\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nclaude/t27c-x\tdddddddddddddddddddddddddddddddddddddddd\n", "*", "cccccccccccccccccccccccccccccccccccccccc\n", "", "dddddddddddddddddddddddddddddddddddddddd\tclaude/t27c-x\tmain\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tdev\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\tfeature/a\n"); + assert lab_test_plan("claude/gen-zig\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nmaster\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nmain\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nrefs/pull/1/head\tdddddddddddddddddddddddddddddddddddddddd\nclaude/t27c-z\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nclaude/t27c-a\tdddddddddddddddddddddddddddddddddddddddd\n", "claude/*", "", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\tclaude/gen-zig\ndddddddddddddddddddddddddddddddddddddddd\tclaude/t27c-a\n"); + assert lab_test_plan("claude/gen-zig\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nmaster\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nclaude/t27c-a\tcccccccccccccccccccccccccccccccccccccccc\nclaude/t27c-x\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\ndev\tcccccccccccccccccccccccccccccccccccccccc\n", "dev,feature/*", "", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", "cccccccccccccccccccccccccccccccccccccccc\tdev\n"); + assert lab_test_plan("claude/t27c-b\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nmaster\tdddddddddddddddddddddddddddddddddddddddd\nfeature/a\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nclaude/t27c-z\tcccccccccccccccccccccccccccccccccccccccc\nclaude/lab\tcccccccccccccccccccccccccccccccccccccccc\n", "master", "", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", "dddddddddddddddddddddddddddddddddddddddd\tmaster\n"); + assert lab_test_plan("claude/t27c-x\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nclaude/lab\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nclaude/t27c-a\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nclaude/t27c-y\tcccccccccccccccccccccccccccccccccccccccc\nfeature/a\tcccccccccccccccccccccccccccccccccccccccc\n", "dev,feature/*", "", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", "cccccccccccccccccccccccccccccccccccccccc\tfeature/a\n"); + assert lab_test_plan("refs/pull/1/head\tcccccccccccccccccccccccccccccccccccccccc\nclaude/t27c-z\tcccccccccccccccccccccccccccccccccccccccc\nmaster\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nclaude/t27c-a\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nclaude/t27c-x\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", "", "", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", ""); + assert lab_test_plan("", "master,claude/t27c-*", "cccccccccccccccccccccccccccccccccccccccc\n", "cccccccccccccccccccccccccccccccccccccccc\ndddddddddddddddddddddddddddddddddddddddd\n", ""); + assert lab_test_plan("dev\tdddddddddddddddddddddddddddddddddddddddd\nmain\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nclaude/t27c-y\tcccccccccccccccccccccccccccccccccccccccc\nfeature/a\tcccccccccccccccccccccccccccccccccccccccc\nmaster\tdddddddddddddddddddddddddddddddddddddddd\nclaude/t27c-a\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", "dev,feature/*", "", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", "dddddddddddddddddddddddddddddddddddddddd\tdev\ncccccccccccccccccccccccccccccccccccccccc\tfeature/a\n"); + assert lab_test_plan("feature/a\tdddddddddddddddddddddddddddddddddddddddd\nclaude/gen-zig\tcccccccccccccccccccccccccccccccccccccccc\nclaude/t27c-a\tdddddddddddddddddddddddddddddddddddddddd\n", "master,claude/t27c-*", "cccccccccccccccccccccccccccccccccccccccc\n", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", "dddddddddddddddddddddddddddddddddddddddd\tclaude/t27c-a\n"); + assert lab_test_plan("feature/a\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", "master,main,claude/*", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\ndddddddddddddddddddddddddddddddddddddddd\n", ""); + assert lab_test_plan("claude/t27c-y\tcccccccccccccccccccccccccccccccccccccccc\nclaude/t27c-a\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nclaude/t27c-b\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\ndev\tdddddddddddddddddddddddddddddddddddddddd\nclaude/gen-zig\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nclaude/t27c-z\tcccccccccccccccccccccccccccccccccccccccc\n", "*", "", "", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tclaude/gen-zig\tclaude/t27c-b\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\tclaude/t27c-a\ncccccccccccccccccccccccccccccccccccccccc\tclaude/t27c-y\tclaude/t27c-z\ndddddddddddddddddddddddddddddddddddddddd\tdev\n"); + assert lab_test_plan("claude/lab\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nrefs/pull/1/head\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\ndev\tdddddddddddddddddddddddddddddddddddddddd\nclaude/t27c-b\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", "dev,feature/*", "", "", "dddddddddddddddddddddddddddddddddddddddd\tdev\n"); + assert lab_test_plan("claude/t27c-x\tcccccccccccccccccccccccccccccccccccccccc\nmain\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nrefs/pull/1/head\tdddddddddddddddddddddddddddddddddddddddd\nclaude/t27c-y\tcccccccccccccccccccccccccccccccccccccccc\nclaude/t27c-b\tcccccccccccccccccccccccccccccccccccccccc\n", "claude/t27c-*,master", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", "cccccccccccccccccccccccccccccccccccccccc\tclaude/t27c-b\tclaude/t27c-x\tclaude/t27c-y\n"); + assert lab_test_plan("claude/t27c-z\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nclaude/t27c-y\tcccccccccccccccccccccccccccccccccccccccc\nclaude/t27c-b\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nmain\tdddddddddddddddddddddddddddddddddddddddd\nclaude/t27c-a\tcccccccccccccccccccccccccccccccccccccccc\n", "claude/t27c-*", "", "dddddddddddddddddddddddddddddddddddddddd\n", "cccccccccccccccccccccccccccccccccccccccc\tclaude/t27c-a\tclaude/t27c-y\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\tclaude/t27c-b\tclaude/t27c-z\n"); + assert lab_test_plan("refs/pull/1/head\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nclaude/t27c-z\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ndev\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nclaude/t27c-a\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", "master,main,claude/*", "cccccccccccccccccccccccccccccccccccccccc\n", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tclaude/t27c-a\n"); +} + +// >>> pinned table: plan_plan +// <<< pinned table: plan_stale +// stale_dropped(queue, heads): a queued commit survives when some watched head points at it or it was asked for by hand. +test lab_py_table_stale_dropped { + assert lab_test_stale("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tasked\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tasked\n", "claude/t27c-x\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nmaster\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nfeature/a\tdddddddddddddddddddddddddddddddddddddddd\n", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tasked\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tasked\n"); + assert lab_test_stale("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\ncccccccccccccccccccccccccccccccccccccccc\ndddddddddddddddddddddddddddddddddddddddd\tasked\n", "main\tdddddddddddddddddddddddddddddddddddddddd\nfeature/a\tcccccccccccccccccccccccccccccccccccccccc\ndev\tcccccccccccccccccccccccccccccccccccccccc\nrefs/pull/1/head\tcccccccccccccccccccccccccccccccccccccccc\n", "cccccccccccccccccccccccccccccccccccccccc\ndddddddddddddddddddddddddddddddddddddddd\tasked\n"); + assert lab_test_stale("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\ndddddddddddddddddddddddddddddddddddddddd\tasked\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tasked\n", "", "dddddddddddddddddddddddddddddddddddddddd\tasked\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tasked\n"); + assert lab_test_stale("cccccccccccccccccccccccccccccccccccccccc\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tasked\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\tasked\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", "claude/t27c-y\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nclaude/lab\tdddddddddddddddddddddddddddddddddddddddd\nrefs/pull/1/head\tcccccccccccccccccccccccccccccccccccccccc\nclaude/t27c-a\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", "cccccccccccccccccccccccccccccccccccccccc\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tasked\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\tasked\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"); + assert lab_test_stale("cccccccccccccccccccccccccccccccccccccccc\ndddddddddddddddddddddddddddddddddddddddd\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\tasked\ndddddddddddddddddddddddddddddddddddddddd\n", "claude/gen-zig\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nclaude/t27c-z\tdddddddddddddddddddddddddddddddddddddddd\nclaude/t27c-y\tcccccccccccccccccccccccccccccccccccccccc\ndev\tcccccccccccccccccccccccccccccccccccccccc\n", "cccccccccccccccccccccccccccccccccccccccc\ndddddddddddddddddddddddddddddddddddddddd\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\tasked\ndddddddddddddddddddddddddddddddddddddddd\n"); + assert lab_test_stale("cccccccccccccccccccccccccccccccccccccccc\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\ndddddddddddddddddddddddddddddddddddddddd\ndddddddddddddddddddddddddddddddddddddddd\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\ndddddddddddddddddddddddddddddddddddddddd\tasked\n", "claude/t27c-a\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nrefs/pull/1/head\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nclaude/t27c-b\tcccccccccccccccccccccccccccccccccccccccc\n", "cccccccccccccccccccccccccccccccccccccccc\ndddddddddddddddddddddddddddddddddddddddd\tasked\n"); + assert lab_test_stale("cccccccccccccccccccccccccccccccccccccccc\n", "claude/t27c-a\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nclaude/lab\tdddddddddddddddddddddddddddddddddddddddd\n", ""); + assert lab_test_stale("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\tasked\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\ncccccccccccccccccccccccccccccccccccccccc\tasked\n", "claude/gen-zig\tdddddddddddddddddddddddddddddddddddddddd\nfeature/a\tcccccccccccccccccccccccccccccccccccccccc\ndev\tcccccccccccccccccccccccccccccccccccccccc\n", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\tasked\ncccccccccccccccccccccccccccccccccccccccc\tasked\n"); + assert lab_test_stale("dddddddddddddddddddddddddddddddddddddddd\n", "refs/pull/1/head\tdddddddddddddddddddddddddddddddddddddddd\n", "dddddddddddddddddddddddddddddddddddddddd\n"); + assert lab_test_stale("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\ndddddddddddddddddddddddddddddddddddddddd\tasked\n", "dev\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nclaude/t27c-b\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\ndddddddddddddddddddddddddddddddddddddddd\tasked\n"); + assert lab_test_stale("cccccccccccccccccccccccccccccccccccccccc\n", "claude/t27c-b\tcccccccccccccccccccccccccccccccccccccccc\nclaude/t27c-a\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", "cccccccccccccccccccccccccccccccccccccccc\n"); + assert lab_test_stale("dddddddddddddddddddddddddddddddddddddddd\tasked\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\tasked\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tasked\ncccccccccccccccccccccccccccccccccccccccc\tasked\ndddddddddddddddddddddddddddddddddddddddd\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\n", "", "dddddddddddddddddddddddddddddddddddddddd\tasked\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\tasked\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tasked\ncccccccccccccccccccccccccccccccccccccccc\tasked\n"); + assert lab_test_stale("", "claude/t27c-y\tcccccccccccccccccccccccccccccccccccccccc\nclaude/t27c-b\tcccccccccccccccccccccccccccccccccccccccc\nmain\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", ""); + assert lab_test_stale("cccccccccccccccccccccccccccccccccccccccc\ndddddddddddddddddddddddddddddddddddddddd\n", "claude/gen-zig\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", ""); + assert lab_test_stale("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\tasked\n", "main\tdddddddddddddddddddddddddddddddddddddddd\n", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\tasked\n"); + assert lab_test_stale("dddddddddddddddddddddddddddddddddddddddd\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\tasked\n", "claude/gen-zig\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\tasked\n"); + assert lab_test_stale("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", "claude/t27c-z\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", ""); + assert lab_test_stale("dddddddddddddddddddddddddddddddddddddddd\n", "claude/t27c-z\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nclaude/gen-zig\tdddddddddddddddddddddddddddddddddddddddd\n", "dddddddddddddddddddddddddddddddddddddddd\n"); + assert lab_test_stale("cccccccccccccccccccccccccccccccccccccccc\ncccccccccccccccccccccccccccccccccccccccc\tasked\ndddddddddddddddddddddddddddddddddddddddd\n", "claude/t27c-a\tcccccccccccccccccccccccccccccccccccccccc\n", "cccccccccccccccccccccccccccccccccccccccc\ncccccccccccccccccccccccccccccccccccccccc\tasked\n"); + assert lab_test_stale("dddddddddddddddddddddddddddddddddddddddd\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\tasked\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\tasked\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", "master\tcccccccccccccccccccccccccccccccccccccccc\nclaude/t27c-a\tdddddddddddddddddddddddddddddddddddddddd\nclaude/t27c-x\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nclaude/t27c-b\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", "dddddddddddddddddddddddddddddddddddddddd\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\tasked\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\tasked\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n"); + assert lab_test_stale("dddddddddddddddddddddddddddddddddddddddd\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\tasked\ncccccccccccccccccccccccccccccccccccccccc\n", "claude/lab\tcccccccccccccccccccccccccccccccccccccccc\nrefs/pull/1/head\tcccccccccccccccccccccccccccccccccccccccc\nclaude/t27c-z\tcccccccccccccccccccccccccccccccccccccccc\nmaster\tdddddddddddddddddddddddddddddddddddddddd\n", "dddddddddddddddddddddddddddddddddddddddd\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\tasked\ncccccccccccccccccccccccccccccccccccccccc\n"); + assert lab_test_stale("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\n", "claude/t27c-y\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nfeature/a\tdddddddddddddddddddddddddddddddddddddddd\n", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n"); + assert lab_test_stale("dddddddddddddddddddddddddddddddddddddddd\ndddddddddddddddddddddddddddddddddddddddd\tasked\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\tasked\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", "dev\tdddddddddddddddddddddddddddddddddddddddd\n", "dddddddddddddddddddddddddddddddddddddddd\ndddddddddddddddddddddddddddddddddddddddd\tasked\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\tasked\n"); + assert lab_test_stale("cccccccccccccccccccccccccccccccccccccccc\ncccccccccccccccccccccccccccccccccccccccc\ncccccccccccccccccccccccccccccccccccccccc\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tasked\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\n", "", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tasked\n"); + assert lab_test_stale("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ncccccccccccccccccccccccccccccccccccccccc\tasked\ncccccccccccccccccccccccccccccccccccccccc\ndddddddddddddddddddddddddddddddddddddddd\ndddddddddddddddddddddddddddddddddddddddd\n", "dev\tcccccccccccccccccccccccccccccccccccccccc\n", "cccccccccccccccccccccccccccccccccccccccc\tasked\ncccccccccccccccccccccccccccccccccccccccc\n"); + assert lab_test_stale("cccccccccccccccccccccccccccccccccccccccc\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\tasked\n", "refs/pull/1/head\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nclaude/t27c-b\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nmaster\tcccccccccccccccccccccccccccccccccccccccc\nclaude/t27c-x\tdddddddddddddddddddddddddddddddddddddddd\n", "cccccccccccccccccccccccccccccccccccccccc\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\tasked\n"); + assert lab_test_stale("cccccccccccccccccccccccccccccccccccccccc\ncccccccccccccccccccccccccccccccccccccccc\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\tasked\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\n", "", "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\tasked\n"); + assert lab_test_stale("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\ncccccccccccccccccccccccccccccccccccccccc\tasked\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", "claude/lab\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nclaude/t27c-x\tcccccccccccccccccccccccccccccccccccccccc\n", "cccccccccccccccccccccccccccccccccccccccc\tasked\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n"); + assert lab_test_stale("cccccccccccccccccccccccccccccccccccccccc\tasked\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ndddddddddddddddddddddddddddddddddddddddd\n", "claude/t27c-x\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nclaude/t27c-y\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nmaster\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", "cccccccccccccccccccccccccccccccccccccccc\tasked\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"); + assert lab_test_stale("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\ndddddddddddddddddddddddddddddddddddddddd\tasked\ndddddddddddddddddddddddddddddddddddddddd\tasked\ndddddddddddddddddddddddddddddddddddddddd\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\n", "refs/pull/1/head\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", "dddddddddddddddddddddddddddddddddddddddd\tasked\ndddddddddddddddddddddddddddddddddddddddd\tasked\n"); + assert lab_test_stale("", "claude/lab\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ndev\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", ""); + assert lab_test_stale("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\ndddddddddddddddddddddddddddddddddddddddd\tasked\n", "", "dddddddddddddddddddddddddddddddddddddddd\tasked\n"); + assert lab_test_stale("dddddddddddddddddddddddddddddddddddddddd\tasked\ncccccccccccccccccccccccccccccccccccccccc\tasked\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\tasked\ncccccccccccccccccccccccccccccccccccccccc\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", "claude/lab\tdddddddddddddddddddddddddddddddddddddddd\nrefs/pull/1/head\tdddddddddddddddddddddddddddddddddddddddd\ndev\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nclaude/t27c-x\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", "dddddddddddddddddddddddddddddddddddddddd\tasked\ncccccccccccccccccccccccccccccccccccccccc\tasked\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\tasked\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n"); + assert lab_test_stale("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\tasked\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\ncccccccccccccccccccccccccccccccccccccccc\ndddddddddddddddddddddddddddddddddddddddd\n", "claude/t27c-a\tcccccccccccccccccccccccccccccccccccccccc\nclaude/t27c-b\tcccccccccccccccccccccccccccccccccccccccc\nclaude/t27c-x\tcccccccccccccccccccccccccccccccccccccccc\nrefs/pull/1/head\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\tasked\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\ncccccccccccccccccccccccccccccccccccccccc\n"); + assert lab_test_stale("cccccccccccccccccccccccccccccccccccccccc\tasked\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tasked\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ndddddddddddddddddddddddddddddddddddddddd\tasked\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", "claude/t27c-x\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nclaude/t27c-z\tdddddddddddddddddddddddddddddddddddddddd\nfeature/a\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nclaude/t27c-b\tdddddddddddddddddddddddddddddddddddddddd\n", "cccccccccccccccccccccccccccccccccccccccc\tasked\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tasked\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ndddddddddddddddddddddddddddddddddddddddd\tasked\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"); +} + +// >>> pinned table: plan_stale +// <<< pinned table: plan_item_has +// run_item(): `"master" in item["branches"]` (and any other name) over the items of the plans above. +test lab_py_table_item_has_branch { + assert lab_test_item_has("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\tclaude/lab", "master", false); + assert lab_test_item_has("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\tclaude/lab", "claude/t27c-x", false); + assert lab_test_item_has("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\tclaude/lab", "mast", false); + assert lab_test_item_has("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\tclaude/lab", "", false); + assert lab_test_item_has("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\tclaude/lab", "master ", false); + assert lab_test_item_has("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tclaude/t27c-a", "master", false); + assert lab_test_item_has("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tclaude/t27c-a", "claude/t27c-x", false); + assert lab_test_item_has("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tclaude/t27c-a", "mast", false); + assert lab_test_item_has("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tclaude/t27c-a", "", false); + assert lab_test_item_has("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tclaude/t27c-a", "master ", false); + assert lab_test_item_has("cccccccccccccccccccccccccccccccccccccccc\tmaster", "master", true); + assert lab_test_item_has("cccccccccccccccccccccccccccccccccccccccc\tmaster", "claude/t27c-x", false); + assert lab_test_item_has("cccccccccccccccccccccccccccccccccccccccc\tmaster", "mast", false); + assert lab_test_item_has("cccccccccccccccccccccccccccccccccccccccc\tmaster", "", false); + assert lab_test_item_has("cccccccccccccccccccccccccccccccccccccccc\tmaster", "master ", false); + assert lab_test_item_has("cccccccccccccccccccccccccccccccccccccccc\tclaude/t27c-x\tfeature/a\trefs/pull/1/head", "master", false); + assert lab_test_item_has("cccccccccccccccccccccccccccccccccccccccc\tclaude/t27c-x\tfeature/a\trefs/pull/1/head", "claude/t27c-x", true); + assert lab_test_item_has("cccccccccccccccccccccccccccccccccccccccc\tclaude/t27c-x\tfeature/a\trefs/pull/1/head", "mast", false); + assert lab_test_item_has("cccccccccccccccccccccccccccccccccccccccc\tclaude/t27c-x\tfeature/a\trefs/pull/1/head", "", false); + assert lab_test_item_has("cccccccccccccccccccccccccccccccccccccccc\tclaude/t27c-x\tfeature/a\trefs/pull/1/head", "master ", false); + assert lab_test_item_has("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tclaude/t27c-y", "master", false); + assert lab_test_item_has("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tclaude/t27c-y", "claude/t27c-x", false); + assert lab_test_item_has("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tclaude/t27c-y", "mast", false); + assert lab_test_item_has("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tclaude/t27c-y", "", false); + assert lab_test_item_has("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tclaude/t27c-y", "master ", false); + assert lab_test_item_has("dddddddddddddddddddddddddddddddddddddddd\tclaude/gen-zig", "master", false); + assert lab_test_item_has("dddddddddddddddddddddddddddddddddddddddd\tclaude/gen-zig", "claude/t27c-x", false); + assert lab_test_item_has("dddddddddddddddddddddddddddddddddddddddd\tclaude/gen-zig", "mast", false); + assert lab_test_item_has("dddddddddddddddddddddddddddddddddddddddd\tclaude/gen-zig", "", false); + assert lab_test_item_has("dddddddddddddddddddddddddddddddddddddddd\tclaude/gen-zig", "master ", false); + assert lab_test_item_has("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tclaude/t27c-b", "master", false); + assert lab_test_item_has("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tclaude/t27c-b", "claude/t27c-x", false); + assert lab_test_item_has("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tclaude/t27c-b", "mast", false); + assert lab_test_item_has("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tclaude/t27c-b", "", false); + assert lab_test_item_has("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\tclaude/t27c-b", "master ", false); + assert lab_test_item_has("cccccccccccccccccccccccccccccccccccccccc\tclaude/t27c-y", "master", false); + assert lab_test_item_has("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "master", false); +} + +// >>> pinned table: plan_item_has + +// Scenario: `*` and `?` cross `/` and a line break (the regex is DOTALL); `[!x]` takes them too. Checked on Python. +test the_star_and_the_question_mark_cross_slashes_and_line_breaks { + assert lab_fnmatch("a?b", 0, 3, "a\nb", 0, 3) == LAB_FN_YES; + assert lab_fnmatch("a*", 0, 2, "a\nb", 0, 3) == LAB_FN_YES; + assert lab_fnmatch("*", 0, 1, "a\nb", 0, 3) == LAB_FN_YES; + assert lab_fnmatch("a[!x]b", 0, 6, "a\nb", 0, 3) == LAB_FN_YES; + assert lab_fnmatch("a?b", 0, 3, "a/b", 0, 3) == LAB_FN_YES; + assert lab_fnmatch("a[/]b", 0, 5, "a/b", 0, 3) == LAB_FN_YES; + assert lab_fnmatch("", 0, 0, "", 0, 0) == LAB_FN_YES; + assert lab_fnmatch("", 0, 0, "a", 0, 1) == LAB_FN_NO; + assert lab_fnmatch("?", 0, 1, "", 0, 0) == LAB_FN_NO; +} + +// Scenario: a character is a code point. `?` takes all of e-acute (2 bytes), a euro sign (3) and a face (4); `??` does not take +// one; a class holds a character by its code point and a range reaches U+00FF but not U+0100. Checked on Python. +test a_character_is_a_code_point_not_a_byte { + var eacute : [2]u8 = undefined; + eacute[0] = 195; + eacute[1] = 169; + assert lab_fnmatch("?", 0, 1, é, 0, 2) == LAB_FN_YES; + assert lab_fnmatch("??", 0, 2, é, 0, 2) == LAB_FN_NO; + assert lab_fnmatch("*", 0, 1, é, 0, 2) == LAB_FN_YES; + var cls : [4]u8 = undefined; + cls[0] = 91; + cls[1] = 195; + cls[2] = 169; + cls[3] = 93; + assert lab_fnmatch(&cls, 0, 4, é, 0, 2) == LAB_FN_YES; + assert lab_fnmatch(&cls, 0, 4, "e", 0, 1) == LAB_FN_NO; + var neg : [5]u8 = undefined; + neg[0] = 91; + neg[1] = 33; + neg[2] = 195; + neg[3] = 169; + neg[4] = 93; + assert lab_fnmatch(&neg, 0, 5, é, 0, 2) == LAB_FN_NO; + assert lab_fnmatch(&neg, 0, 5, "e", 0, 1) == LAB_FN_YES; + var euro : [3]u8 = undefined; + euro[0] = 226; + euro[1] = 130; + euro[2] = 172; + assert lab_fnmatch("?", 0, 1, &euro, 0, 3) == LAB_FN_YES; + assert lab_fnmatch("??", 0, 2, &euro, 0, 3) == LAB_FN_NO; + var face : [4]u8 = undefined; + face[0] = 240; + face[1] = 159; + face[2] = 152; + face[3] = 128; + assert lab_fnmatch("?", 0, 1, &face, 0, 4) == LAB_FN_YES; + assert lab_fnmatch("*?", 0, 2, &face, 0, 4) == LAB_FN_YES; + var rng : [6]u8 = undefined; + rng[0] = 91; + rng[1] = 97; + rng[2] = 45; + rng[3] = 195; + rng[4] = 191; + rng[5] = 93; + assert lab_fnmatch(&rng, 0, 6, é, 0, 2) == LAB_FN_YES; + var a_grave : [2]u8 = undefined; + a_grave[0] = 196; + a_grave[1] = 128; + assert lab_fnmatch(&rng, 0, 6, &a_grave, 0, 2) == LAB_FN_NO; +} + +// Scenario (negative control, NOT Python): a range that runs backwards is refused, wherever it is and whatever the name is. +// Python gives such a class no members at all (`[z-a]` takes neither z nor a), and a second range after it can move which +// ends are dropped; the port refuses the glob rather than answer. A glob after the match is not tried, as in Python. +test a_backwards_range_is_refused_not_answered_differently { + assert lab_fnmatch("[z-a]", 0, 5, "z", 0, 1) == LAB_FN_REFUSED; + assert lab_fnmatch("[z-a]", 0, 5, "a", 0, 1) == LAB_FN_REFUSED; + assert lab_fnmatch("[bz-a]", 0, 6, "b", 0, 1) == LAB_FN_REFUSED; + assert lab_fnmatch("x[9-0]y", 0, 7, "x5y", 0, 3) == LAB_FN_REFUSED; + assert lab_fnmatch("[!z-a]", 0, 6, "q", 0, 1) == LAB_FN_REFUSED; + assert lab_fnmatch("a-z", 0, 3, "a-z", 0, 3) == LAB_FN_YES; + assert lab_fnmatch("[a-a]", 0, 5, "a", 0, 1) == LAB_FN_YES; + assert lab_fnmatch("[a-b-c]", 0, 7, "c", 0, 1) == LAB_FN_YES; + assert lab_fnmatch("[a-]", 0, 4, "a", 0, 1) == LAB_FN_YES; + assert lab_glob_ok("[z-a]", 0, 5) == false; + assert lab_glob_ok("[a-z]", 0, 5) == true; + assert lab_glob_ok("[z-a", 0, 4) == true; + assert lab_watch_ok("master,[z-a]") == false; + assert lab_watch_ok("master, ,claude/*") == true; + assert lab_watched("master", 0, 6, "master,[z-a]") == 0; + assert lab_watched("main", 0, 4, "master,[z-a]") == LAB_WATCH_BAD; + assert lab_watched("main", 0, 4, "[z-a],main") == LAB_WATCH_BAD; +} + +// Scenario: a writer reports the length it needs when the buffer is short, and writes what fits. +test a_short_buffer_still_gets_the_length { + const h : string = "master\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"; + var out : [8]u8 = undefined; + assert lab_plan(h, "master", "", "", &out, 8, 0) == 48; + assert span_is(&out, 0, 8, "aaaaaaaa"); + assert lab_plan(h, "master", "", "", &out, 8, 5) == 53; + assert lab_plan(h, "[z-a]", "", "", &out, 8, 0) == LAB_NO; + assert lab_parse_heads("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\trefs/heads/master\n", &out, 8, 0) == 48; + assert lab_stale_dropped("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\tasked\n", "", &out, 8, 0) == 47; + assert lab_stale_dropped("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\tasked\n", "", &out, 8, 3) == 50; +} + +// Scenario: the watch text is cut at commas and stripped of Unicode spaces, as Python's strip() does (a no-break space is one). +test a_glob_is_stripped_of_unicode_spaces { + var v : [10]u8 = undefined; + v[0] = 194; + v[1] = 160; + v[2] = 109; + v[3] = 97; + v[4] = 115; + v[5] = 116; + v[6] = 101; + v[7] = 114; + v[8] = 194; + v[9] = 160; + assert lab_watched("master", 0, 6, &v) == 0; + assert lab_watched("xx master yy", 3, 9, "master") == 0; + assert lab_watched("xx master yy", 3, 8, "master") == LAB_WATCH_NONE; +} + +// Scenario: a head line is ` TAB refs/heads/`: the offset of the branch comes back, and a line that is not one is no head. +test the_branch_of_a_head_line_is_found_by_its_offset { + const l : string = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\trefs/heads/claude/x"; + assert lab_ls_branch(l, 0, l.len) == 52; + assert lab_ls_branch(l, 0, 51) == LAB_NO; + assert lab_ls_branch(l, 0, 52) == 52; + assert lab_ls_branch("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\trefs/heads/", 0, 52) == 52; + assert lab_ls_branch("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\trefs/tags/v1", 0, 53) == LAB_NO; + assert lab_ls_branch("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\trefs/heads/x", 0, 51) == LAB_NO; + assert lab_ls_branch("", 0, 0) == LAB_NO; +} diff --git a/specs/lab/prune.t27 b/specs/lab/prune.t27 new file mode 100644 index 0000000000..76ee74c87c --- /dev/null +++ b/specs/lab/prune.t27 @@ -0,0 +1,595 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/lab/prune.t27 -- which finished runs and which scratch build directories the t27c lab deletes (gHashTag/t27#11041, #11040) +; The t27c lab (infra/t27c-lab/lab.py) keeps what it publishes and what it builds on one volume, and two rules keep that volume +; from filling (2026-10-07: it filled to 99% and the queue stopped on ENOSPC, #7090). Both are decisions about names, ages and +; numbers, ported here as pure functions with the Python as the oracle until the cutover; they know no host, no path and no cloud: +; prune() runs[KEEP_RUNS:] by modification time, newest first, except the commits a watched head points at +; lab_prune_start, lab_run_rank, lab_prune_runs +; reap_scratch_targets() when free space is under min_free of the volume, a directory `-target` that nobody wrote in +; idle_s seconds is dropped; the lab's own `target` is never matched, nor a link, nor a file +; lab_reap_pressure, lab_scratch_name, lab_reap_decision +; THE RUNS. `runs` is one finished run per line, ` TAB `: the file name without `.json` and its modification time as +; an unsigned decimal integer of at most 18 digits in any one unit (only the order is read). A line that is not that is not a +; run. They are ranked as sorted(..., key=mtime, reverse=True) ranks them: newest first, equal times in listing order (Python's +; reverse sort is stable). KEEP_RUNS is a Python slice start, so a negative value counts from the end: with -5 the candidates are +; the OLDEST five and nothing else is ever deleted, the lab's own quirk, ported as it is. `live` is the shas of the watched +; heads, one per line. +; lab_prune_runs writes the stems to delete, one per line: those at rank KEEP_RUNS or after that are not live. +; THE SCRATCH TARGETS. Free space is `free` of `total` bytes. `free >= total * min_free` means room: nothing is reaped, whatever +; the directories are (float arithmetic as Python does it, the comparison in f64). Then a name that does not end in `-target` is +; not a scratch target (pathlib's `*-target` matches `-target` and a dot name too, and is case sensitive on Linux); a symbolic link +; and a non-directory are kept; a directory whose newest write is less than idle_s seconds old is kept; the rest is deleted. The +; age is `now - newest mtime` over the directory and every file under it, which the host computes (it reads the file system); this +; file decides from the number. lab_reap_decision names the first rule that applies. +; THE LAB DISK GATE. specs/claw/lab_disk.t27 holds a CHECK before it runs when the volume is full (under 1 GiB) and warns under +; 5 GiB or 10%; the reaper starts earlier, at 15% free. The tests below tie the two: whatever lab_disk calls low by the percent +; line, the reaper is already running. +; ASCII only (L3) +; phi^2 + 1/phi^2 = 3 | TRINITY + +module LabPrune; + +use tri::lab::receipt::span_is; +use lab::text::LAB_NO; +use lab::text::LAB_DEC_BAD; +use lab::text::lab_line_end; +use lab::text::lab_line_next; +use lab::text::lab_find_byte; +use lab::text::lab_count_of; +use lab::text::lab_set_has; +use lab::text::lab_put_span; +use lab::text::lab_put_lit; +use claw::lab_disk::disk_reason; +use claw::lab_disk::R_OK; +use claw::lab_disk::R_LOW; +use claw::lab_disk::R_FULL; + +/// KEEP_RUNS's default. +pub const LAB_KEEP_RUNS : i64 = 200; +/// reap_scratch_targets()'s defaults: under 15% free, a directory idle for a day. +pub const LAB_REAP_MIN_FREE : f64 = 0.15; +pub const LAB_REAP_IDLE_S : f64 = 86400.0; + +/// What reap_scratch_targets() does with one entry of the data directory, the first rule that applies. +pub const LAB_REAP_KEEP_ROOM : u8 = 0; +pub const LAB_REAP_KEEP_NAME : u8 = 1; +pub const LAB_REAP_KEEP_LINK : u8 = 2; +pub const LAB_REAP_KEEP_NOT_DIR : u8 = 3; +pub const LAB_REAP_KEEP_RECENT : u8 = 4; +pub const LAB_REAP_DELETE : u8 = 5; + +/// The index of the first run `runs[KEEP_RUNS:]` takes out of n runs: Python's slice start, a negative one counting from the end. +pub fn lab_prune_start(n: usize, keep: i64) -> usize { + if (keep >= 0) { + if ((keep as usize) >= n) { return n; } + return keep as usize; + } + if (keep < 0 - 4611686018427387904) { return 0; } + const back : usize = (0 - keep) as usize; + if (back >= n) { return 0; } + return n - back; +} + +/// The modification time on the run line runs[s..e]: LAB_DEC_BAD when the line is not ` TAB `. +pub fn lab_run_mtime(runs: string, s: usize, e: usize) -> u64 { + const tab : usize = lab_find_byte(runs, s, e, 9); + if (tab == LAB_NO) { return LAB_DEC_BAD; } + return lab_count_of(runs, tab + 1, e); +} + +/// How many lines of `runs` are runs. +pub fn lab_run_count(runs: string) -> usize { + var n : usize = 0; + var s : usize = 0; + while (s < runs.len) { + const e : usize = lab_line_end(runs, s); + if (lab_run_mtime(runs, s, e) != LAB_DEC_BAD) { n = n + 1; } + s = lab_line_next(runs, e); + } + return n; +} + +/// The rank, 0 for the newest, of the run on the line that starts at s: the runs strictly newer, and the ones listed before it +/// with the same time. +pub fn lab_run_rank(runs: string, s: usize) -> usize { + const mine : u64 = lab_run_mtime(runs, s, lab_line_end(runs, s)); + var rank : usize = 0; + var k : usize = 0; + while (k < runs.len) { + const e : usize = lab_line_end(runs, k); + const m : u64 = lab_run_mtime(runs, k, e); + if (m != LAB_DEC_BAD and (m > mine or (m == mine and k < s))) { rank = rank + 1; } + k = lab_line_next(runs, e); + } + return rank; +} + +/// prune(): the stems of the runs to delete, one per line, into dst at w, clipped at cap; the new cursor. +pub fn lab_prune_runs(runs: string, live: string, keep: i64, dst: []u8, cap: usize, w: usize) -> usize { + const start : usize = lab_prune_start(lab_run_count(runs), keep); + var x : usize = w; + var s : usize = 0; + while (s < runs.len) { + const e : usize = lab_line_end(runs, s); + if (lab_run_mtime(runs, s, e) != LAB_DEC_BAD) { + const tab : usize = lab_find_byte(runs, s, e, 9); + if (lab_run_rank(runs, s) >= start and lab_set_has(live, runs, s, tab) == false) { + x = lab_put_span(dst, cap, x, runs, s, tab); + x = lab_put_lit(dst, cap, x, "\n"); + } + } + s = lab_line_next(runs, e); + } + return x; +} + +/// reap_scratch_targets(): is there room? Free space at or above min_free of the volume means nothing is reaped. +pub fn lab_reap_pressure(total: u64, free: u64, min_free: f64) -> bool { + return (free as f64) < (total as f64) * min_free; +} + +/// root.glob("*-target"): a name that ends in `-target` (a name of just `-target` too). +pub fn lab_scratch_name(t: string, from: usize, to: usize) -> bool { + if (to < from or to - from < 7) { return false; } + return span_is(t, to - 7, to, "-target"); +} + +/// reap_scratch_targets(): what to do with the entry name[nf..nt] of the data directory. +pub fn lab_reap_decision(name: string, nf: usize, nt: usize, is_link: bool, is_dir: bool, age_s: f64, total: u64, free: u64, min_free: f64, idle_s: f64) -> u8 { + if (lab_reap_pressure(total, free, min_free) == false) { return LAB_REAP_KEEP_ROOM; } + if (lab_scratch_name(name, nf, nt) == false) { return LAB_REAP_KEEP_NAME; } + if (is_link) { return LAB_REAP_KEEP_LINK; } + if (is_dir == false) { return LAB_REAP_KEEP_NOT_DIR; } + if (age_s < idle_s) { return LAB_REAP_KEEP_RECENT; } + return LAB_REAP_DELETE; +} + +// ---- tests ---- +// The oracle is infra/t27c-lab/lab.py. The cases below are its answers (Python 3.14.6): prune() was run for real on a directory +// of run files with chosen modification times (the data directory and the reaper patched out), and reap_scratch_targets() was +// run for real on a directory of entries (a directory, a link to one, a file; old and new; min_free 2.0 to reap and 0.0 not to). +// Its self_check() case (old-target goes; new-target and target stay) is the first test of the reaper, one to one. + +fn lab_test_prune(runs: string, live: string, keep: i64, want: string) -> bool { + var out : [1024]u8 = undefined; + const w : usize = lab_prune_runs(runs, live, keep, &out, 1024, 0); + return w == want.len and span_is(&out, 0, w, want); +} + +/// One entry of the data directory under pressure (total 100, free 0, min_free 2.0), idle limit 1000 s. +fn lab_test_reap(name: string, is_link: bool, is_dir: bool, age: f64, want: u8) -> bool { + return lab_reap_decision(name, 0, name.len, is_link, is_dir, age, 100, 0, 2.0, 1000.0) == want; +} + +// <<< pinned table: prune_self_check +test lab_py_self_check_the_reaper_drops_the_old_scratch_target_and_nothing_else { + assert lab_test_reap("old-target", false, true, 1700000000.0, LAB_REAP_DELETE); + assert lab_test_reap("new-target", false, true, 0.5, LAB_REAP_KEEP_RECENT); + assert lab_test_reap("target", false, true, 1700000000.0, LAB_REAP_KEEP_NAME); + assert lab_test_reap("target", false, true, 0.5, LAB_REAP_KEEP_NAME); +} + +// >>> pinned table: prune_self_check +// <<< pinned table: prune_reap +// reap_scratch_targets() on one entry at a time: a directory, a link to one, a file, each old (written long ago) and new, under pressure. +test lab_py_table_the_reaper_on_every_kind_of_entry { + assert lab_test_reap("old-target", false, true, 1700000000.0, LAB_REAP_DELETE); + assert lab_test_reap("old-target", false, true, 0.5, LAB_REAP_KEEP_RECENT); + assert lab_test_reap("old-target", true, true, 1700000000.0, LAB_REAP_KEEP_LINK); + assert lab_test_reap("old-target", true, true, 0.5, LAB_REAP_KEEP_LINK); + assert lab_test_reap("old-target", false, false, 1700000000.0, LAB_REAP_KEEP_NOT_DIR); + assert lab_test_reap("old-target", false, false, 0.5, LAB_REAP_KEEP_NOT_DIR); + assert lab_test_reap("new-target", false, true, 1700000000.0, LAB_REAP_DELETE); + assert lab_test_reap("new-target", false, true, 0.5, LAB_REAP_KEEP_RECENT); + assert lab_test_reap("new-target", true, true, 1700000000.0, LAB_REAP_KEEP_LINK); + assert lab_test_reap("new-target", true, true, 0.5, LAB_REAP_KEEP_LINK); + assert lab_test_reap("new-target", false, false, 1700000000.0, LAB_REAP_KEEP_NOT_DIR); + assert lab_test_reap("new-target", false, false, 0.5, LAB_REAP_KEEP_NOT_DIR); + assert lab_test_reap("target", false, true, 1700000000.0, LAB_REAP_KEEP_NAME); + assert lab_test_reap("target", false, true, 0.5, LAB_REAP_KEEP_NAME); + assert lab_test_reap("target", true, true, 1700000000.0, LAB_REAP_KEEP_NAME); + assert lab_test_reap("target", true, true, 0.5, LAB_REAP_KEEP_NAME); + assert lab_test_reap("target", false, false, 1700000000.0, LAB_REAP_KEEP_NAME); + assert lab_test_reap("target", false, false, 0.5, LAB_REAP_KEEP_NAME); + assert lab_test_reap("a-target", false, true, 1700000000.0, LAB_REAP_DELETE); + assert lab_test_reap("a-target", false, true, 0.5, LAB_REAP_KEEP_RECENT); + assert lab_test_reap("a-target", true, true, 1700000000.0, LAB_REAP_KEEP_LINK); + assert lab_test_reap("a-target", true, true, 0.5, LAB_REAP_KEEP_LINK); + assert lab_test_reap("a-target", false, false, 1700000000.0, LAB_REAP_KEEP_NOT_DIR); + assert lab_test_reap("a-target", false, false, 0.5, LAB_REAP_KEEP_NOT_DIR); + assert lab_test_reap("-target", false, true, 1700000000.0, LAB_REAP_DELETE); + assert lab_test_reap("-target", false, true, 0.5, LAB_REAP_KEEP_RECENT); + assert lab_test_reap("-target", true, true, 1700000000.0, LAB_REAP_KEEP_LINK); + assert lab_test_reap("-target", true, true, 0.5, LAB_REAP_KEEP_LINK); + assert lab_test_reap("-target", false, false, 1700000000.0, LAB_REAP_KEEP_NOT_DIR); + assert lab_test_reap("-target", false, false, 0.5, LAB_REAP_KEEP_NOT_DIR); + assert lab_test_reap(".x-target", false, true, 1700000000.0, LAB_REAP_DELETE); + assert lab_test_reap(".x-target", false, true, 0.5, LAB_REAP_KEEP_RECENT); + assert lab_test_reap(".x-target", true, true, 1700000000.0, LAB_REAP_KEEP_LINK); + assert lab_test_reap(".x-target", true, true, 0.5, LAB_REAP_KEEP_LINK); + assert lab_test_reap(".x-target", false, false, 1700000000.0, LAB_REAP_KEEP_NOT_DIR); + assert lab_test_reap(".x-target", false, false, 0.5, LAB_REAP_KEEP_NOT_DIR); + assert lab_test_reap("a-targets", false, true, 1700000000.0, LAB_REAP_KEEP_NAME); + assert lab_test_reap("a-targets", false, true, 0.5, LAB_REAP_KEEP_NAME); + assert lab_test_reap("a-targets", true, true, 1700000000.0, LAB_REAP_KEEP_NAME); + assert lab_test_reap("a-targets", true, true, 0.5, LAB_REAP_KEEP_NAME); + assert lab_test_reap("a-targets", false, false, 1700000000.0, LAB_REAP_KEEP_NAME); + assert lab_test_reap("a-targets", false, false, 0.5, LAB_REAP_KEEP_NAME); + assert lab_test_reap("x-target-y", false, true, 1700000000.0, LAB_REAP_KEEP_NAME); + assert lab_test_reap("x-target-y", false, true, 0.5, LAB_REAP_KEEP_NAME); + assert lab_test_reap("x-target-y", true, true, 1700000000.0, LAB_REAP_KEEP_NAME); + assert lab_test_reap("x-target-y", true, true, 0.5, LAB_REAP_KEEP_NAME); + assert lab_test_reap("x-target-y", false, false, 1700000000.0, LAB_REAP_KEEP_NAME); + assert lab_test_reap("x-target-y", false, false, 0.5, LAB_REAP_KEEP_NAME); + assert lab_test_reap("a-target.", false, true, 1700000000.0, LAB_REAP_KEEP_NAME); + assert lab_test_reap("a-target.", false, true, 0.5, LAB_REAP_KEEP_NAME); + assert lab_test_reap("a-target.", true, true, 1700000000.0, LAB_REAP_KEEP_NAME); + assert lab_test_reap("a-target.", true, true, 0.5, LAB_REAP_KEEP_NAME); + assert lab_test_reap("a-target.", false, false, 1700000000.0, LAB_REAP_KEEP_NAME); + assert lab_test_reap("a-target.", false, false, 0.5, LAB_REAP_KEEP_NAME); + assert lab_test_reap("a b-target", false, true, 1700000000.0, LAB_REAP_DELETE); + assert lab_test_reap("a b-target", false, true, 0.5, LAB_REAP_KEEP_RECENT); + assert lab_test_reap("a b-target", true, true, 1700000000.0, LAB_REAP_KEEP_LINK); + assert lab_test_reap("a b-target", true, true, 0.5, LAB_REAP_KEEP_LINK); + assert lab_test_reap("a b-target", false, false, 1700000000.0, LAB_REAP_KEEP_NOT_DIR); + assert lab_test_reap("a b-target", false, false, 0.5, LAB_REAP_KEEP_NOT_DIR); + assert lab_test_reap("x-target-target", false, true, 1700000000.0, LAB_REAP_DELETE); + assert lab_test_reap("x-target-target", false, true, 0.5, LAB_REAP_KEEP_RECENT); + assert lab_test_reap("x-target-target", true, true, 1700000000.0, LAB_REAP_KEEP_LINK); + assert lab_test_reap("x-target-target", true, true, 0.5, LAB_REAP_KEEP_LINK); + assert lab_test_reap("x-target-target", false, false, 1700000000.0, LAB_REAP_KEEP_NOT_DIR); + assert lab_test_reap("x-target-target", false, false, 0.5, LAB_REAP_KEEP_NOT_DIR); + assert lab_test_reap("--target", false, true, 1700000000.0, LAB_REAP_DELETE); + assert lab_test_reap("--target", false, true, 0.5, LAB_REAP_KEEP_RECENT); + assert lab_test_reap("--target", true, true, 1700000000.0, LAB_REAP_KEEP_LINK); + assert lab_test_reap("--target", true, true, 0.5, LAB_REAP_KEEP_LINK); + assert lab_test_reap("--target", false, false, 1700000000.0, LAB_REAP_KEEP_NOT_DIR); + assert lab_test_reap("--target", false, false, 0.5, LAB_REAP_KEEP_NOT_DIR); + assert lab_test_reap("target-", false, true, 1700000000.0, LAB_REAP_KEEP_NAME); + assert lab_test_reap("target-", false, true, 0.5, LAB_REAP_KEEP_NAME); + assert lab_test_reap("target-", true, true, 1700000000.0, LAB_REAP_KEEP_NAME); + assert lab_test_reap("target-", true, true, 0.5, LAB_REAP_KEEP_NAME); + assert lab_test_reap("target-", false, false, 1700000000.0, LAB_REAP_KEEP_NAME); + assert lab_test_reap("target-", false, false, 0.5, LAB_REAP_KEEP_NAME); + assert lab_test_reap("-targe", false, true, 1700000000.0, LAB_REAP_KEEP_NAME); + assert lab_test_reap("-targe", false, true, 0.5, LAB_REAP_KEEP_NAME); + assert lab_test_reap("-targe", true, true, 1700000000.0, LAB_REAP_KEEP_NAME); + assert lab_test_reap("-targe", true, true, 0.5, LAB_REAP_KEEP_NAME); + assert lab_test_reap("-targe", false, false, 1700000000.0, LAB_REAP_KEEP_NAME); + assert lab_test_reap("-targe", false, false, 0.5, LAB_REAP_KEEP_NAME); + assert lab_test_reap("atarget", false, true, 1700000000.0, LAB_REAP_KEEP_NAME); + assert lab_test_reap("atarget", false, true, 0.5, LAB_REAP_KEEP_NAME); + assert lab_test_reap("atarget", true, true, 1700000000.0, LAB_REAP_KEEP_NAME); + assert lab_test_reap("atarget", true, true, 0.5, LAB_REAP_KEEP_NAME); + assert lab_test_reap("atarget", false, false, 1700000000.0, LAB_REAP_KEEP_NAME); + assert lab_test_reap("atarget", false, false, 0.5, LAB_REAP_KEEP_NAME); + assert lab_test_reap("b-Target", false, true, 1700000000.0, LAB_REAP_KEEP_NAME); + assert lab_test_reap("b-Target", false, true, 0.5, LAB_REAP_KEEP_NAME); + assert lab_test_reap("b-Target", true, true, 1700000000.0, LAB_REAP_KEEP_NAME); + assert lab_test_reap("b-Target", true, true, 0.5, LAB_REAP_KEEP_NAME); + assert lab_test_reap("b-Target", false, false, 1700000000.0, LAB_REAP_KEEP_NAME); + assert lab_test_reap("b-Target", false, false, 0.5, LAB_REAP_KEEP_NAME); + assert lab_test_reap("TARGET", false, true, 1700000000.0, LAB_REAP_KEEP_NAME); + assert lab_test_reap("TARGET", false, true, 0.5, LAB_REAP_KEEP_NAME); + assert lab_test_reap("TARGET", true, true, 1700000000.0, LAB_REAP_KEEP_NAME); + assert lab_test_reap("TARGET", true, true, 0.5, LAB_REAP_KEEP_NAME); + assert lab_test_reap("TARGET", false, false, 1700000000.0, LAB_REAP_KEEP_NAME); + assert lab_test_reap("TARGET", false, false, 0.5, LAB_REAP_KEEP_NAME); + assert lab_test_reap("scratch-target", false, true, 1700000000.0, LAB_REAP_DELETE); + assert lab_test_reap("scratch-target", false, true, 0.5, LAB_REAP_KEEP_RECENT); + assert lab_test_reap("scratch-target", true, true, 1700000000.0, LAB_REAP_KEEP_LINK); + assert lab_test_reap("scratch-target", true, true, 0.5, LAB_REAP_KEEP_LINK); + assert lab_test_reap("scratch-target", false, false, 1700000000.0, LAB_REAP_KEEP_NOT_DIR); + assert lab_test_reap("scratch-target", false, false, 0.5, LAB_REAP_KEEP_NOT_DIR); + assert lab_test_reap("www", false, true, 1700000000.0, LAB_REAP_KEEP_NAME); + assert lab_test_reap("www", false, true, 0.5, LAB_REAP_KEEP_NAME); + assert lab_test_reap("www", true, true, 1700000000.0, LAB_REAP_KEEP_NAME); + assert lab_test_reap("www", true, true, 0.5, LAB_REAP_KEEP_NAME); + assert lab_test_reap("www", false, false, 1700000000.0, LAB_REAP_KEEP_NAME); + assert lab_test_reap("www", false, false, 0.5, LAB_REAP_KEEP_NAME); + assert lab_test_reap("requests", false, true, 1700000000.0, LAB_REAP_KEEP_NAME); + assert lab_test_reap("requests", false, true, 0.5, LAB_REAP_KEEP_NAME); + assert lab_test_reap("requests", true, true, 1700000000.0, LAB_REAP_KEEP_NAME); + assert lab_test_reap("requests", true, true, 0.5, LAB_REAP_KEEP_NAME); + assert lab_test_reap("requests", false, false, 1700000000.0, LAB_REAP_KEEP_NAME); + assert lab_test_reap("requests", false, false, 0.5, LAB_REAP_KEEP_NAME); + assert lab_reap_decision("old-target", 0, 10, false, true, 1700000000.0, 100, 0, 0.0, 1000.0) == LAB_REAP_KEEP_ROOM; + assert lab_reap_decision("old-target", 0, 10, true, true, 1700000000.0, 100, 0, 0.0, 1000.0) == LAB_REAP_KEEP_ROOM; + assert lab_reap_decision("old-target", 0, 10, false, false, 1700000000.0, 100, 0, 0.0, 1000.0) == LAB_REAP_KEEP_ROOM; + assert lab_reap_decision("a-target", 0, 8, false, true, 1700000000.0, 100, 0, 0.0, 1000.0) == LAB_REAP_KEEP_ROOM; + assert lab_reap_decision("a-target", 0, 8, true, true, 1700000000.0, 100, 0, 0.0, 1000.0) == LAB_REAP_KEEP_ROOM; + assert lab_reap_decision("a-target", 0, 8, false, false, 1700000000.0, 100, 0, 0.0, 1000.0) == LAB_REAP_KEEP_ROOM; + assert lab_reap_decision("target", 0, 6, false, true, 1700000000.0, 100, 0, 0.0, 1000.0) == LAB_REAP_KEEP_ROOM; + assert lab_reap_decision("target", 0, 6, true, true, 1700000000.0, 100, 0, 0.0, 1000.0) == LAB_REAP_KEEP_ROOM; + assert lab_reap_decision("target", 0, 6, false, false, 1700000000.0, 100, 0, 0.0, 1000.0) == LAB_REAP_KEEP_ROOM; +} + +// >>> pinned table: prune_reap +// <<< pinned table: prune_pressure +// `du.free >= du.total * min_free` returns before anything is deleted; `time.time() - newest < idle_s` keeps a directory. +test lab_py_table_pressure_and_idle { + assert lab_reap_pressure(100, 15, 0.15) == false; + assert lab_reap_pressure(100, 14, 0.15) == true; + assert lab_reap_pressure(100, 16, 0.15) == false; + assert lab_reap_pressure(100, 0, 0.15) == true; + assert lab_reap_pressure(100, 100, 0.15) == false; + assert lab_reap_pressure(0, 0, 0.15) == false; + assert lab_reap_pressure(47745772, 0, 0.15) == true; + assert lab_reap_pressure(48891670528, 0, 0.15) == true; + assert lab_reap_pressure(2079065522176, 535033524224, 0.15) == false; + assert lab_reap_pressure(2079065522176, 307200000000, 0.15) == true; + assert lab_reap_pressure(100, 100, 2.0) == true; + assert lab_reap_pressure(100, 0, 2.0) == true; + assert lab_reap_pressure(100, 0, 0.0) == false; + assert lab_reap_pressure(100, 1, 0.0) == false; + assert lab_reap_pressure(3, 1, 0.34) == true; + assert lab_reap_pressure(3, 1, 0.33) == false; + assert lab_reap_pressure(3, 1, 0.3333333333333333) == false; + assert lab_reap_pressure(20, 3, 0.15) == false; + assert lab_reap_pressure(20, 4, 0.15) == false; + assert lab_reap_pressure(1000, 150, 0.15) == false; + assert lab_reap_pressure(1000, 149, 0.15) == true; + assert lab_reap_pressure(1000, 151, 0.15) == false; + assert lab_reap_pressure(10, 1, 0.1) == false; + assert lab_reap_pressure(10, 2, 0.1) == false; + assert lab_reap_pressure(53687091200, 8053063680, 0.15) == false; + assert lab_reap_pressure(53687091200, 8053063679, 0.15) == true; + assert lab_reap_pressure(53687091200, 8053063681, 0.15) == false; + assert lab_reap_pressure(9007199254740992, 9007199254740992, 1.0) == false; + assert lab_reap_pressure(9007199254740992, 9007199254740991, 1.0) == true; + assert lab_reap_pressure(1000000000000000, 100000000000000, 0.1) == false; + assert lab_reap_pressure(1000000000000000, 99999999999999, 0.1) == true; + assert lab_reap_pressure(1, 0, 0.5) == true; + assert lab_reap_pressure(1, 1, 0.5) == false; + assert lab_reap_pressure(2, 1, 0.5) == false; + assert lab_reap_pressure(7, 1, 0.15) == true; + assert lab_reap_pressure(7, 2, 0.15) == false; + assert lab_reap_pressure(6, 1, 0.17) == true; + assert lab_reap_pressure(6, 1, 0.16) == false; + // Under pressure the decision follows the age: `age < idle_s` keeps the directory, an age equal to the limit does not. + assert lab_reap_decision("a-target", 0, 8, false, true, 0.0, 100, 0, 2.0, 86400.0) == LAB_REAP_KEEP_RECENT; + assert lab_reap_decision("a-target", 0, 8, false, true, 86399.9, 100, 0, 2.0, 86400.0) == LAB_REAP_KEEP_RECENT; + assert lab_reap_decision("a-target", 0, 8, false, true, 86400.0, 100, 0, 2.0, 86400.0) == LAB_REAP_DELETE; + assert lab_reap_decision("a-target", 0, 8, false, true, 86400.1, 100, 0, 2.0, 86400.0) == LAB_REAP_DELETE; + assert lab_reap_decision("a-target", 0, 8, false, true, 1.0, 100, 0, 2.0, 1.0) == LAB_REAP_DELETE; + assert lab_reap_decision("a-target", 0, 8, false, true, 0.99, 100, 0, 2.0, 1.0) == LAB_REAP_KEEP_RECENT; + assert lab_reap_decision("a-target", 0, 8, false, true, 1000.0, 100, 0, 2.0, 1000.0) == LAB_REAP_DELETE; + assert lab_reap_decision("a-target", 0, 8, false, true, 999.999, 100, 0, 2.0, 1000.0) == LAB_REAP_KEEP_RECENT; + assert lab_reap_decision("a-target", 0, 8, false, true, 1000000000.0, 100, 0, 2.0, 86400.0) == LAB_REAP_DELETE; + assert lab_reap_decision("a-target", 0, 8, false, true, 5e-07, 100, 0, 2.0, 1e-06) == LAB_REAP_KEEP_RECENT; + assert lab_reap_decision("a-target", 0, 8, false, true, 0.0, 100, 0, 2.0, 0.0) == LAB_REAP_DELETE; + assert lab_reap_decision("a-target", 0, 8, false, true, 1.0, 100, 0, 2.0, 0.0) == LAB_REAP_DELETE; + assert LAB_REAP_MIN_FREE == 0.15 and LAB_REAP_IDLE_S == 86400.0 and LAB_KEEP_RUNS == 200; + assert lab_reap_pressure(100, 15, LAB_REAP_MIN_FREE) == false; + assert lab_reap_pressure(100, 14, LAB_REAP_MIN_FREE) == true; +} + +// >>> pinned table: prune_pressure +// <<< pinned table: prune_runs +// prune(): the runs at rank KEEP_RUNS or after, newest first by modification time, that no watched head points at. +test lab_py_table_prune { + assert lab_test_prune("", "", 200, ""); + assert lab_test_prune("", "", 0, ""); + assert lab_test_prune("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\t5\n", "", 0, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"); + assert lab_test_prune("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\t5\n", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", 0, ""); + assert lab_test_prune("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\t5\n", "", 1, ""); + assert lab_test_prune("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\t5\n", "", -1, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"); + assert lab_test_prune("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\t5\n", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", -1, ""); + assert lab_test_prune("ffffffffffffffffffffffffffffffffffffffff\t1000000034\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\t1000000068\ncccccccccccccccccccccccccccccccccccccccc\t1000000027\nx\t1000000021\n", "dddddddddddddddddddddddddddddddddddddddd\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\n", 1, "ffffffffffffffffffffffffffffffffffffffff\ncccccccccccccccccccccccccccccccccccccccc\nx\n"); + assert lab_test_prune("x\t1000000001\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\t1000000069\ndddddddddddddddddddddddddddddddddddddddd\t1000000052\nrun-1\t1000000070\n0123456789abcdef0123456789abcdef01234567\t1000000092\ny\t1000000068\n", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\n0123456789abcdef0123456789abcdef01234567\ny\n", -100, "x\ndddddddddddddddddddddddddddddddddddddddd\nrun-1\n"); + assert lab_test_prune("0123456789abcdef0123456789abcdef01234567\t1000000023\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\t1000000085\ndddddddddddddddddddddddddddddddddddddddd\t1000000033\nx\t1000000069\ny\t1000000083\n", "ffffffffffffffffffffffffffffffffffffffff\n", -5, "0123456789abcdef0123456789abcdef01234567\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ndddddddddddddddddddddddddddddddddddddddd\nx\ny\n"); + assert lab_test_prune("y\t1000000009\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\t1000000007\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\t1000000061\ncccccccccccccccccccccccccccccccccccccccc\t1000000010\n0123456789abcdef0123456789abcdef01234567\t1000000066\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\t1000000026\n", "y\nzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\n", 100, ""); + assert lab_test_prune("", "dddddddddddddddddddddddddddddddddddddddd\nffffffffffffffffffffffffffffffffffffffff\ny\nzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\n", 7, ""); + assert lab_test_prune("dddddddddddddddddddddddddddddddddddddddd\t1000000026\ny\t1000000002\nx\t1000000081\nffffffffffffffffffffffffffffffffffffffff\t1000000055\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\t1000000028\nzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\t1000000084\n0123456789abcdef0123456789abcdef01234567\t1000000029\n", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\ndddddddddddddddddddddddddddddddddddddddd\nffffffffffffffffffffffffffffffffffffffff\ny\nrun-1\n", 1, "x\n0123456789abcdef0123456789abcdef01234567\n"); + assert lab_test_prune("0123456789abcdef0123456789abcdef01234567\t1000000097\n", "0123456789abcdef0123456789abcdef01234567\nx\ny\n", 4, ""); + assert lab_test_prune("dddddddddddddddddddddddddddddddddddddddd\t1000000069\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\t1000000091\ny\t1000000040\nrun-2\t1000000061\nzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\t1000000039\n", "ffffffffffffffffffffffffffffffffffffffff\n0123456789abcdef0123456789abcdef01234567\nx\ny\nrun-1\nrun-2\nzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\n", 9, ""); + assert lab_test_prune("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\t1000000064\n", "cccccccccccccccccccccccccccccccccccccccc\n", 100, ""); + assert lab_test_prune("run-2\t1000000066\ndddddddddddddddddddddddddddddddddddddddd\t1000000045\nx\t1000000044\nrun-1\t1000000091\n", "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\nffffffffffffffffffffffffffffffffffffffff\n0123456789abcdef0123456789abcdef01234567\ny\n", -3, "run-2\ndddddddddddddddddddddddddddddddddddddddd\nx\n"); + assert lab_test_prune("0123456789abcdef0123456789abcdef01234567\t1000000081\nffffffffffffffffffffffffffffffffffffffff\t1000000017\ncccccccccccccccccccccccccccccccccccccccc\t1000000080\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\t1000000050\ndddddddddddddddddddddddddddddddddddddddd\t1000000064\nzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\t1000000039\nrun-2\t1000000013\ny\t1000000041\n", "dddddddddddddddddddddddddddddddddddddddd\nffffffffffffffffffffffffffffffffffffffff\nrun-2\n", -8, "0123456789abcdef0123456789abcdef01234567\ncccccccccccccccccccccccccccccccccccccccc\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\nzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\ny\n"); + assert lab_test_prune("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\t1000000036\nffffffffffffffffffffffffffffffffffffffff\t1000000097\nrun-2\t1000000071\ny\t1000000008\ndddddddddddddddddddddddddddddddddddddddd\t1000000091\n", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ncccccccccccccccccccccccccccccccccccccccc\nzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\n", -100, "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nffffffffffffffffffffffffffffffffffffffff\nrun-2\ny\ndddddddddddddddddddddddddddddddddddddddd\n"); + assert lab_test_prune("0123456789abcdef0123456789abcdef01234567\t1000000063\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\t1000000014\ndddddddddddddddddddddddddddddddddddddddd\t1000000049\n", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nx\ny\nrun-1\nrun-2\n", 200, ""); + assert lab_test_prune("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\t1000000046\nrun-1\t1000000063\n0123456789abcdef0123456789abcdef01234567\t1000000020\nx\t1000000036\nffffffffffffffffffffffffffffffffffffffff\t1000000080\n", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\n0123456789abcdef0123456789abcdef01234567\ny\nzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\n", 2, "x\n"); + assert lab_test_prune("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\t1000000025\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\t1000000083\nrun-1\t1000000030\ndddddddddddddddddddddddddddddddddddddddd\t1000000019\ny\t1000000066\nrun-2\t1000000028\n", "dddddddddddddddddddddddddddddddddddddddd\ny\nrun-2\nzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\n", -1, ""); + assert lab_test_prune("run-1\t1000000030\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\t1000000038\nffffffffffffffffffffffffffffffffffffffff\t1000000083\nzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\t1000000089\ndddddddddddddddddddddddddddddddddddddddd\t1000000040\ny\t1000000053\ncccccccccccccccccccccccccccccccccccccccc\t1000000056\n", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\ndddddddddddddddddddddddddddddddddddddddd\nx\nrun-2\n", -5, "run-1\ny\ncccccccccccccccccccccccccccccccccccccccc\n"); + assert lab_test_prune("0123456789abcdef0123456789abcdef01234567\t1000000041\ndddddddddddddddddddddddddddddddddddddddd\t1000000029\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\t1000000008\nrun-1\t1000000033\nrun-2\t1000000021\ny\t1000000035\ncccccccccccccccccccccccccccccccccccccccc\t1000000064\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\t1000000091\n", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\ncccccccccccccccccccccccccccccccccccccccc\n0123456789abcdef0123456789abcdef01234567\nrun-1\n", -5, "dddddddddddddddddddddddddddddddddddddddd\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nrun-2\ny\n"); + assert lab_test_prune("0123456789abcdef0123456789abcdef01234567\t1000000004\nx\t1000000007\nzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\t1000000031\nffffffffffffffffffffffffffffffffffffffff\t1000000083\nrun-2\t1000000058\nrun-1\t1000000080\n", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\nrun-2\n", -5, "0123456789abcdef0123456789abcdef01234567\nx\nzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\nrun-1\n"); + assert lab_test_prune("run-1\t1000000080\ncccccccccccccccccccccccccccccccccccccccc\t1000000037\nzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\t1000000022\n", "dddddddddddddddddddddddddddddddddddddddd\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\nrun-2\n", 5, ""); + assert lab_test_prune("", "dddddddddddddddddddddddddddddddddddddddd\n", 8, ""); + assert lab_test_prune("y\t1000000075\nrun-1\t1000000010\n0123456789abcdef0123456789abcdef01234567\t1000000079\ndddddddddddddddddddddddddddddddddddddddd\t1000000018\n", "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\n0123456789abcdef0123456789abcdef01234567\ny\nzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\n", 9, ""); + assert lab_test_prune("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\t1000000042\ndddddddddddddddddddddddddddddddddddddddd\t1000000006\nzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\t1000000047\n", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ncccccccccccccccccccccccccccccccccccccccc\n", 5, ""); + assert lab_test_prune("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\t1000000037\nffffffffffffffffffffffffffffffffffffffff\t1000000006\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\t1000000051\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\t1000000047\nrun-1\t1000000067\nzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\t1000000059\ncccccccccccccccccccccccccccccccccccccccc\t1000000017\ndddddddddddddddddddddddddddddddddddddddd\t1000000014\n", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ncccccccccccccccccccccccccccccccccccccccc\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\n", -5, "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\nffffffffffffffffffffffffffffffffffffffff\ndddddddddddddddddddddddddddddddddddddddd\n"); + assert lab_test_prune("run-2\t1000000065\nffffffffffffffffffffffffffffffffffffffff\t1000000023\n", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\n0123456789abcdef0123456789abcdef01234567\nrun-1\nzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\n", -5, "run-2\nffffffffffffffffffffffffffffffffffffffff\n"); + assert lab_test_prune("x\t1000000009\ny\t1000000014\ndddddddddddddddddddddddddddddddddddddddd\t1000000054\n", "y\n", -100, "x\ndddddddddddddddddddddddddddddddddddddddd\n"); + assert lab_test_prune("ffffffffffffffffffffffffffffffffffffffff\t1000000022\nrun-2\t1000000096\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\t1000000078\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\t1000000015\ndddddddddddddddddddddddddddddddddddddddd\t1000000058\nzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\t1000000088\nx\t1000000030\n", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ncccccccccccccccccccccccccccccccccccccccc\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\nffffffffffffffffffffffffffffffffffffffff\nx\ny\nrun-1\n", -9, "run-2\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\ndddddddddddddddddddddddddddddddddddddddd\nzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\n"); + assert lab_test_prune("0123456789abcdef0123456789abcdef01234567\t1000000069\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\t1000000070\nx\t1000000032\nrun-2\t1000000005\nzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\t1000000041\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\t1000000006\ncccccccccccccccccccccccccccccccccccccccc\t1000000049\n", "cccccccccccccccccccccccccccccccccccccccc\ny\nrun-2\n", 200, ""); + assert lab_test_prune("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\t1000000096\n", "run-2\n", 200, ""); + assert lab_test_prune("ffffffffffffffffffffffffffffffffffffffff\t1000000016\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\t1000000067\n0123456789abcdef0123456789abcdef01234567\t1000000072\nx\t1000000037\ndddddddddddddddddddddddddddddddddddddddd\t1000000087\nrun-1\t1000000066\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\t1000000097\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\t1000000056\n", "0123456789abcdef0123456789abcdef01234567\n", 9, ""); + assert lab_test_prune("", "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\nffffffffffffffffffffffffffffffffffffffff\n0123456789abcdef0123456789abcdef01234567\nx\nrun-1\nzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\n", 7, ""); + assert lab_test_prune("", "cccccccccccccccccccccccccccccccccccccccc\ndddddddddddddddddddddddddddddddddddddddd\nffffffffffffffffffffffffffffffffffffffff\nzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\n", -3, ""); + assert lab_test_prune("zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\t1000000026\nrun-2\t1000000015\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\t1000000085\n0123456789abcdef0123456789abcdef01234567\t1000000035\n", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\ncccccccccccccccccccccccccccccccccccccccc\nffffffffffffffffffffffffffffffffffffffff\n0123456789abcdef0123456789abcdef01234567\nx\nrun-1\n", -9, "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\nrun-2\n"); + assert lab_test_prune("ffffffffffffffffffffffffffffffffffffffff\t1000000019\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\t1000000072\ny\t1000000037\nrun-1\t1000000006\nzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\t1000000035\ncccccccccccccccccccccccccccccccccccccccc\t1000000005\n", "cccccccccccccccccccccccccccccccccccccccc\n", -5, "ffffffffffffffffffffffffffffffffffffffff\ny\nrun-1\nzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\n"); + assert lab_test_prune("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\t1000000012\ndddddddddddddddddddddddddddddddddddddddd\t1000000073\ncccccccccccccccccccccccccccccccccccccccc\t1000000020\nrun-1\t1000000079\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\t1000000080\ny\t1000000035\nzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\t1000000034\nffffffffffffffffffffffffffffffffffffffff\t1000000043\n", "dddddddddddddddddddddddddddddddddddddddd\nx\n", 100, ""); + assert lab_test_prune("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\t1000000068\n", "run-2\n", 9, ""); + assert lab_test_prune("zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\t1000000065\nrun-1\t1000000096\n", "dddddddddddddddddddddddddddddddddddddddd\n", 8, ""); + assert lab_test_prune("cccccccccccccccccccccccccccccccccccccccc\t1000000036\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\t1000000007\n", "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\nffffffffffffffffffffffffffffffffffffffff\n", 3, ""); + assert lab_test_prune("y\t1000000055\n", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n0123456789abcdef0123456789abcdef01234567\ny\n", 5, ""); + assert lab_test_prune("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\t1000000062\ncccccccccccccccccccccccccccccccccccccccc\t1000000035\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\t1000000073\nrun-1\t1000000087\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\t1000000026\nx\t1000000076\ny\t1000000095\n", "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\n", -1, "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n"); + assert lab_test_prune("zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\t1000000060\nffffffffffffffffffffffffffffffffffffffff\t1000000066\ny\t1000000056\n", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\nx\nrun-1\nrun-2\nzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\n", 1, "y\n"); + assert lab_test_prune("", "", 1, ""); + assert lab_test_prune("dddddddddddddddddddddddddddddddddddddddd\t1000000054\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\t1000000013\n0123456789abcdef0123456789abcdef01234567\t1000000036\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\t1000000075\ny\t1000000092\nx\t1000000021\n", "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\n", -100, "dddddddddddddddddddddddddddddddddddddddd\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n0123456789abcdef0123456789abcdef01234567\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\ny\nx\n"); + assert lab_test_prune("", "ffffffffffffffffffffffffffffffffffffffff\n0123456789abcdef0123456789abcdef01234567\n", -2, ""); + assert lab_test_prune("zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\t1000000004\ncccccccccccccccccccccccccccccccccccccccc\t1000000040\nrun-2\t1000000044\nx\t1000000057\n0123456789abcdef0123456789abcdef01234567\t1000000092\n", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nffffffffffffffffffffffffffffffffffffffff\nrun-1\n", 2, "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\ncccccccccccccccccccccccccccccccccccccccc\nrun-2\n"); + assert lab_test_prune("x\t1000000061\n0123456789abcdef0123456789abcdef01234567\t1000000078\nffffffffffffffffffffffffffffffffffffffff\t1000000069\ndddddddddddddddddddddddddddddddddddddddd\t1000000042\nzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\t1000000017\n", "dddddddddddddddddddddddddddddddddddddddd\ny\nrun-1\n", 8, ""); + assert lab_test_prune("run-1\t1000000094\ny\t1000000011\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\t1000000078\ncccccccccccccccccccccccccccccccccccccccc\t1000000053\ndddddddddddddddddddddddddddddddddddddddd\t1000000058\n", "cccccccccccccccccccccccccccccccccccccccc\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\ny\nrun-1\n", 200, ""); + assert lab_test_prune("ffffffffffffffffffffffffffffffffffffffff\t1000000015\ny\t1000000011\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\t1000000051\neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\t1000000027\nrun-1\t1000000058\nzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\t1000000041\n0123456789abcdef0123456789abcdef01234567\t1000000081\ndddddddddddddddddddddddddddddddddddddddd\t1000000073\n", "x\nrun-1\nzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\n", 9, ""); + assert lab_test_prune("run-1\t1000000072\nffffffffffffffffffffffffffffffffffffffff\t1000000066\ndddddddddddddddddddddddddddddddddddddddd\t1000000037\nzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\t1000000098\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\t1000000077\n", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ndddddddddddddddddddddddddddddddddddddddd\n", 9, ""); + assert lab_test_prune("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\t1000000028\n0123456789abcdef0123456789abcdef01234567\t1000000057\ncccccccccccccccccccccccccccccccccccccccc\t1000000071\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\t1000000056\nx\t1000000000\ndddddddddddddddddddddddddddddddddddddddd\t1000000017\n", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", -2, "x\ndddddddddddddddddddddddddddddddddddddddd\n"); + assert lab_test_prune("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\t1000000099\nzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\t1000000080\n0123456789abcdef0123456789abcdef01234567\t1000000025\ndddddddddddddddddddddddddddddddddddddddd\t1000000002\nx\t1000000050\ncccccccccccccccccccccccccccccccccccccccc\t1000000044\ny\t1000000015\n", "cccccccccccccccccccccccccccccccccccccccc\nffffffffffffffffffffffffffffffffffffffff\n", 2, "0123456789abcdef0123456789abcdef01234567\ndddddddddddddddddddddddddddddddddddddddd\nx\ny\n"); +} + +// prune(): the slice start of runs[KEEP_RUNS:], for every length and every sign. +test lab_py_table_the_slice_start { + assert lab_prune_start(0, -9) == 0; + assert lab_prune_start(0, -7) == 0; + assert lab_prune_start(0, -6) == 0; + assert lab_prune_start(0, -3) == 0; + assert lab_prune_start(0, -2) == 0; + assert lab_prune_start(0, -1) == 0; + assert lab_prune_start(0, 0) == 0; + assert lab_prune_start(0, 1) == 0; + assert lab_prune_start(0, 2) == 0; + assert lab_prune_start(0, 5) == 0; + assert lab_prune_start(0, 6) == 0; + assert lab_prune_start(0, 7) == 0; + assert lab_prune_start(0, 200) == 0; + assert lab_prune_start(1, -9) == 0; + assert lab_prune_start(1, -7) == 0; + assert lab_prune_start(1, -6) == 0; + assert lab_prune_start(1, -3) == 0; + assert lab_prune_start(1, -2) == 0; + assert lab_prune_start(1, -1) == 0; + assert lab_prune_start(1, 0) == 0; + assert lab_prune_start(1, 1) == 1; + assert lab_prune_start(1, 2) == 1; + assert lab_prune_start(1, 5) == 1; + assert lab_prune_start(1, 6) == 1; + assert lab_prune_start(1, 7) == 1; + assert lab_prune_start(1, 200) == 1; + assert lab_prune_start(2, -9) == 0; + assert lab_prune_start(2, -7) == 0; + assert lab_prune_start(2, -6) == 0; + assert lab_prune_start(2, -3) == 0; + assert lab_prune_start(2, -2) == 0; + assert lab_prune_start(2, -1) == 1; + assert lab_prune_start(2, 0) == 0; + assert lab_prune_start(2, 1) == 1; + assert lab_prune_start(2, 2) == 2; + assert lab_prune_start(2, 5) == 2; + assert lab_prune_start(2, 6) == 2; + assert lab_prune_start(2, 7) == 2; + assert lab_prune_start(2, 200) == 2; + assert lab_prune_start(3, -9) == 0; + assert lab_prune_start(3, -7) == 0; + assert lab_prune_start(3, -6) == 0; + assert lab_prune_start(3, -3) == 0; + assert lab_prune_start(3, -2) == 1; + assert lab_prune_start(3, -1) == 2; + assert lab_prune_start(3, 0) == 0; + assert lab_prune_start(3, 1) == 1; + assert lab_prune_start(3, 2) == 2; + assert lab_prune_start(3, 5) == 3; + assert lab_prune_start(3, 6) == 3; + assert lab_prune_start(3, 7) == 3; + assert lab_prune_start(3, 200) == 3; + assert lab_prune_start(4, -9) == 0; + assert lab_prune_start(4, -7) == 0; + assert lab_prune_start(4, -6) == 0; + assert lab_prune_start(4, -3) == 1; + assert lab_prune_start(4, -2) == 2; + assert lab_prune_start(4, -1) == 3; + assert lab_prune_start(4, 0) == 0; + assert lab_prune_start(4, 1) == 1; + assert lab_prune_start(4, 2) == 2; + assert lab_prune_start(4, 5) == 4; + assert lab_prune_start(4, 6) == 4; + assert lab_prune_start(4, 7) == 4; + assert lab_prune_start(4, 200) == 4; + assert lab_prune_start(5, -9) == 0; + assert lab_prune_start(5, -7) == 0; + assert lab_prune_start(5, -6) == 0; + assert lab_prune_start(5, -3) == 2; + assert lab_prune_start(5, -2) == 3; + assert lab_prune_start(5, -1) == 4; + assert lab_prune_start(5, 0) == 0; + assert lab_prune_start(5, 1) == 1; + assert lab_prune_start(5, 2) == 2; + assert lab_prune_start(5, 5) == 5; + assert lab_prune_start(5, 6) == 5; + assert lab_prune_start(5, 7) == 5; + assert lab_prune_start(5, 200) == 5; + assert lab_prune_start(6, -9) == 0; + assert lab_prune_start(6, -7) == 0; + assert lab_prune_start(6, -6) == 0; + assert lab_prune_start(6, -3) == 3; + assert lab_prune_start(6, -2) == 4; + assert lab_prune_start(6, -1) == 5; + assert lab_prune_start(6, 0) == 0; + assert lab_prune_start(6, 1) == 1; + assert lab_prune_start(6, 2) == 2; + assert lab_prune_start(6, 5) == 5; + assert lab_prune_start(6, 6) == 6; + assert lab_prune_start(6, 7) == 6; + assert lab_prune_start(6, 200) == 6; + assert lab_prune_start(5, -9223372036854775807) == 0 and lab_prune_start(5, 9223372036854775807) == 5; +} + +// >>> pinned table: prune_runs + +/// The volumes of the grid below, in KiB. +const LAB_TEST_TOTALS : [5]u64 = [104857600, 47745772, 6000000, 50000000, 2030337424]; + +// Scenario: specs/claw/lab_disk.t27 holds a check at under 1 GiB free and warns under 5 GiB or 10%; this reaper starts at 15%. +// The tick-40 volume (47745772 KiB, 0 free) is full for the gate and under pressure for the reaper; a volume at 12% free is +// still `go` for the gate while the reaper already works; and over the whole grid, whatever the gate calls low by the percent +// line (avail * 10 < blocks) the reaper is already running on, and a full volume of 7 GiB or more is under pressure too. +test the_reaper_starts_before_the_lab_disk_gate_holds_a_check { + assert disk_reason(47745772, 0) == R_FULL; + assert lab_reap_pressure(47745772 * 1024, 0, LAB_REAP_MIN_FREE) == true; + assert disk_reason(104857600, 12582912) == R_OK; + assert lab_reap_pressure(104857600 * 1024, 12582912 * 1024, LAB_REAP_MIN_FREE) == true; + assert disk_reason(104857600, 9437184) == R_LOW; + assert lab_reap_pressure(104857600 * 1024, 9437184 * 1024, LAB_REAP_MIN_FREE) == true; + assert disk_reason(104857600, 20971520) == R_OK; + assert lab_reap_pressure(104857600 * 1024, 20971520 * 1024, LAB_REAP_MIN_FREE) == false; + var i : usize = 0; + while (i < 5) { + const total : u64 = LAB_TEST_TOTALS[i]; + var pct : u64 = 0; + while (pct <= 100) { + const avail : u64 = total * pct / 100; + const under_ten : bool = avail * 10 < total; + if (under_ten) { + assert lab_reap_pressure(total * 1024, avail * 1024, LAB_REAP_MIN_FREE) == true; + } + if (disk_reason(total, avail) == R_FULL and total >= 7340032) { + assert lab_reap_pressure(total * 1024, avail * 1024, LAB_REAP_MIN_FREE) == true; + } + if (disk_reason(total, avail) == R_OK) { + // `go` means 10% or more free and 5 GiB or more; the reaper may still be running between 10% and 15%. + assert under_ten == false; + } + pct = pct + 1; + } + i = i + 1; + } +} + +// Scenario: equal modification times rank in listing order (Python's reverse sort is stable; which order the directory lists +// them in is the file system's, so this is the port's rule, not a Python answer); a line that is not a run is not counted. +test equal_times_rank_in_listing_order_and_a_bad_line_is_no_run { + assert lab_test_prune("x\t5\ny\t5\nz\t5\n", "", 1, "y\nz\n"); + assert lab_test_prune("x\t5\ny\t5\nz\t5\n", "", 2, "z\n"); + assert lab_test_prune("x\t5\ny\t5\nz\t5\n", "y\n", 1, "z\n"); + assert lab_run_count("x\t5\nbad\ny\t\nz\t12a\nw\t9\n") == 2; + assert lab_run_count("") == 0; + assert lab_run_count("\n") == 0; + assert lab_run_count("x\t1234567890123456789\n") == 0; + assert lab_run_count("x\t999999999999999999\n") == 1; + assert lab_run_rank("x\t5\nbad\nw\t9\n", 0) == 1; + assert lab_run_rank("x\t5\nbad\nw\t9\n", 8) == 0; + assert lab_test_prune("x\t5\nbad\ny\t\nz\t12a\nw\t9\n", "", 1, "x\n"); + assert lab_test_prune("x\t5\nw\t9\n", "x\nw\n", 0, ""); + assert lab_test_prune("x\t5\nw\t9\n", "xx\nw \n", 0, "x\nw\n"); +} + +// Scenario: the scratch name is the last seven bytes of the entry's name, not a prefix and not a word inside it. +test a_scratch_target_name_ends_in_dash_target { + assert lab_scratch_name("xx a-target yy", 3, 11) == true; + assert lab_scratch_name("-target", 0, 7) == true; + assert lab_scratch_name("target", 0, 6) == false; + assert lab_scratch_name("xtarget", 0, 7) == false; + assert lab_scratch_name("-targe", 0, 6) == false; + assert lab_scratch_name("a-targets", 0, 9) == false; + assert lab_scratch_name("a-target", 3, 1) == false; + assert lab_scratch_name("", 0, 0) == false; +} diff --git a/specs/lab/text.t27 b/specs/lab/text.t27 new file mode 100644 index 0000000000..7b228ea956 --- /dev/null +++ b/specs/lab/text.t27 @@ -0,0 +1,549 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/lab/text.t27 -- the byte-text readers the lab specs (plan, paths, verdict, prune) share (gHashTag/t27#11041, epic #11040) +; The t27c lab (infra/t27c-lab/lab.py) reads text a person or a command wrote: `git ls-remote` output, a gate's log, +; a request path. Python's own text rules decide what counts as a line, as blank, and as a character, and the lab's +; verdicts rest on them: str.splitlines() breaks on ten characters (not only LF), str.strip() and the regex `\s` +; strip thirty (the Unicode spaces), and `[:300]` counts characters, not bytes. This file reads those rules off +; Python 3.14 (python3 -c, every code point checked) and holds them once, as functions of a byte text. +; The text is UTF-8 bytes (the lab decodes a command's output with errors=replace before it reads it). A string is +; `string`: `.len` and `t[i]` as a u8. A span is a `from`/`to` pair of offsets into one text; nothing is copied until a +; writer puts bytes into a caller's buffer, and a writer returns the new cursor, which still advances when the +; buffer is full (the claw write idiom), so a short buffer tells how long the output is. +; The break and space sets: +; line breaks LF CR CR LF VT FF FS GS RS U+0085 (C2 85) U+2028 (E2 80 A8) U+2029 (E2 80 A9) +; spaces HT LF VT FF CR FS GS RS US SP U+0085 U+00A0 U+1680 U+2000..U+200A U+2028 U+2029 U+202F U+205F U+3000 +; Hand-written is this file; the lab's own Python is the oracle until the cutover (#11040). +; ASCII only (L3) +; phi^2 + 1/phi^2 = 3 | TRINITY + +module LabText; + +use tri::lab::receipt::span_is; + +/// "No such position." +pub const LAB_NO : usize = 4294967295; + +/// The most digits of a count the lab reads; a longer run is not a count (Python would read it, the lab's logs do not carry one). +pub const LAB_DEC_DIGITS : usize = 18; +/// "Not a decimal count." +pub const LAB_DEC_BAD : u64 = 18446744073709551615; + +/// The length in bytes of the line break that starts at i (str.splitlines): 0 when none does. +pub fn lab_break_len(t: string, i: usize) -> usize { + if (i >= t.len) { return 0; } + const c : u8 = t[i]; + if (c == 13) { + if (i + 1 < t.len and t[i + 1] == 10) { return 2; } + return 1; + } + if (c == 10 or c == 11 or c == 12 or c == 28 or c == 29 or c == 30) { return 1; } + if (c == 194 and i + 1 < t.len and t[i + 1] == 133) { return 2; } + if (c == 226 and i + 2 < t.len and t[i + 1] == 128 and (t[i + 2] == 168 or t[i + 2] == 169)) { return 3; } + return 0; +} + +/// The end of the line that starts at s: the offset of its break, or t.len. +pub fn lab_line_end(t: string, s: usize) -> usize { + var k : usize = s; + while (k < t.len and lab_break_len(t, k) == 0) { + k = k + 1; + } + return k; +} + +/// Where the next line starts, given the end e of this one: past its break. A text that ends in a break has no +/// empty last line, as in Python: the loop `while s < t.len` stops there. +pub fn lab_line_next(t: string, e: usize) -> usize { + return e + lab_break_len(t, e); +} + +/// The length in bytes of the space (str.isspace) that starts at i, not reading past end: 0 when none does. +pub fn lab_space_len(t: string, i: usize, end: usize) -> usize { + if (i >= end or i >= t.len) { return 0; } + const c : u8 = t[i]; + if (c == 32 or (c >= 9 and c <= 13) or (c >= 28 and c <= 31)) { return 1; } + if (c == 194 and i + 1 < end and i + 1 < t.len and (t[i + 1] == 133 or t[i + 1] == 160)) { return 2; } + if (i + 2 >= end or i + 2 >= t.len) { return 0; } + const d : u8 = t[i + 1]; + const e : u8 = t[i + 2]; + if (c == 225 and d == 154 and e == 128) { return 3; } + if (c == 226 and d == 128 and ((e >= 128 and e <= 138) or e == 168 or e == 169 or e == 175)) { return 3; } + if (c == 226 and d == 129 and e == 159) { return 3; } + if (c == 227 and d == 128 and e == 128) { return 3; } + return 0; +} + +/// The length of the space that ends at i, not reading before lo: 0 when none does. +pub fn lab_space_before(t: string, lo: usize, i: usize) -> usize { + var n : usize = 1; + while (n <= 3 and i >= lo + n) { + if (lab_space_len(t, i - n, i) == n) { return n; } + n = n + 1; + } + return 0; +} + +/// Where text[from..to] starts once its leading spaces are stripped (str.lstrip). +pub fn lab_strip_from(t: string, from: usize, to: usize) -> usize { + var k : usize = from; + while (k < to) { + const n : usize = lab_space_len(t, k, to); + if (n == 0) { return k; } + k = k + n; + } + return k; +} + +/// Where text[from..to] ends once its trailing spaces are stripped (str.rstrip). +pub fn lab_strip_to(t: string, from: usize, to: usize) -> usize { + var k : usize = to; + while (k > from) { + const n : usize = lab_space_before(t, from, k); + if (n == 0) { return k; } + k = k - n; + } + return k; +} + +/// Does text[from..to] hold anything but spaces? (`ln.strip()` is truthy.) +pub fn lab_has_text(t: string, from: usize, to: usize) -> bool { + return lab_strip_from(t, from, to) < to; +} + +/// The length in bytes of the character (UTF-8) that starts at i, clipped at end; a stray continuation byte is one. +pub fn lab_char_len(t: string, i: usize, end: usize) -> usize { + if (i >= end or i >= t.len) { return 0; } + const c : u8 = t[i]; + var n : usize = 1; + if (c >= 192 and c < 224) { n = 2; } + if (c >= 224 and c < 240) { n = 3; } + if (c >= 240) { n = 4; } + var k : usize = 1; + while (k < n and i + k < end and i + k < t.len and t[i + k] >= 128 and t[i + k] < 192) { + k = k + 1; + } + return k; +} + +/// The code point of the character that starts at i (see lab_char_len); a broken sequence is its first byte. +pub fn lab_char_val(t: string, i: usize, end: usize) -> u32 { + const n : usize = lab_char_len(t, i, end); + const c : u32 = t[i] as u32; + if (n <= 1) { return c; } + var v : u32 = c; + var want : usize = 4; + if (c < 224) { want = 2; v = c - 192; } + if (c >= 224 and c < 240) { want = 3; v = c - 224; } + if (c >= 240) { v = c - 240; } + if (n != want) { return c; } + var k : usize = 1; + while (k < n) { + v = v * 64 + ((t[i + k] as u32) - 128); + k = k + 1; + } + return v; +} + +/// The UTF-8 bytes of code point cp into dst at w, clipped at cap; the new cursor. +pub fn lab_put_char(dst: []u8, cap: usize, w: usize, cp: u32) -> usize { + var b : [4]u8 = undefined; + var n : usize = 1; + if (cp < 128) { + b[0] = cp as u8; + } else { + if (cp < 2048) { + b[0] = (192 + cp / 64) as u8; + b[1] = (128 + cp % 64) as u8; + n = 2; + } else { + if (cp < 65536) { + b[0] = (224 + cp / 4096) as u8; + b[1] = (128 + (cp / 64) % 64) as u8; + b[2] = (128 + cp % 64) as u8; + n = 3; + } else { + b[0] = (240 + cp / 262144) as u8; + b[1] = (128 + (cp / 4096) % 64) as u8; + b[2] = (128 + (cp / 64) % 64) as u8; + b[3] = (128 + cp % 64) as u8; + n = 4; + } + } + } + var k : usize = 0; + while (k < n) { + if (w + k < cap) { dst[w + k] = b[k]; } + k = k + 1; + } + return w + n; +} + +/// The end of text[from..to] cut to at most n characters (Python's `s[:n]`). +pub fn lab_cut_chars(t: string, from: usize, to: usize, n: usize) -> usize { + var k : usize = from; + var seen : usize = 0; + while (k < to and seen < n) { + k = k + lab_char_len(t, k, to); + seen = seen + 1; + } + return k; +} + +/// Does lit start at offset i of text, without reading past `to`? +pub fn lab_lit_at(t: string, i: usize, to: usize, lit: string) -> bool { + if (to > t.len or i + lit.len > to) { return false; } + return span_is(t, i, i + lit.len, lit); +} + +/// Does text[from..to] hold lit anywhere (`lit in s`)? The empty literal is in every span. +pub fn lab_has_lit(t: string, from: usize, to: usize, lit: string) -> bool { + var k : usize = from; + while (k + lit.len <= to) { + if (lab_lit_at(t, k, to, lit)) { return true; } + k = k + 1; + } + return false; +} + +/// Is text[a_from..a_to] byte for byte text[b_from..b_to] of the second text? +pub fn lab_same_span(a: string, a_from: usize, a_to: usize, b: string, b_from: usize, b_to: usize) -> bool { + if (a_to < a_from or b_to < b_from or a_to - a_from != b_to - b_from) { return false; } + var k : usize = 0; + while (a_from + k < a_to) { + if (a[a_from + k] != b[b_from + k]) { return false; } + k = k + 1; + } + return true; +} + +/// Is t[tf..tt] byte for byte one of the lines of the set text? (A sha is `in` a set of shas; lines as splitlines() cuts them.) +pub fn lab_set_has(set: string, t: string, tf: usize, tt: usize) -> bool { + var s : usize = 0; + while (s < set.len) { + const e : usize = lab_line_end(set, s); + if (lab_same_span(set, s, e, t, tf, tt)) { return true; } + s = lab_line_next(set, e); + } + return false; +} + +/// Where text[a_from..a_to] sorts against text[b_from..b_to]: -1, 0 or 1, byte by byte (UTF-8 bytes sort as code points, as +/// Python's str does), a prefix before the longer text. +pub fn lab_span_cmp(a: string, a_from: usize, a_to: usize, b: string, b_from: usize, b_to: usize) -> i32 { + var k : usize = 0; + while (a_from + k < a_to and b_from + k < b_to) { + const x : u8 = a[a_from + k]; + const y : u8 = b[b_from + k]; + if (x < y) { return -1; } + if (x > y) { return 1; } + k = k + 1; + } + if (a_to - a_from < b_to - b_from) { return -1; } + if (a_to - a_from > b_to - b_from) { return 1; } + return 0; +} + +/// The first offset in text[from..to] of the byte c, or LAB_NO. +pub fn lab_find_byte(t: string, from: usize, to: usize, c: u8) -> usize { + var k : usize = from; + while (k < to and k < t.len) { + if (t[k] == c) { return k; } + k = k + 1; + } + return LAB_NO; +} + +/// Is text[from..to] one or more ASCII digits, at most LAB_DEC_DIGITS of them? +pub fn lab_is_count(t: string, from: usize, to: usize) -> bool { + if (to <= from or to - from > LAB_DEC_DIGITS) { return false; } + var k : usize = from; + while (k < to) { + if (t[k] < 48 or t[k] > 57) { return false; } + k = k + 1; + } + return true; +} + +/// The count text[from..to] holds, or LAB_DEC_BAD when it is not a count (lab_is_count). +pub fn lab_count_of(t: string, from: usize, to: usize) -> u64 { + if (lab_is_count(t, from, to) == false) { return LAB_DEC_BAD; } + var n : u64 = 0; + var k : usize = from; + while (k < to) { + n = n * 10 + ((t[k] as u64) - 48); + k = k + 1; + } + return n; +} + +/// Copy text[from..to] into dst at w, clipped at cap; the new cursor, which advances by the whole span. +pub fn lab_put_span(dst: []u8, cap: usize, w: usize, t: string, from: usize, to: usize) -> usize { + var k : usize = from; + while (k < to) { + if (w + (k - from) < cap) { dst[w + (k - from)] = t[k]; } + k = k + 1; + } + return w + (to - from); +} + +/// Copy the literal lit into dst at w, clipped at cap; the new cursor. +pub fn lab_put_lit(dst: []u8, cap: usize, w: usize, lit: string) -> usize { + return lab_put_span(dst, cap, w, lit, 0, lit.len); +} + +/// The decimal digits of v into dst at w, clipped at cap; the new cursor. (claw::text put_count takes a u32; a lab count is a u64.) +pub fn lab_put_count(dst: []u8, cap: usize, w: usize, v: u64) -> usize { + var digits : [20]u8 = undefined; + var n : usize = 0; + var x : u64 = v; + if (x == 0) { + digits[0] = 48; + n = 1; + } + while (x > 0) { + digits[n] = 48 + ((x % 10) as u8); + n = n + 1; + x = x / 10; + } + var k : usize = 0; + while (k < n) { + if (w + k < cap) { dst[w + k] = digits[n - 1 - k]; } + k = k + 1; + } + return w + n; +} + +// ---- tests: the rules read off Python 3.14 ---- + +/// A buffer of four zero bytes holding the UTF-8 of cp; the spans are read against its whole length. +fn lab_test_encode(b: []u8, cp: u32) -> usize { + b[0] = 0; + b[1] = 0; + b[2] = 0; + b[3] = 0; + return lab_put_char(b, 4, 0, cp); +} + +test the_ten_characters_python_breaks_lines_on_break_and_no_other { + // python3 -c: [hex(c) for c in range(0x110000) if len(('a'+chr(c)+'b').splitlines()) > 1] + var b : [4]u8 = undefined; + var cp : u32 = 0; + var breaks : u32 = 0; + while (cp < 12544) { + const n : usize = lab_test_encode(&b, cp); + const k : usize = lab_break_len(&b, 0); + if (k > 0) { + breaks = breaks + 1; + // A break is the whole character, never a part of one (CR alone is CR, its LF is not in the buffer). + assert k == n; + assert cp == 10 or cp == 11 or cp == 12 or cp == 13 or cp == 28 or cp == 29 or cp == 30 or cp == 133 or cp == 8232 or cp == 8233; + } + cp = cp + 1; + } + assert breaks == 10; +} + +test the_twenty_nine_characters_python_calls_a_space_are_spaces_and_no_other { + // python3 -c: [hex(c) for c in range(0x110000) if chr(c).isspace()] -- the same set the regex \s matches + var b : [4]u8 = undefined; + var cp : u32 = 0; + var spaces : u32 = 0; + while (cp < 12544) { + const n : usize = lab_test_encode(&b, cp); + const k : usize = lab_space_len(&b, 0, 4); + if (k > 0) { + spaces = spaces + 1; + assert k == n; + assert lab_space_before(&b, 0, n) == n; + } else { + assert lab_space_before(&b, 0, n) == 0; + } + cp = cp + 1; + } + assert spaces == 29; +} + +test a_character_round_trips_through_its_utf8_bytes { + var b : [4]u8 = undefined; + var cp : u32 = 0; + while (cp < 70000) { + const n : usize = lab_test_encode(&b, cp); + assert lab_char_len(&b, 0, 4) == n; + assert lab_char_val(&b, 0, 4) == cp; + cp = cp + 997; + } + assert lab_test_encode(&b, 65) == 1; + assert lab_test_encode(&b, 233) == 2; + assert lab_test_encode(&b, 8364) == 3; + assert lab_test_encode(&b, 128512) == 4; +} + +test a_line_ends_at_its_first_break_and_the_next_starts_past_it { + assert lab_line_end("ab\ncd", 0) == 2; + assert lab_line_next("ab\ncd", 2) == 3; + assert lab_line_end("ab\ncd", 3) == 5; + assert lab_line_next("ab\ncd", 5) == 5; + assert lab_line_end("", 0) == 0; + assert lab_line_end("\n", 0) == 0; + assert lab_line_next("\n", 0) == 1; + assert lab_line_end("ab", 7) == 7; + var crlf : [4]u8 = undefined; + crlf[0] = 97; + crlf[1] = 13; + crlf[2] = 10; + crlf[3] = 98; + assert lab_break_len(&crlf, 1) == 2; + assert lab_line_end(&crlf, 0) == 1; + assert lab_line_next(&crlf, 1) == 3; + var cr : [3]u8 = undefined; + cr[0] = 97; + cr[1] = 13; + cr[2] = 98; + assert lab_break_len(&cr, 1) == 1; + assert lab_line_next(&cr, 1) == 2; + var nel : [4]u8 = undefined; + nel[0] = 97; + nel[1] = 194; + nel[2] = 133; + nel[3] = 98; + assert lab_line_end(&nel, 0) == 1; + assert lab_line_next(&nel, 1) == 3; + // The lead byte of U+0085 at the very end of the text is not a break: it has no second byte. + var cut : [2]u8 = undefined; + cut[0] = 97; + cut[1] = 194; + assert lab_break_len(&cut, 1) == 0; + assert lab_line_end(&cut, 0) == 2; +} + +test strip_takes_the_unicode_spaces_off_both_ends { + assert lab_strip_from(" ab ", 0, 5) == 2; + assert lab_strip_to(" ab ", 0, 5) == 4; + assert lab_strip_from(" ", 0, 3) == 3; + assert lab_strip_to(" ", 0, 3) == 0; + assert lab_strip_from("ab", 0, 2) == 0; + assert lab_strip_to("ab", 0, 2) == 2; + assert lab_strip_to("a b", 0, 3) == 3; + assert lab_has_text(" x ", 0, 4) == true; + assert lab_has_text(" ", 0, 4) == false; + assert lab_has_text("", 0, 0) == false; + assert lab_has_text("a", 1, 1) == false; + // U+00A0 and U+3000 are spaces for strip(), U+200B (zero width space) is not. + var b : [10]u8 = undefined; + b[0] = 194; + b[1] = 160; + b[2] = 120; + b[3] = 227; + b[4] = 128; + b[5] = 128; + b[6] = 226; + b[7] = 128; + b[8] = 139; + b[9] = 32; + assert lab_strip_from(&b, 0, 10) == 2; + assert lab_strip_to(&b, 0, 10) == 9; + assert lab_has_text(&b, 0, 2) == false; + assert lab_has_text(&b, 6, 9) == true; +} + +test the_cut_counts_characters_not_bytes { + assert lab_cut_chars("abcdef", 0, 6, 3) == 3; + assert lab_cut_chars("abcdef", 0, 6, 0) == 0; + assert lab_cut_chars("abcdef", 0, 6, 99) == 6; + assert lab_cut_chars("abcdef", 2, 6, 2) == 4; + assert lab_cut_chars("", 0, 0, 5) == 0; + var b : [7]u8 = undefined; + b[0] = 97; + b[1] = 195; + b[2] = 169; + b[3] = 226; + b[4] = 130; + b[5] = 172; + b[6] = 98; + assert lab_cut_chars(&b, 0, 7, 1) == 1; + assert lab_cut_chars(&b, 0, 7, 2) == 3; + assert lab_cut_chars(&b, 0, 7, 3) == 6; + assert lab_cut_chars(&b, 0, 7, 4) == 7; + // A cut never ends inside a character, even when `to` does: the character is clipped at `to`. + assert lab_cut_chars(&b, 0, 5, 3) == 5; +} + +test a_literal_is_found_at_an_offset_or_anywhere_in_a_span { + assert lab_lit_at("abcdef", 2, 6, "cd") == true; + assert lab_lit_at("abcdef", 2, 6, "ce") == false; + assert lab_lit_at("abcdef", 4, 6, "efg") == false; + assert lab_lit_at("abcdef", 4, 5, "ef") == false; + assert lab_lit_at("abcdef", 0, 99, "a") == false; + assert lab_has_lit("a pair(s) are silent", 0, 20, "pair(s) are silent") == true; + assert lab_has_lit("a pair(s) are silent", 0, 19, "pair(s) are silent") == false; + assert lab_has_lit("abc", 0, 3, "") == true; + assert lab_has_lit("", 0, 0, "") == true; + assert lab_has_lit("abc", 0, 3, "abcd") == false; + assert lab_has_lit("abcabd", 1, 6, "abd") == true; + assert lab_has_lit("abcabd", 1, 5, "abd") == false; +} + +test two_spans_are_the_same_text_or_not { + assert lab_same_span("abc", 0, 3, "xabc", 1, 4) == true; + assert lab_same_span("abc", 0, 3, "xabd", 1, 4) == false; + assert lab_same_span("abc", 0, 2, "abc", 0, 3) == false; + assert lab_same_span("", 0, 0, "", 0, 0) == true; + assert lab_same_span("abc", 2, 1, "abc", 2, 1) == false; + assert lab_set_has("a\nbc\nd", "xbcx", 1, 3) == true; + assert lab_set_has("a\nbc\nd", "xbcx", 1, 2) == false; + assert lab_set_has("a\nbc\nd", "d", 0, 1) == true; + assert lab_set_has("a\nbc\nd\n", "", 0, 0) == false; + assert lab_set_has("a\n\nd", "", 0, 0) == true; + assert lab_set_has("", "a", 0, 1) == false; + assert lab_set_has("", "", 0, 0) == false; + assert lab_span_cmp("abc", 0, 3, "abd", 0, 3) == -1; + assert lab_span_cmp("abd", 0, 3, "abc", 0, 3) == 1; + assert lab_span_cmp("abc", 0, 3, "xabc", 1, 4) == 0; + assert lab_span_cmp("ab", 0, 2, "abc", 0, 3) == -1; + assert lab_span_cmp("abc", 0, 3, "ab", 0, 2) == 1; + assert lab_span_cmp("", 0, 0, "", 0, 0) == 0; + assert lab_span_cmp("", 0, 0, "a", 0, 1) == -1; + assert lab_span_cmp("B", 0, 1, "a", 0, 1) == -1; + assert lab_find_byte("a\tb", 0, 3, 9) == 1; + assert lab_find_byte("a\tb", 2, 3, 9) == LAB_NO; + assert lab_find_byte("a\tb", 0, 1, 9) == LAB_NO; + assert lab_find_byte("", 0, 0, 9) == LAB_NO; +} + +test a_count_is_one_to_eighteen_ascii_digits { + assert lab_count_of("12", 0, 2) == 12; + assert lab_count_of("x007y", 1, 4) == 7; + assert lab_count_of("0", 0, 1) == 0; + assert lab_count_of("999999999999999999", 0, 18) == 999999999999999999; + assert lab_count_of("1000000000000000000", 0, 19) == LAB_DEC_BAD; + assert lab_count_of("", 0, 0) == LAB_DEC_BAD; + assert lab_count_of("1a", 0, 2) == LAB_DEC_BAD; + assert lab_count_of("-1", 0, 2) == LAB_DEC_BAD; + assert lab_count_of(" 1", 0, 2) == LAB_DEC_BAD; + assert lab_is_count("123", 0, 3) == true; + assert lab_is_count("123", 0, 0) == false; +} + +test the_writers_return_the_cursor_and_clip_at_the_cap { + var out : [12]u8 = undefined; + var w : usize = lab_put_lit(&out, 12, 0, "ab"); + assert w == 2; + w = lab_put_span(&out, 12, w, "xxcdxx", 2, 4); + assert w == 4; + w = lab_put_count(&out, 12, w, 0); + w = lab_put_count(&out, 12, w, 1050); + w = lab_put_count(&out, 12, w, 18446744073709551615); + assert w == 4 + 1 + 4 + 20; + assert span_is(&out, 0, 12, "abcd01050184"); + var tiny : [3]u8 = undefined; + // A full buffer drops the bytes but not the count: the caller reads the length it would need. + assert lab_put_lit(&tiny, 3, 0, "abcdef") == 6; + assert span_is(&tiny, 0, 3, "abc"); + assert lab_put_count(&tiny, 3, 2, 12345) == 7; + assert lab_put_span(&tiny, 3, 9, "abc", 1, 3) == 11; + var chars : [8]u8 = undefined; + assert lab_put_char(&chars, 8, 0, 233) == 2; + assert lab_put_char(&chars, 8, 2, 8364) == 5; + assert lab_put_char(&chars, 8, 5, 128512) == 9; + assert chars[0] == 195 and chars[1] == 169 and chars[2] == 226 and chars[4] == 172 and chars[5] == 240; +} diff --git a/specs/lab/verdict.t27 b/specs/lab/verdict.t27 new file mode 100644 index 0000000000..b34267ecbe --- /dev/null +++ b/specs/lab/verdict.t27 @@ -0,0 +1,1056 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/lab/verdict.t27 -- what the t27c lab's gates are, how a gate's log is read, and when a commit is green (gHashTag/t27#11041, #11040) +; The t27c lab (infra/t27c-lab/lab.py) runs every commit it queues through eight gates in CI's order and records for each one an +; exit code, the seconds, and one line a person reads first (the gate's `summary`). The commit is green when every gate that +; counts exited 0. This file is those decisions as pure functions of text and numbers, one per Python function (the Python being +; the oracle until the cutover), on no host in particular: whoever runs a gate hands the exit code and the log to these. The eight +; gates are a closed table (lab_gate_*): the ids and the commands a lab job kind (#11043) may name, for any node that takes one. +; GATES, NEEDS_BUILD, REPORT_ONLY the lab_gate_* table +; summary(text, pattern) lab_summary, lab_summary_write +; misread_counts(text) lab_misread_counts +; misread_line(counts, fallback) lab_misread_line +; verdict(gates) lab_verdict +; run_item(): red_gates, a skipped gate, built, when the seals are refreshed lab_red_gates, lab_gate_skipped, lab_built_after, lab_seals_due +; sh(): a limit kills the gate, exit 124 lab_exit_after +; publish_latest(): what a head's state is lab_head_state +; THE PATTERNS. summary() takes a regex. The lab uses five: none, and the four of GATES (`Finished|^error`, `RATCHET`, +; `test result:`, `pair\(s\) are silent|^\s*Nothing found|CONTROL FAILED`). They are the LAB_PAT_* rows, each coded as the literals +; it holds, the anchor and the leading spaces; a sixth pattern is a new row, not a regex engine. +; THE TEXTS. A gate row is ` TAB ` on a line: the name may be empty (a gate with no name), the exit is the decimal +; integer Python would print, or empty for None (a gate that did not run, or no exit at all). A gate counts unless its name +; is `misread`. verdict() is green when at least one gate counts and every counting gate has the exit text `0`. +; THE LINES. summary() and misread_counts() read command output the way Python does: str.splitlines() breaks, str.strip() +; spaces, `[:300]` characters (spec text.t27). The misread rows are the regex `^\s+(\d+)\s+(\d+)\s+(\d+)\s+\S` with re.M, run as +; findall does: left to right, each match ending where the next search starts. `\s` crosses lines, so a row of three numbers +; with nothing after them still matches when the next line starts with a non-space, and then it takes that character: the port +; does the same, and tests it. Not ported: a count of more than 18 digits (Python reads any size; the lab's logs carry none), and +; a Unicode digit that is not 0-9. A log with either is read as NOT READ (fail closed), and the summary falls back to its log line. +; ASCII only (L3) +; phi^2 + 1/phi^2 = 3 | TRINITY + +module LabVerdict; + +use tri::lab::receipt::span_is; +use lab::text::LAB_NO; +use lab::text::LAB_DEC_BAD; +use lab::text::lab_line_end; +use lab::text::lab_line_next; +use lab::text::lab_find_byte; +use lab::text::lab_has_text; +use lab::text::lab_has_lit; +use lab::text::lab_lit_at; +use lab::text::lab_strip_from; +use lab::text::lab_strip_to; +use lab::text::lab_cut_chars; +use lab::text::lab_space_len; +use lab::text::lab_char_len; +use lab::text::lab_count_of; +use lab::text::lab_put_span; +use lab::text::lab_put_lit; +use lab::text::lab_put_count; + +/// The number of gates, in CI's order: nothing runs on a failed build. +pub const LAB_GATE_COUNT : usize = 8; + +/// The summary patterns (the regex of a gate, or none: the last non-empty line). +pub const LAB_PAT_NONE : u8 = 0; +pub const LAB_PAT_BUILD : u8 = 1; +pub const LAB_PAT_SUITE : u8 = 2; +pub const LAB_PAT_LEAN : u8 = 3; +pub const LAB_PAT_MISREAD : u8 = 4; + +/// The longest summary line: 300 characters. +pub const LAB_SUMMARY_CHARS : usize = 300; + +/// What the lab publishes of a verdict. +pub const LAB_V_RED : u8 = 0; +pub const LAB_V_GREEN : u8 = 1; + +/// The most any one of the three misread counts, and their sums, may hold: 10^18 - 1 (an 18-digit count fits u64 with room to add). +pub const LAB_COUNT_MAX : u64 = 999999999999999999; + +/// The exit codes the lab itself makes up: a limit killed the gate, the program was not found. +pub const LAB_EXIT_TIMEOUT : i64 = 124; +pub const LAB_EXIT_NOT_FOUND : i64 = 127; + +/// What a head shows in /latest.json. +pub const LAB_HS_NOT_RUN : u8 = 0; +pub const LAB_HS_QUEUED : u8 = 1; +pub const LAB_HS_RUNNING : u8 = 2; +pub const LAB_HS_UNREADABLE : u8 = 3; +pub const LAB_HS_FROM_RUN : u8 = 4; + +/// The record the lab writes for a gate it skipped, and the log line. +pub const LAB_SKIPPED : string = "skipped: the build failed"; + +/// Gates the lab invents when no gate of the table ran: a checkout that failed, and an error of the lab itself. +pub const LAB_GATE_CHECKOUT : string = "checkout"; +pub const LAB_GATE_LAB : string = "lab"; + +/// The name of gate i (GATES[i][0]). +pub fn lab_gate_name(i: usize) -> string { + if (i == 0) { return "build"; } + if (i == 1) { return "suite"; } + if (i == 2) { return "lean"; } + if (i == 3) { return "seal-currency"; } + if (i == 4) { return "seal-coverage"; } + if (i == 5) { return "specs-parse"; } + if (i == 6) { return "specs-generate"; } + return "misread"; +} + +/// The command of gate i, argv joined by single spaces as the record's `cmd` holds it ({run} is the run's directory). +pub fn lab_gate_cmd(i: usize) -> string { + if (i == 0) { return "cargo build --release -p t27c -p tri"; } + if (i == 1) { return "./target/release/t27c suite --repo-root . --corpus-only --ratchet --json {run}/suite.json"; } + if (i == 2) { return "cargo test --release -p t27c --test icarus_lowerable corpus_classifier_matches_lean_completeness"; } + if (i == 3) { return "python3 tools/check_seal_currency.py"; } + if (i == 4) { return "python3 tools/check_seal_coverage.py"; } + if (i == 5) { return "python3 tools/ci/check_specs_still_parse.py --base origin/master"; } + if (i == 6) { return "python3 tools/check_specs_generate.py"; } + return "./target/release/tri misread --list"; +} + +/// The limit of gate i, in seconds. +pub fn lab_gate_timeout(i: usize) -> u32 { + if (i == 0 or i == 2) { return 3600; } + if (i == 1) { return 5400; } + return 1800; +} + +/// The pattern whose last matching line is gate i's summary. +pub fn lab_gate_pattern(i: usize) -> u8 { + if (i == 0) { return LAB_PAT_BUILD; } + if (i == 1) { return LAB_PAT_SUITE; } + if (i == 2) { return LAB_PAT_LEAN; } + if (i == 7) { return LAB_PAT_MISREAD; } + return LAB_PAT_NONE; +} + +/// Is the text t[from..to] the name of gate i? +pub fn lab_gate_is(i: usize, t: string, from: usize, to: usize) -> bool { + return span_is(t, from, to, lab_gate_name(i)); +} + +/// The index of the gate named t[from..to] in the table, or LAB_NO (the invented gates are not in it). +pub fn lab_gate_index(t: string, from: usize, to: usize) -> usize { + var i : usize = 0; + while (i < LAB_GATE_COUNT) { + if (lab_gate_is(i, t, from, to)) { return i; } + i = i + 1; + } + return LAB_NO; +} + +/// Is t[from..to] the name of a gate of the table? The table is a CLOSED set: a name outside it (a branch's text, a command, an +/// upper-case or padded spelling) is no gate, so whoever hands out work (one lab, or a network of nodes, #11043) refuses it before +/// anyone runs it, and the command a gate runs is only ever lab_gate_cmd(i), never text from a pull request. +pub fn lab_gate_known(t: string, from: usize, to: usize) -> bool { + return lab_gate_index(t, from, to) != LAB_NO; +} + +/// REPORT_ONLY: measured and published, never part of the verdict. Any name, in the table or not. +pub fn lab_name_report_only(t: string, from: usize, to: usize) -> bool { + return span_is(t, from, to, "misread"); +} + +/// NEEDS_BUILD: every gate of the table but `build` itself. +pub fn lab_name_needs_build(t: string, from: usize, to: usize) -> bool { + const i : usize = lab_gate_index(t, from, to); + return i != LAB_NO and i != 0; +} + +/// Does the line t[from..to] (already stripped on the right) match the gate pattern pid, as `re.search` would? +pub fn lab_pattern_hits(pid: u8, t: string, from: usize, to: usize) -> bool { + if (pid == LAB_PAT_BUILD) { return lab_has_lit(t, from, to, "Finished") or lab_lit_at(t, from, to, "error"); } + if (pid == LAB_PAT_SUITE) { return lab_has_lit(t, from, to, "RATCHET"); } + if (pid == LAB_PAT_LEAN) { return lab_has_lit(t, from, to, "test result:"); } + if (pid == LAB_PAT_MISREAD) { + if (lab_has_lit(t, from, to, "pair(s) are silent") or lab_has_lit(t, from, to, "CONTROL FAILED")) { return true; } + return lab_lit_at(t, lab_strip_from(t, from, to), to, "Nothing found"); + } + return false; +} + +/// A span of the text a summary is: t[from..to]. +pub struct LabSummarySpan { from: usize, to: usize } + +/// summary(text, pattern): the last line that matches the pattern, else the last non-empty line, stripped and cut to 300 characters; +/// an empty span when the text has no non-empty line. (A line is str.splitlines()'s; empty means all spaces.) +pub fn lab_summary(t: string, pid: u8) -> LabSummarySpan { + var hit_s : usize = LAB_NO; + var hit_e : usize = 0; + var last_s : usize = LAB_NO; + var last_e : usize = 0; + var s : usize = 0; + while (s < t.len) { + const e : usize = lab_line_end(t, s); + if (lab_has_text(t, s, e)) { + const r : usize = lab_strip_to(t, s, e); + last_s = s; + last_e = r; + if (lab_pattern_hits(pid, t, s, r)) { + hit_s = s; + hit_e = r; + } + } + s = lab_line_next(t, e); + } + var from : usize = last_s; + var to : usize = last_e; + if (hit_s != LAB_NO) { + from = hit_s; + to = hit_e; + } + if (from == LAB_NO) { return LabSummarySpan { from: 0, to: 0 }; } + const f : usize = lab_strip_from(t, from, to); + return LabSummarySpan { from: f, to: lab_cut_chars(t, f, to, LAB_SUMMARY_CHARS) }; +} + +/// summary(): write the summary line into dst at w, clipped at cap; the new cursor. +pub fn lab_summary_write(t: string, pid: u8, dst: []u8, cap: usize, w: usize) -> usize { + const sp : LabSummarySpan = lab_summary(t, pid); + return lab_put_span(dst, cap, w, t, sp.from, sp.to); +} + +/// The three totals of `tri misread`'s table; `read` is false when the output did not reach its table (or this port cannot read it). +pub struct LabMisread { read: bool, pairs: u64, refused: u64, silent: u64 } + +/// A row of the table: the end of its match (LAB_NO when it does not match) and its three counts. +pub struct LabMisreadRow { end: usize, pairs: u64, refused: u64, silent: u64 } + +/// The end of the run of spaces that starts at q (q itself when none does). +pub fn lab_space_run(t: string, q: usize) -> usize { + var k : usize = q; + while (k < t.len) { + const n : usize = lab_space_len(t, k, t.len); + if (n == 0) { return k; } + k = k + n; + } + return k; +} + +/// The end of the run of ASCII digits that starts at q (q itself when none does). +pub fn lab_digit_run(t: string, q: usize) -> usize { + var k : usize = q; + while (k < t.len and t[k] >= 48 and t[k] <= 57) { + k = k + 1; + } + return k; +} + +/// Try the row regex `\s+(\d+)\s+(\d+)\s+(\d+)\s+\S` at p. Greedy runs never need to give back: a space is not a digit and a +/// digit is not a space, so the first way to match is the only one. +pub fn lab_misread_row(t: string, p: usize) -> LabMisreadRow { + const no_row : LabMisreadRow =LabMisreadRow { end: LAB_NO, pairs: 0, refused: 0, silent: 0 }; + const a0 : usize = lab_space_run(t, p); + if (a0 == p) { return no_row; } + const a1 : usize = lab_digit_run(t, a0); + if (a1 == a0) { return no_row; } + const b0 : usize = lab_space_run(t, a1); + if (b0 == a1) { return no_row; } + const b1 : usize = lab_digit_run(t, b0); + if (b1 == b0) { return no_row; } + const c0 : usize = lab_space_run(t, b1); + if (c0 == b1) { return no_row; } + const c1 : usize = lab_digit_run(t, c0); + if (c1 == c0) { return no_row; } + const d0 : usize = lab_space_run(t, c1); + if (d0 == c1 or d0 >= t.len) { return no_row; } + return LabMisreadRow { end: d0 + lab_char_len(t, d0, t.len), pairs: lab_count_of(t, a0, a1), refused: lab_count_of(t, b0, b1), silent: lab_count_of(t, c0, c1) }; +} + +/// misread_counts(text): the totals of the rows of `tri misread`'s table; not read when the text has CONTROL FAILED, has no table +/// header (`pairs refused silent`, two spaces between), or a count is one this port refuses (see the header). +pub fn lab_misread_counts(t: string) -> LabMisread { + const unread : LabMisread = LabMisread { read: false, pairs: 0, refused: 0, silent: 0 }; + if (lab_has_lit(t, 0, t.len, "CONTROL FAILED")) { return unread; } + if (lab_has_lit(t, 0, t.len, "pairs refused silent") == false) { return unread; } + var pairs : u64 = 0; + var refused : u64 = 0; + var silent : u64 = 0; + var p : usize = 0; + while (p < t.len) { + var step : usize = 1; + if (p == 0 or t[p - 1] == 10) { + const row : LabMisreadRow = lab_misread_row(t, p); + if (row.end != LAB_NO) { + if (row.pairs == LAB_DEC_BAD or row.refused == LAB_DEC_BAD or row.silent == LAB_DEC_BAD) { return unread; } + if (pairs + row.pairs > LAB_COUNT_MAX or refused + row.refused > LAB_COUNT_MAX or silent + row.silent > LAB_COUNT_MAX) { return unread; } + pairs = pairs + row.pairs; + refused = refused + row.refused; + silent = silent + row.silent; + step = row.end - p; + } + } + p = p + step; + } + return LabMisread { read: true, pairs: pairs, refused: refused, silent: silent }; +} + +/// misread_line(counts, fallback): the counts in words (` of pair(s) silent, refused by typecheck`; refused +/// LAB_DEC_BAD is None: `refusals not measured`), else the fallback line fb[ff..ft], into dst at w, clipped at cap; the new cursor. +pub fn lab_misread_line(m: LabMisread, fb: string, ff: usize, ft: usize, dst: []u8, cap: usize, w: usize) -> usize { + if (m.read == false) { return lab_put_span(dst, cap, w, fb, ff, ft); } + var x : usize = lab_put_count(dst, cap, w, m.silent); + x = lab_put_lit(dst, cap, x, " of "); + x = lab_put_count(dst, cap, x, m.pairs); + x = lab_put_lit(dst, cap, x, " pair(s) silent, "); + if (m.refused == LAB_DEC_BAD) { return lab_put_lit(dst, cap, x, "refusals not measured"); } + x = lab_put_count(dst, cap, x, m.refused); + return lab_put_lit(dst, cap, x, " refused by typecheck"); +} + +/// verdict(gates): green when at least one gate counts (its name is not REPORT_ONLY) and every counting gate has the exit text 0. +pub fn lab_verdict(rows: string) -> u8 { + var counted : usize = 0; + var s : usize = 0; + while (s < rows.len) { + const e : usize = lab_line_end(rows, s); + var name_to : usize = lab_find_byte(rows, s, e, 9); + var exit_from : usize = e; + if (name_to == LAB_NO) { + name_to = e; + } else { + exit_from = name_to + 1; + } + if (lab_name_report_only(rows, s, name_to) == false) { + counted = counted + 1; + if (span_is(rows, exit_from, e, "0") == false) { return LAB_V_RED; } + } + s = lab_line_next(rows, e); + } + if (counted == 0) { return LAB_V_RED; } + return LAB_V_GREEN; +} + +/// The word the lab publishes for a verdict. +pub fn lab_verdict_name(v: u8) -> string { + if (v == LAB_V_GREEN) { return "green"; } + return "red"; +} + +/// run_item()'s red_gates: the names of the gates whose exit is not 0 and that are not REPORT_ONLY, one per line, in order, into dst +/// at w, clipped at cap; the new cursor. (A gate with no exit is red.) +pub fn lab_red_gates(rows: string, dst: []u8, cap: usize, w: usize) -> usize { + var x : usize = w; + var s : usize = 0; + while (s < rows.len) { + const e : usize = lab_line_end(rows, s); + var name_to : usize = lab_find_byte(rows, s, e, 9); + var exit_from : usize = e; + if (name_to == LAB_NO) { + name_to = e; + } else { + exit_from = name_to + 1; + } + if (lab_name_report_only(rows, s, name_to) == false and span_is(rows, exit_from, e, "0") == false) { + x = lab_put_span(dst, cap, x, rows, s, name_to); + x = lab_put_lit(dst, cap, x, "\n"); + } + s = lab_line_next(rows, e); + } + return x; +} + +/// run_item(): a gate that needs the build is skipped once the build failed. +pub fn lab_gate_skipped(i: usize, built: bool) -> bool { + return i != 0 and i < LAB_GATE_COUNT and built == false; +} + +/// run_item(): the build is still good after gate i exited with code, unless this is the build and it did not exit 0. +pub fn lab_built_after(i: usize, code: i64, built: bool) -> bool { + if (i == 0 and code != 0) { return false; } + return built; +} + +/// run_item(): the seal refresh of #9080 runs after the gates of a commit a `master` branch points at, and only if it built. +pub fn lab_seals_due(has_master: bool, built: bool) -> bool { + return has_master and built; +} + +/// sh(): a gate that ran longer than its limit is killed and exits 124, whatever the program said. +pub fn lab_exit_after(code: i64, elapsed: f64, limit: i64) -> i64 { + if (elapsed > (limit as f64)) { return LAB_EXIT_TIMEOUT; } + return code; +} + +/// publish_latest(): what a head shows. A run file that exists but cannot be read is `unreadable`; one that reads shows its own +/// verdict; no run file: `running` when it is the commit being run, else `queued` when it is in the queue, else `not run`. +pub fn lab_head_state(run_exists: bool, run_readable: bool, is_running: bool, is_queued: bool) -> u8 { + if (run_exists) { + if (run_readable) { return LAB_HS_FROM_RUN; } + return LAB_HS_UNREADABLE; + } + if (is_running) { return LAB_HS_RUNNING; } + if (is_queued) { return LAB_HS_QUEUED; } + return LAB_HS_NOT_RUN; +} + +/// The word a head shows for a state (LAB_HS_FROM_RUN shows the run's own verdict, so it has none here). +pub fn lab_head_state_name(h: u8) -> string { + if (h == LAB_HS_QUEUED) { return "queued"; } + if (h == LAB_HS_RUNNING) { return "running"; } + if (h == LAB_HS_UNREADABLE) { return "unreadable"; } + if (h == LAB_HS_NOT_RUN) { return "not run"; } + return ""; +} + +// ---- tests ---- +// The oracle is infra/t27c-lab/lab.py. Every case below is its answer (Python 3.14.6), pinned: first the asserts of its +// self_check(), one to one, then the gate table and tables of inputs run through its own functions (summary, misread_counts, +// misread_line, verdict, the red_gates expression, sh()'s limit, publish_latest()'s head state). + +/// summary(text, pattern) for the five patterns: none, GATES[0], GATES[1], GATES[2], GATES[7]. +fn lab_test_summary(t: string, pid: u8, want: string) -> bool { + var out : [1400]u8 = undefined; + const w : usize = lab_summary_write(t, pid, &out, 1400, 0); + return w == want.len and span_is(&out, 0, w, want); +} + +fn lab_test_summaries(t: string, w0: string, w1: string, w2: string, w3: string, w4: string) -> bool { + return lab_test_summary(t, LAB_PAT_NONE, w0) and lab_test_summary(t, LAB_PAT_BUILD, w1) and lab_test_summary(t, LAB_PAT_SUITE, w2) and lab_test_summary(t, LAB_PAT_LEAN, w3) and lab_test_summary(t, LAB_PAT_MISREAD, w4); +} + +/// misread_counts(text): read or not, and the three totals. +fn lab_test_misread(t: string, read: bool, pairs: u64, refused: u64, silent: u64) -> bool { + const m : LabMisread = lab_misread_counts(t); + if (m.read != read) { return false; } + if (read == false) { return m.pairs == 0 and m.refused == 0 and m.silent == 0; } + return m.pairs == pairs and m.refused == refused and m.silent == silent; +} + +/// misread_line(misread_counts(text), fallback) +fn lab_test_misread_line(t: string, fb: string, want: string) -> bool { + var out : [256]u8 = undefined; + const w : usize = lab_misread_line(lab_misread_counts(t), fb, 0, fb.len, &out, 256, 0); + return w == want.len and span_is(&out, 0, w, want); +} + +fn lab_test_verdict(rows: string, green: bool) -> bool { + if (green) { return lab_verdict(rows) == LAB_V_GREEN; } + return lab_verdict(rows) == LAB_V_RED; +} + +fn lab_test_red(rows: string, want: string) -> bool { + var out : [256]u8 = undefined; + const w : usize = lab_red_gates(rows, &out, 256, 0); + return w == want.len and span_is(&out, 0, w, want); +} + +/// One row of GATES: its name, limit, summary pattern, whether it needs the build, whether it is report-only, and its command. +fn lab_test_gate(i: usize, name: string, limit: u32, pid: u8, needs_build: bool, report_only: bool, cmd: string) -> bool { + const n : string = lab_gate_name(i); + const c : string = lab_gate_cmd(i); + if (span_is(n, 0, n.len, name) == false or span_is(c, 0, c.len, cmd) == false) { return false; } + if (lab_gate_timeout(i) != limit or lab_gate_pattern(i) != pid) { return false; } + if (lab_gate_index(name, 0, name.len) != i) { return false; } + return lab_name_needs_build(name, 0, name.len) == needs_build and lab_name_report_only(name, 0, name.len) == report_only; +} + +// <<< pinned table: verdict_self_check +test lab_py_self_check_summary_is_the_last_matching_line_else_the_last_non_empty_line { + assert lab_test_summary("x\nRATCHET CLEAN 113/113\ny\n", LAB_PAT_SUITE, "RATCHET CLEAN 113/113"); + assert lab_test_summary("x\n\nlast\n\n", LAB_PAT_NONE, "last"); + assert lab_test_summary("", LAB_PAT_SUITE, ""); + assert lab_test_summary(" pairs refused silent\n 12 12 0 an empty type slot\n 5 3 2 a colon inside a field type\n\n 2 of 17 pair(s) are silent: the spec parses\n", LAB_PAT_MISREAD, "2 of 17 pair(s) are silent: the spec parses"); +} + +test lab_py_self_check_verdict_is_green_only_when_every_counting_gate_exited_zero { + assert lab_test_verdict("\t0\n\t0\n", true); + assert lab_test_verdict("\t0\n\n", false); + assert lab_test_verdict("", false); + assert lab_test_verdict("build\t0\nmisread\t1\n", true); + assert lab_test_verdict("misread\t0\n", false); +} + +test lab_py_self_check_misread_counts_reads_the_table_and_nothing_else { + assert lab_test_misread(" pairs refused silent\n 12 12 0 an empty type slot\n 5 3 2 a colon inside a field type\n\n 2 of 17 pair(s) are silent: the spec parses\n", true, 17, 15, 2); + assert lab_test_misread(" CONTROL FAILED -- these shapes did not fire\n", false, 0, 0, 0); + assert lab_test_misread("error: target/release/t27c is not built\n", false, 0, 0, 0); + assert lab_test_misread(" pairs refused silent\n\n Nothing found\n", true, 0, 0, 0); +} + +test lab_py_self_check_misread_line_says_the_counts_in_words { + assert lab_test_misread_line(" pairs refused silent\n 12 12 0 an empty type slot\n 5 3 2 a colon inside a field type\n\n 2 of 17 pair(s) are silent: the spec parses\n", "", "2 of 17 pair(s) silent, 15 refused by typecheck"); +} + +// >>> pinned table: verdict_self_check +// <<< pinned table: verdict_gates +// GATES, NEEDS_BUILD and REPORT_ONLY, row by row. +test lab_py_table_gates { + assert lab_test_gate(0, "build", 3600, 1, false, false, "cargo build --release -p t27c -p tri"); + assert lab_test_gate(1, "suite", 5400, 2, true, false, "./target/release/t27c suite --repo-root . --corpus-only --ratchet --json {run}/suite.json"); + assert lab_test_gate(2, "lean", 3600, 3, true, false, "cargo test --release -p t27c --test icarus_lowerable corpus_classifier_matches_lean_completeness"); + assert lab_test_gate(3, "seal-currency", 1800, 0, true, false, "python3 tools/check_seal_currency.py"); + assert lab_test_gate(4, "seal-coverage", 1800, 0, true, false, "python3 tools/check_seal_coverage.py"); + assert lab_test_gate(5, "specs-parse", 1800, 0, true, false, "python3 tools/ci/check_specs_still_parse.py --base origin/master"); + assert lab_test_gate(6, "specs-generate", 1800, 0, true, false, "python3 tools/check_specs_generate.py"); + assert lab_test_gate(7, "misread", 1800, 4, true, true, "./target/release/tri misread --list"); + assert lab_gate_index("checkout", 0, 8) == LAB_NO and lab_gate_index("lab", 0, 3) == LAB_NO; +} + +// The names outside the table: not in NEEDS_BUILD, not in REPORT_ONLY, unless they are `misread`. +test lab_py_table_names_outside_the_table { + assert lab_name_needs_build("checkout", 0, 8) == false and lab_name_report_only("checkout", 0, 8) == false; + assert lab_name_needs_build("lab", 0, 3) == false and lab_name_report_only("lab", 0, 3) == false; + assert lab_name_needs_build("", 0, 0) == false and lab_name_report_only("", 0, 0) == false; + assert lab_name_needs_build("build ", 0, 6) == false and lab_name_report_only("build ", 0, 6) == false; + assert lab_name_needs_build(" misread", 0, 8) == false and lab_name_report_only(" misread", 0, 8) == false; + assert lab_name_needs_build("Misread", 0, 7) == false and lab_name_report_only("Misread", 0, 7) == false; + assert lab_name_needs_build("misread ", 0, 8) == false and lab_name_report_only("misread ", 0, 8) == false; + assert lab_name_needs_build("misreads", 0, 8) == false and lab_name_report_only("misreads", 0, 8) == false; + assert lab_name_needs_build("specs", 0, 5) == false and lab_name_report_only("specs", 0, 5) == false; + assert lab_name_needs_build("specs-generate", 0, 14) == true and lab_name_report_only("specs-generate", 0, 14) == false; + assert lab_name_needs_build("seal", 0, 4) == false and lab_name_report_only("seal", 0, 4) == false; + assert lab_name_needs_build("suite", 0, 5) == true and lab_name_report_only("suite", 0, 5) == false; + assert lab_name_needs_build("BUILD", 0, 5) == false and lab_name_report_only("BUILD", 0, 5) == false; + assert lab_name_needs_build("lean", 0, 4) == true and lab_name_report_only("lean", 0, 4) == false; + assert lab_name_needs_build("x", 0, 1) == false and lab_name_report_only("x", 0, 1) == false; +} + +// >>> pinned table: verdict_gates +// <<< pinned table: verdict_summary +// summary(text, pattern) under each of the five patterns (none, GATES[0], GATES[1], GATES[2], GATES[7]), one row per text. +test lab_py_table_summary { + assert lab_test_summaries("", "", "", "", "", ""); + assert lab_test_summaries("\n", "", "", "", "", ""); + assert lab_test_summaries(" ", "", "", "", "", ""); + assert lab_test_summaries("\n\n\n", "", "", "", "", ""); + assert lab_test_summaries(" \n \n", "", "", "", "", ""); + assert lab_test_summaries("x", "x", "x", "x", "x", "x"); + assert lab_test_summaries("x\n", "x", "x", "x", "x", "x"); + assert lab_test_summaries("\nx", "x", "x", "x", "x", "x"); + assert lab_test_summaries("last\n\n", "last", "last", "last", "last", "last"); + assert lab_test_summaries("RATCHET", "RATCHET", "RATCHET", "RATCHET", "RATCHET", "RATCHET"); + assert lab_test_summaries(" RATCHET ", "RATCHET", "RATCHET", "RATCHET", "RATCHET", "RATCHET"); + assert lab_test_summaries(" Nothing found", "Nothing found", "Nothing found", "Nothing found", "Nothing found", "Nothing found"); + assert lab_test_summaries("Nothing found\n Nothing found 2\n", "Nothing found 2", "Nothing found 2", "Nothing found 2", "Nothing found 2", "Nothing found 2"); + assert lab_test_summaries("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); + assert lab_test_summaries("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); + assert lab_test_summaries("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); + assert lab_test_summaries("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); + assert lab_test_summaries(" yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy", "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy", "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy", "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy", "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy", "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"); + assert lab_test_summaries("zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz ", "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz", "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz", "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz", "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz", "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"); + assert lab_test_summaries("zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz ", "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz", "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz", "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz", "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz", "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"); + assert lab_test_summaries("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa RATCHET", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa RATC", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa RATC", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa RATC", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa RATC", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa RATC"); + assert lab_test_summaries("RATCHET qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", "RATCHET qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", "RATCHET qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", "RATCHET qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", "RATCHET qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", "RATCHET qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"); + assert lab_test_summaries("a\nRATCHET kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk\nz\n", "z", "z", "RATCHET kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk", "z", "z"); + assert lab_test_summaries("Finished early\nCONTROL FAILED -- these shapes did not fire\npair(s) are not silent\nFinished late\n \n", "Finished late", "Finished late", "Finished late", "Finished late", "CONTROL FAILED -- these shapes did not fire"); + assert lab_test_summaries("Finished late\n leading\nerror: could not compile `t27c`\nlast\ntest result: FAILED. 0 passed; 1 failed\n 2 of 17 pair(s) are silent: the spec parses\n\n", "2 of 17 pair(s) are silent: the spec parses", "error: could not compile `t27c`", "2 of 17 pair(s) are silent: the spec parses", "test result: FAILED. 0 passed; 1 failed", "2 of 17 pair(s) are silent: the spec parses"); + assert lab_test_summaries("error: first\n \n error: x\nnothing found\n error: x\nFinished early\n \n", "Finished early", "Finished early", "Finished early", "Finished early", "Finished early"); + assert lab_test_summaries(" Nothing found\nok\nRATCHET CLEAN 113/113\n\n", "RATCHET CLEAN 113/113", "RATCHET CLEAN 113/113", "RATCHET CLEAN 113/113", "RATCHET CLEAN 113/113", "Nothing found"); + assert lab_test_summaries("RATCHET FAILED: 3 new\nerror\nNothing found\n leading\nwarning: unused\nratchet\n\n", "ratchet", "error", "RATCHET FAILED: 3 new", "ratchet", "ratchet"); + assert lab_test_summaries("Nothing found\nNothing found\nwarning: unused\n", "warning: unused", "warning: unused", "warning: unused", "warning: unused", "Nothing found"); + assert lab_test_summaries("Nothing found\nFinished late\nRATCHET CLEAN 113/113\n\n", "RATCHET CLEAN 113/113", "Finished late", "RATCHET CLEAN 113/113", "RATCHET CLEAN 113/113", "Nothing found"); + assert lab_test_summaries("Finished early\nError: capital\ntrailing space \ntest result: FAILED. 0 passed; 1 failed\n \n", "test result: FAILED. 0 passed; 1 failed", "Finished early", "test result: FAILED. 0 passed; 1 failed", "test result: FAILED. 0 passed; 1 failed", "test result: FAILED. 0 passed; 1 failed"); + assert lab_test_summaries("a\tb\n\n", "a\tb", "a\tb", "a\tb", "a\tb", "a\tb"); + assert lab_test_summaries("\tNothing found\npair(s) are not silent\nNothing found\n\tNothing found\nRATCHET FAILED: 3 new\npair(s) are not silent", "pair(s) are not silent", "pair(s) are not silent", "RATCHET FAILED: 3 new", "pair(s) are not silent", "Nothing found"); + assert lab_test_summaries("Finished early\n 2 of 17 pair(s) are silent: the spec parses\ntrailing space \n", "trailing space", "Finished early", "trailing space", "trailing space", "2 of 17 pair(s) are silent: the spec parses"); + assert lab_test_summaries("Finished\nNothing found\ntest result: FAILED. 0 passed; 1 failed\n leading\ntrailing space \nCONTROL FAILED -- these shapes did not fire\n", "CONTROL FAILED -- these shapes did not fire", "Finished", "CONTROL FAILED -- these shapes did not fire", "test result: FAILED. 0 passed; 1 failed", "CONTROL FAILED -- these shapes did not fire"); + assert lab_test_summaries(" \na\tb\n\tindented by a tab\n\tNothing found\n \n", "Nothing found", "Nothing found", "Nothing found", "Nothing found", "Nothing found"); + assert lab_test_summaries("CONTROL FAILED -- these shapes did not fire\n\n", "CONTROL FAILED -- these shapes did not fire", "CONTROL FAILED -- these shapes did not fire", "CONTROL FAILED -- these shapes did not fire", "CONTROL FAILED -- these shapes did not fire", "CONTROL FAILED -- these shapes did not fire"); + assert lab_test_summaries(" Compiling t27c v0.5.2\nlast\nlast\nCONTROL FAILED -- these shapes did not fire\nerror\n\tindented by a tab\n \n", "indented by a tab", "error", "indented by a tab", "indented by a tab", "CONTROL FAILED -- these shapes did not fire"); + assert lab_test_summaries("a\tb\n\tNothing found\nx Nothing found\n\tNothing found\n\t\n \n", "Nothing found", "Nothing found", "Nothing found", "Nothing found", "Nothing found"); + assert lab_test_summaries("Nothing found\nnothing found\nratchet", "ratchet", "ratchet", "ratchet", "ratchet", "ratchet"); + assert lab_test_summaries(" error: x\nRATCHET FAILED: 3 new\n\n", "RATCHET FAILED: 3 new", "RATCHET FAILED: 3 new", "RATCHET FAILED: 3 new", "RATCHET FAILED: 3 new", "RATCHET FAILED: 3 new"); + assert lab_test_summaries("RATCHET FAILED: 3 new\nfinished\na\tb\n \npair(s) are silent\n ", "pair(s) are silent", "pair(s) are silent", "RATCHET FAILED: 3 new", "pair(s) are silent", "pair(s) are silent"); + assert lab_test_summaries("test result\n\tindented by a tab\nok\n 2 of 17 pair(s) are silent: the spec parses", "2 of 17 pair(s) are silent: the spec parses", "2 of 17 pair(s) are silent: the spec parses", "2 of 17 pair(s) are silent: the spec parses", "2 of 17 pair(s) are silent: the spec parses", "2 of 17 pair(s) are silent: the spec parses"); + assert lab_test_summaries(" Compiling t27c v0.5.2\nRATCHET\n\n", "RATCHET", "RATCHET", "RATCHET", "RATCHET", "RATCHET"); + assert lab_test_summaries("RATCHET FAILED: 3 new", "RATCHET FAILED: 3 new", "RATCHET FAILED: 3 new", "RATCHET FAILED: 3 new", "RATCHET FAILED: 3 new", "RATCHET FAILED: 3 new"); + assert lab_test_summaries("RATCHET CLEAN 113/113\n leading\n\tNothing found\n \n", "Nothing found", "Nothing found", "RATCHET CLEAN 113/113", "Nothing found", "Nothing found"); + assert lab_test_summaries(" Finished `release` profile [optimized] target(s) in 1m 02s\n\n leading\nlast\n error: x\n leading\n", "leading", "Finished `release` profile [optimized] target(s) in 1m 02s", "leading", "leading", "leading"); + assert lab_test_summaries("warning: unused\nerror: second\n\n", "error: second", "error: second", "error: second", "error: second", "error: second"); + assert lab_test_summaries("test result: ok. 1 passed; 0 failed\n 1 2 3 x \n\n 2 of 17 pair(s) are silent: the spec parses\n\npair(s) are silent\n", "pair(s) are silent", "pair(s) are silent", "pair(s) are silent", "test result: ok. 1 passed; 0 failed", "pair(s) are silent"); + assert lab_test_summaries("Finished late\n Finished `release` profile [optimized] target(s) in 1m 02s", "Finished `release` profile [optimized] target(s) in 1m 02s", "Finished `release` profile [optimized] target(s) in 1m 02s", "Finished `release` profile [optimized] target(s) in 1m 02s", "Finished `release` profile [optimized] target(s) in 1m 02s", "Finished `release` profile [optimized] target(s) in 1m 02s"); + assert lab_test_summaries("error: second\nRATCHET\nok", "ok", "error: second", "RATCHET", "ok", "ok"); +} + +// >>> pinned table: verdict_summary +// <<< pinned table: verdict_misread +// misread_counts(text): the totals, or None (not read), for tables of every shape; the cross-line rows of `\s` included. +test lab_py_table_misread_counts { + assert lab_test_misread("", false, 0, 0, 0); + assert lab_test_misread(" pairs refused silent\n", true, 0, 0, 0); + assert lab_test_misread(" pairs refused silent\n\n", true, 0, 0, 0); + assert lab_test_misread(" pairs refused silent\n\n Nothing found\n", true, 0, 0, 0); + assert lab_test_misread(" pairs refused silent\n 12 12 0 an empty type slot\n", true, 12, 12, 0); + assert lab_test_misread(" pairs refused silent\n 1 2 3", true, 0, 0, 0); + assert lab_test_misread(" pairs refused silent\n 1 2 3\n y", true, 1, 2, 3); + assert lab_test_misread(" pairs refused silent\n 1 2 3\n 5 5 5\n\n\nerror: target/release/t27c is not built\n\n\n\n", true, 1, 2, 3); + assert lab_test_misread(" pairs refused silent\n 1 2\n3 x\n", true, 1, 2, 3); + assert lab_test_misread("1 2 3 x\n 1.5 2 3 x\n pairs refused silent\n\n\n -1 2 3 x\n", false, 0, 0, 0); + assert lab_test_misread(" pairs refused silent\n\t7\t8\t9\tzz\nerror: target/release/t27c is not built\n", true, 7, 8, 9); + assert lab_test_misread(" pairs refused silent\n 5 3 2 a colon inside a field type\n 12 12 0 an empty type slot\n 12 12 0 an empty type slot\n", true, 29, 27, 2); + assert lab_test_misread(" 1 2 3\n 2 of 17 pair(s) are silent: the spec parses\npairs refused silent\n Nothing found\n Nothing found\n", false, 0, 0, 0); + assert lab_test_misread(" pairs refused silent\n 12 12 0 an empty type slot\n 4 5 6\n 7 8 9\n pairs refused silent\n", true, 16, 17, 6); + assert lab_test_misread(" pairs refused silent\n\t7\t8\t9\tzz\n pairs refused silent\nerror: target/release/t27c is not built\npairs refused silent", true, 7, 8, 9); + assert lab_test_misread(" pairs refused silent\n 5 5 5\n\n\n 1 2\n3 x\n 1 2\n 0 0 0 none\n 1 2\n", true, 6, 7, 5); + assert lab_test_misread(" 1.5 2 3 x\n 10 20 30 \n 1.5 2 3 x\n 3 4 5 x\n 1.5 2 3 x\n", false, 0, 0, 0); + assert lab_test_misread(" 01 002 0003 pad\n 12 12 0\nerror: target/release/t27c is not built\n 1 2 3 \n 1 2 3 1 2 3x\n", false, 0, 0, 0); + assert lab_test_misread(" pairs refused silent\n 3 4 5\t\n y\n\n 1.5 2 3 x\n 100 200 300 a\n 400 500 600 b\n 700 800 900 c\n", true, 1203, 1504, 1805); + assert lab_test_misread(" pairs refused silent\n 0 0 0 none\n CONTROL FAILED -- these shapes did not fire\n 1\n2\n3\nx\n 12 12 0\n", false, 0, 0, 0); + assert lab_test_misread("\t7\t8\t9\tzz\n a b c d\n CONTROL FAILED -- these shapes did not fire\n", false, 0, 0, 0); + assert lab_test_misread("x 9 9 9 y\n 1.5 2 3 x\n 12 12 0\n CONTROL FAILED -- these shapes did not fire\n -1 2 3 x\n", false, 0, 0, 0); + assert lab_test_misread("x 9 9 9 y\n", false, 0, 0, 0); + assert lab_test_misread(" 1 2 3x\n", false, 0, 0, 0); + assert lab_test_misread(" pairs refused silent\n 1 2\n pairs refused silent\n 0 0 0 none\n 1 2 3 \n 12 12 0\n 3 4 5 x\n", true, 4, 6, 8); + assert lab_test_misread(" pairs refused silent\n 1 2 3 q q q\n\t7\t8\t9\tzz\n", true, 8, 10, 12); + assert lab_test_misread(" pairs refused silent\n 0 0 0 none\n", true, 0, 0, 0); + assert lab_test_misread(" Nothing found\n 18 17 16 15\n 12 12 0 an empty type slot\n 9 9 9 9 9 9 y\n", false, 0, 0, 0); + assert lab_test_misread(" pairs refused silent\n 4 5 6\n 7 8 9\n pairs refused silent\n 100 200 300 a\n 400 500 600 b\n 700 800 900 c\n", true, 1204, 1505, 1806); + assert lab_test_misread(" pairs refused silent\n1 2 3 x\n 1.5 2 3 x\n pairs refused silent\n 100 200 300 a\n 400 500 600 b\n 700 800 900 c\n Nothing found\n", true, 1200, 1500, 1800); + assert lab_test_misread(" pairs refused silent\n 3 4 5\t\n y\n1 2 3 x\n\n pairs refused silent\n", true, 3, 4, 5); + assert lab_test_misread(" pairs refused silent\n 5 5 5\n\n\n 6 6 6 z\n 0 0 0 none\n 3 4 5 x\n 3 4 5\t\n y\n 3 4 5 6\n\t7\t8\t9\tzz\n", true, 21, 25, 29); + assert lab_test_misread(" pairs refused silent\n 3 4 5\t\n y\n pairs refused silent\n", true, 3, 4, 5); + assert lab_test_misread(" CONTROL FAILED -- these shapes did not fire\n 1 2\n 12 12 0\n -1 2 3 x\nerror: target/release/t27c is not built\n 0 0 0 none\n", false, 0, 0, 0); + assert lab_test_misread(" pairs refused silent\npairs refused silent 4 5 6\n 7 8 9\nerror: target/release/t27c is not built\nx 9 9 9 y\n", true, 7, 8, 9); + assert lab_test_misread(" pairs refused silent\n 1 2 3x\n 1 2 3\n", true, 0, 0, 0); + assert lab_test_misread(" pairs refused silent\n Nothing found\n\t7\t8\t9\tzz\n", true, 7, 8, 9); +} + +// >>> pinned table: verdict_misread +// <<< pinned table: verdict_misread_line +// misread_line(misread_counts(text), fallback): the counts in words, else the fallback. +test lab_py_table_misread_line { + assert lab_test_misread_line("", "", ""); + assert lab_test_misread_line(" pairs refused silent\n", "error: target/release/t27c is not built", "0 of 0 pair(s) silent, 0 refused by typecheck"); + assert lab_test_misread_line(" pairs refused silent\n\n", "2 of 17 pair(s) are silent: the spec parses", "0 of 0 pair(s) silent, 0 refused by typecheck"); + assert lab_test_misread_line(" pairs refused silent\n\n Nothing found\n", "error: target/release/t27c is not built", "0 of 0 pair(s) silent, 0 refused by typecheck"); + assert lab_test_misread_line(" pairs refused silent\n 12 12 0 an empty type slot\n", "", "0 of 12 pair(s) silent, 12 refused by typecheck"); + assert lab_test_misread_line(" pairs refused silent\n 1 2 3", "2 of 17 pair(s) are silent: the spec parses", "0 of 0 pair(s) silent, 0 refused by typecheck"); + assert lab_test_misread_line(" pairs refused silent\n 1 2 3\n y", "2 of 17 pair(s) are silent: the spec parses", "3 of 1 pair(s) silent, 2 refused by typecheck"); + assert lab_test_misread_line(" pairs refused silent\n 1 2 3\n 5 5 5\n\n\nerror: target/release/t27c is not built\n\n\n\n", "error: target/release/t27c is not built", "3 of 1 pair(s) silent, 2 refused by typecheck"); + assert lab_test_misread_line(" pairs refused silent\n 1 2\n3 x\n", "error: target/release/t27c is not built", "3 of 1 pair(s) silent, 2 refused by typecheck"); + assert lab_test_misread_line("1 2 3 x\n 1.5 2 3 x\n pairs refused silent\n\n\n -1 2 3 x\n", "fallback line", "fallback line"); + assert lab_test_misread_line(" pairs refused silent\n\t7\t8\t9\tzz\nerror: target/release/t27c is not built\n", "fallback line", "9 of 7 pair(s) silent, 8 refused by typecheck"); + assert lab_test_misread_line(" pairs refused silent\n 5 3 2 a colon inside a field type\n 12 12 0 an empty type slot\n 12 12 0 an empty type slot\n", "", "2 of 29 pair(s) silent, 27 refused by typecheck"); + assert lab_test_misread_line(" 1 2 3\n 2 of 17 pair(s) are silent: the spec parses\npairs refused silent\n Nothing found\n Nothing found\n", "2 of 17 pair(s) are silent: the spec parses", "2 of 17 pair(s) are silent: the spec parses"); + assert lab_test_misread_line(" pairs refused silent\n 12 12 0 an empty type slot\n 4 5 6\n 7 8 9\n pairs refused silent\n", "error: target/release/t27c is not built", "6 of 16 pair(s) silent, 17 refused by typecheck"); + assert lab_test_misread_line(" pairs refused silent\n\t7\t8\t9\tzz\n pairs refused silent\nerror: target/release/t27c is not built\npairs refused silent", "error: target/release/t27c is not built", "9 of 7 pair(s) silent, 8 refused by typecheck"); + assert lab_test_misread_line(" pairs refused silent\n 5 5 5\n\n\n 1 2\n3 x\n 1 2\n 0 0 0 none\n 1 2\n", "", "5 of 6 pair(s) silent, 7 refused by typecheck"); + assert lab_test_misread_line(" 1.5 2 3 x\n 10 20 30 \n 1.5 2 3 x\n 3 4 5 x\n 1.5 2 3 x\n", "", ""); + assert lab_test_misread_line(" 01 002 0003 pad\n 12 12 0\nerror: target/release/t27c is not built\n 1 2 3 \n 1 2 3 1 2 3x\n", "error: target/release/t27c is not built", "error: target/release/t27c is not built"); + assert lab_test_misread_line(" pairs refused silent\n 3 4 5\t\n y\n\n 1.5 2 3 x\n 100 200 300 a\n 400 500 600 b\n 700 800 900 c\n", "error: target/release/t27c is not built", "1805 of 1203 pair(s) silent, 1504 refused by typecheck"); + assert lab_test_misread_line(" pairs refused silent\n 0 0 0 none\n CONTROL FAILED -- these shapes did not fire\n 1\n2\n3\nx\n 12 12 0\n", "error: target/release/t27c is not built", "error: target/release/t27c is not built"); + assert lab_test_misread_line("\t7\t8\t9\tzz\n a b c d\n CONTROL FAILED -- these shapes did not fire\n", "", ""); + assert lab_test_misread_line("x 9 9 9 y\n 1.5 2 3 x\n 12 12 0\n CONTROL FAILED -- these shapes did not fire\n -1 2 3 x\n", "fallback line", "fallback line"); + assert lab_test_misread_line("x 9 9 9 y\n", "error: target/release/t27c is not built", "error: target/release/t27c is not built"); + assert lab_test_misread_line(" 1 2 3x\n", "fallback line", "fallback line"); + assert lab_test_misread_line(" pairs refused silent\n 1 2\n pairs refused silent\n 0 0 0 none\n 1 2 3 \n 12 12 0\n 3 4 5 x\n", "fallback line", "8 of 4 pair(s) silent, 6 refused by typecheck"); + assert lab_test_misread_line(" pairs refused silent\n 1 2 3 q q q\n\t7\t8\t9\tzz\n", "fallback line", "12 of 8 pair(s) silent, 10 refused by typecheck"); +} + +// >>> pinned table: verdict_misread_line +// <<< pinned table: verdict_verdict +// verdict(gates): green when at least one gate counts and every counting gate exited 0; misread is left out. +test lab_py_table_verdict { + assert lab_test_verdict("build\t-9\nlean\t0\nbuild\t127\n\t1\n", false); + assert lab_test_verdict("suite\t0\nsuite\t-9\nbuild\t\n", false); + assert lab_test_verdict("specs-parse\t-9\nseal-coverage\t0\nsuite\t-9\nseal-coverage\t0\n", false); + assert lab_test_verdict("", false); + assert lab_test_verdict("seal-coverage\t124\nlean\t-9\nlean\t0\nsuite\t1\n", false); + assert lab_test_verdict("seal-coverage\t0\nseal-coverage\t0\nspecs-generate\t0\nlab\t2\nbuild\t1\nspecs-parse\t1\nseal-currency\t1\nmisread\t-9\n", false); + assert lab_test_verdict("seal-coverage\t0\nspecs-parse\t127\nspecs-generate\t124\nlean\t124\nseal-coverage\t0\nspecs-parse\t1\n", false); + assert lab_test_verdict("specs-parse\t127\n\t124\nseal-coverage\t124\n", false); + assert lab_test_verdict("\t1\nlean\t127\ncheckout\t0\nsuite\t0\nseal-currency\t124\n", false); + assert lab_test_verdict("specs-generate\t0\nseal-currency\t1\n\t0\nspecs-generate\t0\n", false); + assert lab_test_verdict("lean\t124\n\nlean\t124\nspecs-generate\t1\nspecs-generate\t0\nmisread\t0\ncheckout\t2\n", false); + assert lab_test_verdict("suite\t124\nseal-coverage\t0\n", false); + assert lab_test_verdict("seal-coverage\t124\nlab\t127\n\t124\nbuild\t127\n\nbuild\t0\nspecs-parse\t124\n", false); + assert lab_test_verdict("build\t124\nseal-coverage\t124\nspecs-parse\t\nbuild\t0\nmisread\t0\nmisread\t0\nsuite\t-9\n", false); + assert lab_test_verdict("specs-parse\t124\nbuild\t0\nspecs-generate\t1\nbuild\t-9\n\t0\nmisread\t0\nlean\t1\nsuite\t0\n", false); + assert lab_test_verdict("specs-parse\t2\nseal-coverage\t2\nmisread\t124\nsuite\t1\nbuild\t0\n", false); + assert lab_test_verdict("suite\t0\nlab\nbuild\t1\nlab\t124\nseal-coverage\t2\nsuite\t2\nspecs-generate\t127\n", false); + assert lab_test_verdict("checkout\t-9\nmisread\t0\ncheckout\t-9\nlab\t-9\nmisread\t124\n\t2\n\t0\n\t0\n", false); + assert lab_test_verdict("specs-generate\t0\nlab\t0\nseal-coverage\t-9\nmisread\t124\nlab\t124\nmisread\t1\nmisread\t0\ncheckout\t0\n", false); + assert lab_test_verdict("specs-parse\t0\ncheckout\t0\nspecs-parse\t-9\nseal-currency\t\nlean\t0\nseal-coverage\t0\n\t0\nsuite\t127\n", false); + assert lab_test_verdict("build\t0\n\t0\nseal-coverage\t0\nbuild\t0\n\t-9\nseal-coverage\t124\ncheckout\t-9\nmisread\t0\n", false); + assert lab_test_verdict("seal-currency\t0\n", true); + assert lab_test_verdict("misread\t0\nlean\t0\nseal-currency\t\nseal-currency\t\nspecs-parse\t0\nlean\t0\nseal-coverage\t127\nspecs-generate\t0\n", false); + assert lab_test_verdict("suite\t0\nlab\t0\nmisread\t2\nbuild\t0\n", true); + assert lab_test_verdict("misread\t0\nlean\t0\nseal-coverage\t-9\nseal-coverage\t-9\n", false); + assert lab_test_verdict("misread\t-9\ncheckout\t1\n", false); + assert lab_test_verdict("lab\t1\nbuild\t127\nspecs-generate\t0\nbuild\t1\nlean\t1\nlean\t124\nmisread\t0\nspecs-generate\t-9\n", false); + assert lab_test_verdict("specs-parse\t0\nspecs-parse\t127\n\t0\nlab\t2\nbuild\t2\ncheckout\t0\nmisread\t127\n", false); + assert lab_test_verdict("specs-parse\t2\nlean\t0\nspecs-parse\t0\nlean\nsuite\t-9\nseal-coverage\t0\nlab\n", false); + assert lab_test_verdict("specs-generate\t1\nseal-currency\t\nsuite\t127\n\t0\n\t0\nspecs-parse\t0\nspecs-generate\t0\n", false); + assert lab_test_verdict("specs-parse\t0\nlab\t127\nspecs-parse\t124\n", false); + assert lab_test_verdict("lab\t0\nseal-currency\t2\nseal-currency\t-9\nlab\t-9\n\t0\nseal-coverage\t0\n", false); + assert lab_test_verdict("checkout\t2\nlean\t1\nspecs-generate\nspecs-parse\t-9\ncheckout\t124\n", false); + assert lab_test_verdict("seal-currency\t0\ncheckout\t0\nlean\t124\ncheckout\t0\nspecs-parse\t0\nspecs-parse\t127\n", false); + assert lab_test_verdict("misread\t0\n", false); + assert lab_test_verdict("seal-coverage\t1\nseal-coverage\t127\nlab\t\ncheckout\t0\nspecs-parse\t124\n", false); + assert lab_test_verdict("seal-coverage\nsuite\t0\nlab\t0\nlean\t0\n", false); + assert lab_test_verdict("seal-currency\t\nseal-currency\t0\nlab\t0\nseal-currency\t\nbuild\t0\nspecs-generate\t2\nseal-currency\t1\n", false); + assert lab_test_verdict("lab\t0\ncheckout\nseal-currency\t-9\nseal-coverage\t2\nlean\t2\nmisread\t0\nlean\t-9\n\t0\n", false); + assert lab_test_verdict("seal-coverage\t-9\nseal-coverage\t2\nsuite\t1\n", false); + assert lab_test_verdict("lean\t2\nlab\t0\nspecs-parse\t0\n", false); + assert lab_test_verdict("misread\t\nsuite\t1\nbuild\t0\nbuild\t-9\nlab\t0\nsuite\t0\n\t124\nbuild\t0\n", false); + assert lab_test_verdict("seal-currency\t-9\nseal-currency\t0\nmisread\t0\nmisread\t2\nsuite\t-9\nmisread\nmisread\t0\n", false); + assert lab_test_verdict("suite\t0\nmisread\t0\nmisread\t124\nlean\t0\nspecs-generate\t0\n", true); + assert lab_test_verdict("suite\t0\ncheckout\t1\nseal-coverage\t1\nbuild\t127\nbuild\t0\n", false); + assert lab_test_verdict("lab\t0\n\t0\nseal-coverage\t1\ncheckout\t0\nseal-coverage\nmisread\t0\nspecs-generate\t0\nspecs-parse\t0\n", false); + assert lab_test_verdict("seal-coverage\t\nmisread\t0\nsuite\t0\ncheckout\t0\nbuild\t0\nseal-currency\t\ncheckout\t-9\nseal-currency\t0\n", false); + assert lab_test_verdict("checkout\t2\nmisread\t2\nsuite\t0\nbuild\t2\n\t1\n", false); + assert lab_test_verdict("lean\t0\nlab\t0\nspecs-generate\t127\nlean\t1\nlab\t0\n", false); + assert lab_test_verdict("seal-currency\t-9\nsuite\nspecs-parse\t0\nseal-coverage\t127\nsuite\t0\nlab\t0\nsuite\t127\ncheckout\t0\n", false); + assert lab_test_verdict("build\t0\nsuite\t0\nseal-coverage\t127\nspecs-parse\t0\n\t124\n", false); + assert lab_test_verdict("seal-coverage\t0\n", true); + assert lab_test_verdict("specs-parse\t0\ncheckout\t2\nseal-coverage\t2\nlab\t0\n\t0\nspecs-parse\t127\nseal-currency\t0\nseal-coverage\t0\n", false); + assert lab_test_verdict("lab\t0\n\t124\nseal-coverage\t127\n", false); + assert lab_test_verdict("specs-generate\t0\nlab\t127\nmisread\t0\nspecs-parse\t0\ncheckout\t0\nseal-coverage\t2\nlab\t0\n", false); + assert lab_test_verdict("build\t2\nlab\t124\n", false); + assert lab_test_verdict("misread\t124\nsuite\t2\nlab\nseal-coverage\t-9\nspecs-parse\t1\ncheckout\t2\nsuite\t0\n", false); + assert lab_test_verdict("lab\t0\nbuild\t124\nmisread\t124\nsuite\t-9\nspecs-parse\t-9\nspecs-generate\t127\ncheckout\t-9\nspecs-generate\t127\n", false); + assert lab_test_verdict("\t-9\nseal-coverage\t0\n", false); + assert lab_test_verdict("lab\t\n", false); + assert lab_test_verdict("specs-generate\t0\nbuild\t1\nmisread\t127\ncheckout\t127\nlab\t0\nlean\t0\nspecs-parse\t1\n", false); + assert lab_test_verdict("lab\t2\n\t1\ncheckout\t-9\n", false); + assert lab_test_verdict("build\t0\nlab\t0\nbuild\t0\ncheckout\t0\nspecs-parse\t1\n", false); + assert lab_test_verdict("misread\t0\nsuite\t0\nmisread\t1\n", true); +} + +// run_item(): red = [g['name'] for g in gates if g.get('exit') != 0 and g['name'] not in REPORT_ONLY] +test lab_py_table_red_gates { + assert lab_test_red("build\t-9\nlean\t0\nbuild\t127\n", "build\nbuild\n"); + assert lab_test_red("suite\t0\nsuite\t-9\nbuild\t\n", "suite\nbuild\n"); + assert lab_test_red("specs-parse\t-9\nseal-coverage\t0\nsuite\t-9\nseal-coverage\t0\n", "specs-parse\nsuite\n"); + assert lab_test_red("", ""); + assert lab_test_red("seal-coverage\t124\nlean\t-9\nlean\t0\nsuite\t1\n", "seal-coverage\nlean\nsuite\n"); + assert lab_test_red("seal-coverage\t0\nseal-coverage\t0\nspecs-generate\t0\nlab\t2\nbuild\t1\nspecs-parse\t1\nseal-currency\t1\nmisread\t-9\n", "lab\nbuild\nspecs-parse\nseal-currency\n"); + assert lab_test_red("seal-coverage\t0\nspecs-parse\t127\nspecs-generate\t124\nlean\t124\nseal-coverage\t0\nspecs-parse\t1\n", "specs-parse\nspecs-generate\nlean\nspecs-parse\n"); + assert lab_test_red("specs-parse\t127\nseal-coverage\t124\n", "specs-parse\nseal-coverage\n"); + assert lab_test_red("lean\t127\ncheckout\t0\nsuite\t0\nseal-currency\t124\n", "lean\nseal-currency\n"); + assert lab_test_red("specs-generate\t0\nseal-currency\t1\nspecs-generate\t0\n", "seal-currency\n"); + assert lab_test_red("lean\t124\nlean\t124\nspecs-generate\t1\nspecs-generate\t0\nmisread\t0\ncheckout\t2\n", "lean\nlean\nspecs-generate\ncheckout\n"); + assert lab_test_red("suite\t124\nseal-coverage\t0\n", "suite\n"); + assert lab_test_red("seal-coverage\t124\nlab\t127\nbuild\t127\nbuild\t0\nspecs-parse\t124\n", "seal-coverage\nlab\nbuild\nspecs-parse\n"); + assert lab_test_red("build\t124\nseal-coverage\t124\nspecs-parse\t\nbuild\t0\nmisread\t0\nmisread\t0\nsuite\t-9\n", "build\nseal-coverage\nspecs-parse\nsuite\n"); + assert lab_test_red("specs-parse\t124\nbuild\t0\nspecs-generate\t1\nbuild\t-9\nmisread\t0\nlean\t1\nsuite\t0\n", "specs-parse\nspecs-generate\nbuild\nlean\n"); + assert lab_test_red("specs-parse\t2\nseal-coverage\t2\nmisread\t124\nsuite\t1\nbuild\t0\n", "specs-parse\nseal-coverage\nsuite\n"); + assert lab_test_red("suite\t0\nlab\t\nbuild\t1\nlab\t124\nseal-coverage\t2\nsuite\t2\nspecs-generate\t127\n", "lab\nbuild\nlab\nseal-coverage\nsuite\nspecs-generate\n"); + assert lab_test_red("checkout\t-9\nmisread\t0\ncheckout\t-9\nlab\t-9\nmisread\t124\n", "checkout\ncheckout\nlab\n"); + assert lab_test_red("specs-generate\t0\nlab\t0\nseal-coverage\t-9\nmisread\t124\nlab\t124\nmisread\t1\nmisread\t0\ncheckout\t0\n", "seal-coverage\nlab\n"); + assert lab_test_red("specs-parse\t0\ncheckout\t0\nspecs-parse\t-9\nseal-currency\t\nlean\t0\nseal-coverage\t0\nsuite\t127\n", "specs-parse\nseal-currency\nsuite\n"); + assert lab_test_red("build\t0\nseal-coverage\t0\nbuild\t0\nseal-coverage\t124\ncheckout\t-9\nmisread\t0\n", "seal-coverage\ncheckout\n"); + assert lab_test_red("seal-currency\t0\n", ""); + assert lab_test_red("misread\t0\nlean\t0\nseal-currency\t\nseal-currency\t\nspecs-parse\t0\nlean\t0\nseal-coverage\t127\nspecs-generate\t0\n", "seal-currency\nseal-currency\nseal-coverage\n"); + assert lab_test_red("suite\t0\nlab\t0\nmisread\t2\nbuild\t0\n", ""); + assert lab_test_red("misread\t0\nlean\t0\nseal-coverage\t-9\nseal-coverage\t-9\n", "seal-coverage\nseal-coverage\n"); + assert lab_test_red("misread\t-9\ncheckout\t1\n", "checkout\n"); + assert lab_test_red("lab\t1\nbuild\t127\nspecs-generate\t0\nbuild\t1\nlean\t1\nlean\t124\nmisread\t0\nspecs-generate\t-9\n", "lab\nbuild\nbuild\nlean\nlean\nspecs-generate\n"); + assert lab_test_red("specs-parse\t0\nspecs-parse\t127\nlab\t2\nbuild\t2\ncheckout\t0\nmisread\t127\n", "specs-parse\nlab\nbuild\n"); + assert lab_test_red("specs-parse\t2\nlean\t0\nspecs-parse\t0\nlean\t\nsuite\t-9\nseal-coverage\t0\nlab\t\n", "specs-parse\nlean\nsuite\nlab\n"); + assert lab_test_red("specs-generate\t1\nseal-currency\t\nsuite\t127\nspecs-parse\t0\nspecs-generate\t0\n", "specs-generate\nseal-currency\nsuite\n"); + assert lab_test_red("specs-parse\t0\nlab\t127\nspecs-parse\t124\n", "lab\nspecs-parse\n"); + assert lab_test_red("lab\t0\nseal-currency\t2\nseal-currency\t-9\nlab\t-9\nseal-coverage\t0\n", "seal-currency\nseal-currency\nlab\n"); + assert lab_test_red("checkout\t2\nlean\t1\nspecs-generate\t\nspecs-parse\t-9\ncheckout\t124\n", "checkout\nlean\nspecs-generate\nspecs-parse\ncheckout\n"); + assert lab_test_red("seal-currency\t0\ncheckout\t0\nlean\t124\ncheckout\t0\nspecs-parse\t0\nspecs-parse\t127\n", "lean\nspecs-parse\n"); + assert lab_test_red("misread\t0\n", ""); + assert lab_test_red("seal-coverage\t1\nseal-coverage\t127\nlab\t\ncheckout\t0\nspecs-parse\t124\n", "seal-coverage\nseal-coverage\nlab\nspecs-parse\n"); + assert lab_test_red("seal-coverage\t\nsuite\t0\nlab\t0\nlean\t0\n", "seal-coverage\n"); + assert lab_test_red("seal-currency\t\nseal-currency\t0\nlab\t0\nseal-currency\t\nbuild\t0\nspecs-generate\t2\nseal-currency\t1\n", "seal-currency\nseal-currency\nspecs-generate\nseal-currency\n"); + assert lab_test_red("lab\t0\ncheckout\t\nseal-currency\t-9\nseal-coverage\t2\nlean\t2\nmisread\t0\nlean\t-9\n", "checkout\nseal-currency\nseal-coverage\nlean\nlean\n"); + assert lab_test_red("seal-coverage\t-9\nseal-coverage\t2\nsuite\t1\n", "seal-coverage\nseal-coverage\nsuite\n"); + assert lab_test_red("lean\t2\nlab\t0\nspecs-parse\t0\n", "lean\n"); + assert lab_test_red("misread\t\nsuite\t1\nbuild\t0\nbuild\t-9\nlab\t0\nsuite\t0\nbuild\t0\n", "suite\nbuild\n"); + assert lab_test_red("seal-currency\t-9\nseal-currency\t0\nmisread\t0\nmisread\t2\nsuite\t-9\nmisread\t\nmisread\t0\n", "seal-currency\nsuite\n"); + assert lab_test_red("suite\t0\nmisread\t0\nmisread\t124\nlean\t0\nspecs-generate\t0\n", ""); + assert lab_test_red("suite\t0\ncheckout\t1\nseal-coverage\t1\nbuild\t127\nbuild\t0\n", "checkout\nseal-coverage\nbuild\n"); + assert lab_test_red("lab\t0\nseal-coverage\t1\ncheckout\t0\nseal-coverage\t\nmisread\t0\nspecs-generate\t0\nspecs-parse\t0\n", "seal-coverage\nseal-coverage\n"); + assert lab_test_red("seal-coverage\t\nmisread\t0\nsuite\t0\ncheckout\t0\nbuild\t0\nseal-currency\t\ncheckout\t-9\nseal-currency\t0\n", "seal-coverage\nseal-currency\ncheckout\n"); + assert lab_test_red("checkout\t2\nmisread\t2\nsuite\t0\nbuild\t2\n", "checkout\nbuild\n"); + assert lab_test_red("lean\t0\nlab\t0\nspecs-generate\t127\nlean\t1\nlab\t0\n", "specs-generate\nlean\n"); + assert lab_test_red("seal-currency\t-9\nsuite\t\nspecs-parse\t0\nseal-coverage\t127\nsuite\t0\nlab\t0\nsuite\t127\ncheckout\t0\n", "seal-currency\nsuite\nseal-coverage\nsuite\n"); + assert lab_test_red("build\t0\nsuite\t0\nseal-coverage\t127\nspecs-parse\t0\n", "seal-coverage\n"); + assert lab_test_red("seal-coverage\t0\n", ""); + assert lab_test_red("specs-parse\t0\ncheckout\t2\nseal-coverage\t2\nlab\t0\nspecs-parse\t127\nseal-currency\t0\nseal-coverage\t0\n", "checkout\nseal-coverage\nspecs-parse\n"); + assert lab_test_red("lab\t0\nseal-coverage\t127\n", "seal-coverage\n"); + assert lab_test_red("specs-generate\t0\nlab\t127\nmisread\t0\nspecs-parse\t0\ncheckout\t0\nseal-coverage\t2\nlab\t0\n", "lab\nseal-coverage\n"); + assert lab_test_red("build\t2\nlab\t124\n", "build\nlab\n"); + assert lab_test_red("misread\t124\nsuite\t2\nlab\t\nseal-coverage\t-9\nspecs-parse\t1\ncheckout\t2\nsuite\t0\n", "suite\nlab\nseal-coverage\nspecs-parse\ncheckout\n"); + assert lab_test_red("lab\t0\nbuild\t124\nmisread\t124\nsuite\t-9\nspecs-parse\t-9\nspecs-generate\t127\ncheckout\t-9\nspecs-generate\t127\n", "build\nsuite\nspecs-parse\nspecs-generate\ncheckout\nspecs-generate\n"); + assert lab_test_red("lab\t\n", "lab\n"); + assert lab_test_red("specs-generate\t0\nbuild\t1\nmisread\t127\ncheckout\t127\nlab\t0\nlean\t0\nspecs-parse\t1\n", "build\ncheckout\nspecs-parse\n"); + assert lab_test_red("lab\t2\ncheckout\t-9\n", "lab\ncheckout\n"); + assert lab_test_red("build\t0\nlab\t0\nbuild\t0\ncheckout\t0\nspecs-parse\t1\n", "specs-parse\n"); + assert lab_test_red("misread\t0\nsuite\t0\nmisread\t1\n", ""); +} + +// >>> pinned table: verdict_verdict +// <<< pinned table: verdict_run +// run_item(): `name in NEEDS_BUILD and not built` skips a gate; `name == 'build' and code != 0` ends `built`; `'master' in branches and built` refreshes seals. +test lab_py_table_run_item_decisions { + assert lab_gate_skipped(0, true) == false; + assert lab_built_after(0, 0, true) == true; + assert lab_built_after(0, 1, true) == false; + assert lab_built_after(0, 124, true) == false; + assert lab_built_after(0, 127, true) == false; + assert lab_built_after(0, -9, true) == false; + assert lab_gate_skipped(0, false) == false; + assert lab_built_after(0, 0, false) == false; + assert lab_built_after(0, 1, false) == false; + assert lab_built_after(0, 124, false) == false; + assert lab_built_after(0, 127, false) == false; + assert lab_built_after(0, -9, false) == false; + assert lab_gate_skipped(1, true) == false; + assert lab_built_after(1, 0, true) == true; + assert lab_built_after(1, 1, true) == true; + assert lab_built_after(1, 124, true) == true; + assert lab_built_after(1, 127, true) == true; + assert lab_built_after(1, -9, true) == true; + assert lab_gate_skipped(1, false) == true; + assert lab_built_after(1, 0, false) == false; + assert lab_built_after(1, 1, false) == false; + assert lab_built_after(1, 124, false) == false; + assert lab_built_after(1, 127, false) == false; + assert lab_built_after(1, -9, false) == false; + assert lab_gate_skipped(2, true) == false; + assert lab_built_after(2, 0, true) == true; + assert lab_built_after(2, 1, true) == true; + assert lab_built_after(2, 124, true) == true; + assert lab_built_after(2, 127, true) == true; + assert lab_built_after(2, -9, true) == true; + assert lab_gate_skipped(2, false) == true; + assert lab_built_after(2, 0, false) == false; + assert lab_built_after(2, 1, false) == false; + assert lab_built_after(2, 124, false) == false; + assert lab_built_after(2, 127, false) == false; + assert lab_built_after(2, -9, false) == false; + assert lab_gate_skipped(3, true) == false; + assert lab_built_after(3, 0, true) == true; + assert lab_built_after(3, 1, true) == true; + assert lab_built_after(3, 124, true) == true; + assert lab_built_after(3, 127, true) == true; + assert lab_built_after(3, -9, true) == true; + assert lab_gate_skipped(3, false) == true; + assert lab_built_after(3, 0, false) == false; + assert lab_built_after(3, 1, false) == false; + assert lab_built_after(3, 124, false) == false; + assert lab_built_after(3, 127, false) == false; + assert lab_built_after(3, -9, false) == false; + assert lab_gate_skipped(4, true) == false; + assert lab_built_after(4, 0, true) == true; + assert lab_built_after(4, 1, true) == true; + assert lab_built_after(4, 124, true) == true; + assert lab_built_after(4, 127, true) == true; + assert lab_built_after(4, -9, true) == true; + assert lab_gate_skipped(4, false) == true; + assert lab_built_after(4, 0, false) == false; + assert lab_built_after(4, 1, false) == false; + assert lab_built_after(4, 124, false) == false; + assert lab_built_after(4, 127, false) == false; + assert lab_built_after(4, -9, false) == false; + assert lab_gate_skipped(5, true) == false; + assert lab_built_after(5, 0, true) == true; + assert lab_built_after(5, 1, true) == true; + assert lab_built_after(5, 124, true) == true; + assert lab_built_after(5, 127, true) == true; + assert lab_built_after(5, -9, true) == true; + assert lab_gate_skipped(5, false) == true; + assert lab_built_after(5, 0, false) == false; + assert lab_built_after(5, 1, false) == false; + assert lab_built_after(5, 124, false) == false; + assert lab_built_after(5, 127, false) == false; + assert lab_built_after(5, -9, false) == false; + assert lab_gate_skipped(6, true) == false; + assert lab_built_after(6, 0, true) == true; + assert lab_built_after(6, 1, true) == true; + assert lab_built_after(6, 124, true) == true; + assert lab_built_after(6, 127, true) == true; + assert lab_built_after(6, -9, true) == true; + assert lab_gate_skipped(6, false) == true; + assert lab_built_after(6, 0, false) == false; + assert lab_built_after(6, 1, false) == false; + assert lab_built_after(6, 124, false) == false; + assert lab_built_after(6, 127, false) == false; + assert lab_built_after(6, -9, false) == false; + assert lab_gate_skipped(7, true) == false; + assert lab_built_after(7, 0, true) == true; + assert lab_built_after(7, 1, true) == true; + assert lab_built_after(7, 124, true) == true; + assert lab_built_after(7, 127, true) == true; + assert lab_built_after(7, -9, true) == true; + assert lab_gate_skipped(7, false) == true; + assert lab_built_after(7, 0, false) == false; + assert lab_built_after(7, 1, false) == false; + assert lab_built_after(7, 124, false) == false; + assert lab_built_after(7, 127, false) == false; + assert lab_built_after(7, -9, false) == false; + assert lab_seals_due(true, true) == true; + assert lab_seals_due(true, false) == false; + assert lab_seals_due(false, true) == false; + assert lab_seals_due(false, false) == false; +} + +// sh(): `if time.time() - start > timeout: return 124` else the program's code. +test lab_py_table_a_limit_kills_the_gate { + assert lab_exit_after(0, 10.0, 1800) == 0; + assert lab_exit_after(0, 1800.0, 1800) == 0; + assert lab_exit_after(0, 1800.5, 1800) == 124; + assert lab_exit_after(1, 1799.9, 1800) == 1; + assert lab_exit_after(0, 3600.01, 3600) == 124; + assert lab_exit_after(-9, 3601.0, 3600) == 124; + assert lab_exit_after(2, 0.0, 1800) == 2; + assert lab_exit_after(0, 5400.0, 5400) == 0; + assert lab_exit_after(0, 5400.000001, 5400) == 124; + assert lab_exit_after(127, 0.1, 3600) == 127; + assert lab_exit_after(-9, 1800.2, 1800) == 124; + assert lab_exit_after(0, 0.0, 0) == 0; + assert LAB_EXIT_TIMEOUT == 124 and LAB_EXIT_NOT_FOUND == 127; +} + +// publish_latest(): the state a head shows. +test lab_py_table_head_state { + assert lab_head_state(true, true, true, true) == 4; + assert lab_head_state(true, true, true, false) == 4; + assert lab_head_state(true, true, false, true) == 4; + assert lab_head_state(true, true, false, false) == 4; + assert lab_head_state(true, false, true, true) == 3; + assert lab_head_state(true, false, true, false) == 3; + assert lab_head_state(true, false, false, true) == 3; + assert lab_head_state(true, false, false, false) == 3; + assert lab_head_state(false, true, true, true) == 2; + assert lab_head_state(false, true, true, false) == 2; + assert lab_head_state(false, true, false, true) == 1; + assert lab_head_state(false, true, false, false) == 0; + assert lab_head_state(false, false, true, true) == 2; + assert lab_head_state(false, false, true, false) == 2; + assert lab_head_state(false, false, false, true) == 1; + assert lab_head_state(false, false, false, false) == 0; + assert span_is(lab_head_state_name(LAB_HS_NOT_RUN), 0, 7, "not run") and span_is(lab_head_state_name(LAB_HS_QUEUED), 0, 6, "queued"); + assert span_is(lab_head_state_name(LAB_HS_RUNNING), 0, 7, "running") and span_is(lab_head_state_name(LAB_HS_UNREADABLE), 0, 10, "unreadable"); + assert lab_head_state_name(LAB_HS_FROM_RUN).len == 0; +} + +// >>> pinned table: verdict_run + +// Scenario: `\s` crosses lines. A row with no description still matches when the next line starts with a non-space, and it +// takes that character; the next row then loses its first digit and is not read (Python: 1 2 3 only). Checked on Python. +test the_row_regex_crosses_lines_as_python_does { + assert lab_test_misread(" pairs refused silent\n 10 20 30 \nx", true, 10, 20, 30); + assert lab_test_misread(" pairs refused silent\n 1 2 3\n4 5 6 x\n", true, 1, 2, 3); + assert lab_test_misread(" pairs refused silent\n 1 2 3\n 4 5 6 x\n", true, 1, 2, 3); + assert lab_test_misread(" pairs refused silent\n 1 2 3 a\n 4 5 6 x\n", true, 5, 7, 9); + assert lab_test_misread(" pairs refused silent\n 1 2 3", true, 0, 0, 0); +} + +// Scenario: the one branch of misread_line() no input of misread_counts() reaches: a refused count of None. +test refusals_not_measured_is_said_when_the_refused_count_is_none { + var out : [128]u8 = undefined; + const m : LabMisread = LabMisread { read: true, pairs: 3, refused: LAB_DEC_BAD, silent: 1 }; + const w : usize = lab_misread_line(m, "fb", 0, 2, &out, 128, 0); + assert w == 44; + assert span_is(&out, 0, w, "1 of 3 pair(s) silent, refusals not measured"); + const n : LabMisread = LabMisread { read: false, pairs: 3, refused: 1, silent: 1 }; + const x : usize = lab_misread_line(n, "fb", 0, 2, &out, 128, 0); + assert x == 2 and span_is(&out, 0, 2, "fb"); +} + +// Scenario (NOT Python): a count of more than 18 digits, or a total past 10^18 - 1, is not read. Python reads both +// (12345678901234567890, and 999999999999999999 + 1); the port fails closed, and the summary falls back to the log's own line. +test a_count_this_port_cannot_hold_makes_the_table_unread { + assert lab_test_misread(" pairs refused silent\n 12345678901234567890 2 3 x\n", false, 0, 0, 0); + assert lab_test_misread(" pairs refused silent\n 999999999999999999 2 3 x\n 1 1 1 y\n", false, 0, 0, 0); + assert lab_test_misread(" pairs refused silent\n 999999999999999999 2 3 x\n", true, 999999999999999999, 2, 3); + assert lab_test_misread_line(" pairs refused silent\n 12345678901234567890 2 3 x\n", "the log line", "the log line"); +} + +// Scenario: summary() cuts at 300 CHARACTERS: 299 letters, then e-acute (2 bytes), then z is 302 bytes and 301 characters; the +// summary is the first 300 characters, 301 bytes. (Python: len 300, ends 'aae-acute'.) +test the_summary_is_cut_at_300_characters_not_bytes { + var b : [302]u8 = undefined; + var k : usize = 0; + while (k < 299) { + b[k] = 97; + k = k + 1; + } + b[299] = 195; + b[300] = 169; + b[301] = 122; + var out : [1400]u8 = undefined; + assert lab_summary_write(&b, LAB_PAT_NONE, &out, 1400, 0) == 301; + assert out[298] == 97 and out[299] == 195 and out[300] == 169; + const sp : LabSummarySpan = lab_summary(&b, LAB_PAT_NONE); + assert sp.from == 0 and sp.to == 301; +} + +// Scenario: the lines of a log are split on the ten characters splitlines() splits on, and stripped of the Unicode spaces +// (checked on Python: 'a VT b' -> 'b'; 'x CR y' -> 'y'; 'RATCHET NEL two' under RATCHET -> 'RATCHET'; NBSP x NBSP -> 'x'; US is +// not a break; a zero width space is not a space). +test the_summary_reads_lines_and_spaces_the_way_python_does { + var vt : [3]u8 = undefined; + vt[0] = 97; + vt[1] = 11; + vt[2] = 98; + assert lab_test_summary(&vt, LAB_PAT_NONE, "b"); + var cr : [3]u8 = undefined; + cr[0] = 120; + cr[1] = 13; + cr[2] = 121; + assert lab_test_summary(&cr, LAB_PAT_NONE, "y"); + var nel : [12]u8 = undefined; + nel[0] = 82; + nel[1] = 65; + nel[2] = 84; + nel[3] = 67; + nel[4] = 72; + nel[5] = 69; + nel[6] = 84; + nel[7] = 194; + nel[8] = 133; + nel[9] = 116; + nel[10] = 119; + nel[11] = 111; + assert lab_test_summary(&nel, LAB_PAT_SUITE, "RATCHET"); + var nb : [5]u8 = undefined; + nb[0] = 194; + nb[1] = 160; + nb[2] = 120; + nb[3] = 194; + nb[4] = 160; + assert lab_test_summary(&nb, LAB_PAT_NONE, "x"); + var us : [3]u8 = undefined; + us[0] = 97; + us[1] = 31; + us[2] = 98; + const sp : LabSummarySpan = lab_summary(&us, LAB_PAT_NONE); + assert sp.from == 0 and sp.to == 3; + var zw : [4]u8 = undefined; + zw[0] = 120; + zw[1] = 226; + zw[2] = 128; + zw[3] = 139; + const zs : LabSummarySpan = lab_summary(&zw, LAB_PAT_NONE); + assert zs.from == 0 and zs.to == 4; +} + +// Scenario: the gate table is a closed set of eight; a name outside it is no gate (negative control: a command, a padded or +// upper-case spelling, a path, a prefix and a longer name are all refused), and every gate of the table has a command. +test only_the_eight_gates_of_the_table_are_known { + var i : usize = 0; + while (i < LAB_GATE_COUNT) { + const n : string = lab_gate_name(i); + assert lab_gate_known(n, 0, n.len) == true; + assert lab_gate_index(n, 0, n.len) == i; + assert lab_gate_cmd(i).len > 10; + assert lab_gate_timeout(i) >= 1800; + i = i + 1; + } + assert lab_gate_known("", 0, 0) == false; + assert lab_gate_known("Build", 0, 5) == false; + assert lab_gate_known("build ", 0, 6) == false; + assert lab_gate_known("buil", 0, 4) == false; + assert lab_gate_known("builds", 0, 6) == false; + assert lab_gate_known("build;ls", 0, 8) == false; + assert lab_gate_known("rm -rf /", 0, 8) == false; + assert lab_gate_known("../suite", 0, 8) == false; + assert lab_gate_known("specs", 0, 5) == false; + assert lab_gate_known("seal-currency", 0, 13) == true; + assert lab_gate_known("seal-currencyX", 0, 13) == true; + assert lab_gate_known("seal-currencyX", 0, 14) == false; + assert lab_gate_known("checkout", 0, 8) == false; + assert lab_gate_known("lab", 0, 3) == false; + assert LAB_GATE_COUNT == 8; +}