nx_field_beat_gate.nx source
↩ module page · 156 lines · 9834 B
1// nx_field_beat_gate.nx -- THE GATE FOR THE FIELD BEAT'S DECISIONS (nx_field_beat_lib, /compare/fieldwatch FW4).
2//
3// Drives the decision core IN-PROCESS on fixtures assembled at RUNTIME under /tmp/nx_field_beat_gate/ (no fork of the
4// discover organ, no production stamp, no journal row). What it must catch: a list reader that counts comment or blank
5// lines as domains or keeps a CR inside a name; a receipt reader that takes a seed line's candidates= for the run's, or
6// reads an absent field as zero; a classifier that calls a clean exit with no receipt OK; a stamp path that lets a trial
7// run write the production heartbeat; a stamp whose partition does not sum; a verdict that reads GREEN over zero runs.
8// The fork of nx_field_discover is deliberately not exercised here (that would share the production artifact with a
9// beat); the first production stamp with domains= and sum= is the wire's witness.
10// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
11import "nx_syscalls.nx"
12import "nx_gate_verdict.nx"
13import "nx_field_beat_lib.nx"
14
15const G_DIR: *u8 = "/tmp/nx_field_beat_gate"
16const G_STAMP: *u8 = "/tmp/nx_field_beat_gate/fixture.stamp"
17const G_STAMP_RED: *u8 = "/tmp/nx_field_beat_gate/fixture_red.stamp"
18const G_MODEDIR: i64 = 493
19const G_CAP: i64 = 65536
20const G_PATH: i64 = 1024
21// '@' stands for '#' and '~' for CR in these literals (the lexer keeps '#' out of strings); g_fix() rewrites them at runtime.
22const G_LIST: *u8 = "@ comment line\nmodding~\n fieldwatch \n\nsearch\n@ tail\n"
23const G_COMMENTS: *u8 = "@ a\n@ b\n\n"
24const G_RECEIPT: *u8 = "[nx_field_discover] CWD anchored\nFIELD-SEED key=nexus kind=named url=https://a/ candidates=5\nFIELD-OK dom=modding seeds=43 scanned=40 fetched=2 reused=38 failed=3 sum=43 named=31 candidates=736 mentions=1200 capped=0 bytes=9999 out=/x\n"
25const G_NO_OK: *u8 = "FIELD-SEED key=nexus kind=named url=https://a/ candidates=5\nFIELD-REFUSE no seeds file\n"
26const G_CH_AT: i64 = 64
27const G_CH_TILDE: i64 = 126
28const G_CH_HASH: i64 = 35
29const G_CH_CR: i64 = 13
30const G_TS: i64 = 1788648000
31const G_LISTPATH: *u8 = "/tmp/nx_field_beat_gate/list.txt"
32const G_OVER: i64 = 3 // rows past FB_MAX_DOMAINS in the capped fixture
33
34func g_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
35func g_fix(src: *u8) -> *u8 {
36 let n: i64 = g_slen(src)
37 let b: *u8 = sys_mmap(n + 1)
38 var i: i64 = 0
39 while i < n {
40 var c: i64 = src[i] as i64
41 if c == G_CH_AT { c = G_CH_HASH }
42 if c == G_CH_TILDE { c = G_CH_CR }
43 b[i] = c as u8
44 i = i + 1
45 }
46 b[n] = 0 as u8
47 return b
48}
49func g_read(path: *u8, lenout: *i64) -> *u8 {
50 let lp: *i64 = sys_mmap(16) as *i64
51 let b: *u8 = sys_read_file(path, lp)
52 if (b as i64) == 0 { lenout[0] = 0; return 0 as *u8 }
53 lenout[0] = lp[0]
54 return b
55}
56
57func main(argc: i64, argv: *i64) -> i64 {
58 let ctr: *i64 = gv_ctr()
59 gv_head("nx_field_beat gate -- the beat's decisions are measured in-process; the wire is witnessed by its production stamp" as *u8)
60 sys_mkdir(G_DIR, G_MODEDIR)
61
62 // ---- the list ----
63 let lst: *u8 = g_fix(G_LIST)
64 let names: *u8 = sys_mmap(FB_NAME_CAP * FB_MAX_DOMAINS)
65 let capped: *i64 = sys_mmap(16) as *i64
66 let n: i64 = fb_list_domains(lst, g_slen(lst), names, capped)
67 gv_check_eq("list-names-three-domains-skipping-comment-and-blank-lines" as *u8, n, 3, ctr)
68 var t2: i64 = 0
69 if n == 3 { if fb_streq(fb_domain_at(names, 0), "modding" as *u8) == 1 { if fb_streq(fb_domain_at(names, 1), "fieldwatch" as *u8) == 1 { if fb_streq(fb_domain_at(names, 2), "search" as *u8) == 1 { t2 = 1 } } } }
70 gv_check("names-are-exact-with-CR-and-surrounding-spaces-trimmed (modding~ -> modding, two-space fieldwatch -> fieldwatch)" as *u8, t2, ctr)
71 gv_check_eq("a-list-of-111-rows-is-not-capped (capped flag 0 on the fixture)" as *u8, capped[0], 0, ctr)
72 let cm: *u8 = g_fix(G_COMMENTS)
73 let ncm: i64 = fb_list_domains(cm, g_slen(cm), names, capped)
74 gv_check_eq("neg-control-a-list-of-only-comments-and-blanks-names-zero-domains" as *u8, ncm, 0, ctr)
75 let nempty: i64 = fb_list_domains(cm, 0, names, capped)
76 gv_check_eq("neg-control-an-empty-buffer-names-zero-domains" as *u8, nempty, 0, ctr)
77 // capped: FB_MAX_DOMAINS + G_OVER one-letter rows
78 let big: *u8 = sys_mmap(2 * (FB_MAX_DOMAINS + G_OVER) + 1)
79 var k: i64 = 0
80 while k < FB_MAX_DOMAINS + G_OVER { big[2 * k] = 100 as u8; big[2 * k + 1] = 10 as u8; k = k + 1 }
81 let nbig: i64 = fb_list_domains(big, 2 * (FB_MAX_DOMAINS + G_OVER), names, capped)
82 var t6: i64 = 0
83 if nbig == FB_MAX_DOMAINS { if capped[0] == 1 { t6 = 1 } }
84 gv_check("a-list-past-the-arena-stops-at-FB_MAX_DOMAINS-and-raises-capped (a floor that announces itself)" as *u8, t6, ctr)
85
86 // ---- the receipt ----
87 let rcp: *u8 = G_RECEIPT
88 let rn: i64 = g_slen(rcp)
89 gv_check_eq("a-receipt-with-a-FIELD-OK-line-reads-ok" as *u8, fb_receipt_ok(rcp, rn), 1, ctr)
90 gv_check_eq("candidates-read-from-the-FIELD-OK-line-not-the-seed-line (736 not 5)" as *u8, fb_receipt_int(rcp, rn, "candidates" as *u8), 736, ctr)
91 gv_check_eq("named-read-from-the-FIELD-OK-line" as *u8, fb_receipt_int(rcp, rn, "named" as *u8), 31, ctr)
92 gv_check_eq("failed-read-from-the-FIELD-OK-line" as *u8, fb_receipt_int(rcp, rn, "failed" as *u8), 3, ctr)
93 gv_check_eq("an-absent-key-reads-UNPARSED-never-zero" as *u8, fb_receipt_int(rcp, rn, "nosuchkey" as *u8), FB_UNPARSED, ctr)
94 let bad: *u8 = G_NO_OK
95 let bn: i64 = g_slen(bad)
96 gv_check_eq("neg-control-a-receipt-without-FIELD-OK-reads-not-ok" as *u8, fb_receipt_ok(bad, bn), 0, ctr)
97 var fired_bad: i64 = 0
98 if fb_receipt_int(bad, bn, "candidates" as *u8) == FB_UNPARSED { fired_bad = 1 }
99 var fired_good: i64 = 0
100 if fb_receipt_int(rcp, rn, "candidates" as *u8) == FB_UNPARSED { fired_good = 1 }
101 gv_bite("neg-control-a-seed-line-carrying-candidates=5-with-no-FIELD-OK-yields-UNPARSED-not-5" as *u8, fired_bad, fired_good, ctr)
102
103 // ---- the class ----
104 gv_check_eq("exit-0-with-a-receipt-and-seeds-is-OK" as *u8, fb_classify(0, 1, 1), FB_CLASS_OK, ctr)
105 gv_check_eq("no-seeds-file-is-SKIP-whatever-the-run-said" as *u8, fb_classify(0, 1, 0), FB_CLASS_SKIP, ctr)
106 gv_check_eq("neg-control-a-clean-exit-that-printed-no-receipt-is-FAIL (the silent manifestation)" as *u8, fb_classify(0, 0, 1), FB_CLASS_FAIL, ctr)
107 gv_check_eq("neg-control-exit-4-with-a-receipt-is-FAIL" as *u8, fb_classify(4, 1, 1), FB_CLASS_FAIL, ctr)
108
109 // ---- the stamp path ----
110 let sp: *u8 = sys_mmap(G_PATH)
111 fb_stamp_path(G_LISTPATH, 1, sp, G_PATH)
112 gv_check("the-default-list-stamps-the-production-heartbeat-path" as *u8, fb_streq(sp, FB_STAMP_DEFAULT), ctr)
113 fb_stamp_path(G_LISTPATH, 0, sp, G_PATH)
114 gv_check("neg-control-a-non-default-list-stamps-beside-itself-never-the-production-path (<list>.stamp)" as *u8, fb_streq(sp, "/tmp/nx_field_beat_gate/list.txt.stamp" as *u8), ctr)
115
116 // ---- the stamp ----
117 let fd: i64 = sys_openat_wr(G_STAMP, MODE_0644)
118 gv_need("fixture-stamp-opened-for-write" as *u8, (fd >= 0) as i64, ctr)
119 fb_stamp_write(fd, G_TS, G_LISTPATH, 5, 2, 0, 3, 800, 0, 7)
120 sys_close(fd)
121 let slen: *i64 = sys_mmap(16) as *i64
122 let sb: *u8 = g_read(G_STAMP, slen)
123 gv_need("fixture-stamp-read-back" as *u8, ((sb as i64) != 0) as i64, ctr)
124 let sn: i64 = slen[0]
125 gv_check_eq("stamp-carries-ts" as *u8, fb_span_int(sb, 0, sn, "ts" as *u8), G_TS, ctr)
126 let s_ok: i64 = fb_span_int(sb, 0, sn, "ok" as *u8)
127 let s_failed: i64 = fb_span_int(sb, 0, sn, "failed" as *u8)
128 let s_skip: i64 = fb_span_int(sb, 0, sn, "skipped_no_seeds" as *u8)
129 let s_sum: i64 = fb_span_int(sb, 0, sn, "sum" as *u8)
130 let s_ran: i64 = fb_span_int(sb, 0, sn, "ran" as *u8)
131 gv_check_eq("stamp-partition-ok-plus-failed-plus-skipped-equals-sum" as *u8, s_ok + s_failed + s_skip, s_sum, ctr)
132 gv_check_eq("stamp-ran-equals-ok-plus-failed" as *u8, s_ran, s_ok + s_failed, ctr)
133 gv_check_eq("stamp-domains-is-the-list-count-not-the-run-count" as *u8, fb_span_int(sb, 0, sn, "domains" as *u8), 5, ctr)
134 gv_check_eq("stamp-candidates_total-carried" as *u8, fb_span_int(sb, 0, sn, "candidates_total" as *u8), 800, ctr)
135 gv_check_eq("stamp-carries-drifted_total (FW5: the beat sums the discover organ's drifted= count into the heartbeat as a measurement, never an alarm)" as *u8, fb_span_int(sb, 0, sn, "drifted_total" as *u8), 7, ctr)
136 // verdict
137 gv_check("two-ok-zero-failed-reads-GREEN" as *u8, fb_streq(fb_stamp_verdict(2, 0), "GREEN" as *u8), ctr)
138 gv_check("neg-control-one-failed-reads-RED" as *u8, fb_streq(fb_stamp_verdict(2, 1), "RED" as *u8), ctr)
139 gv_check("neg-control-zero-runs-read-EMPTY-never-GREEN (a beat that did no work cannot acquit)" as *u8, fb_streq(fb_stamp_verdict(0, 0), "EMPTY" as *u8), ctr)
140 let fd2: i64 = sys_openat_wr(G_STAMP_RED, MODE_0644)
141 fb_stamp_write(fd2, G_TS, G_LISTPATH, 5, 1, 1, 3, 400, 1, 0)
142 sys_close(fd2)
143 let rlen: *i64 = sys_mmap(16) as *i64
144 let rb: *u8 = g_read(G_STAMP_RED, rlen)
145 var t_red: i64 = 0
146 if (rb as i64) != 0 { if fb_span_int(rb, 0, rlen[0], "failed" as *u8) == 1 { if fb_span_int(rb, 0, rlen[0], "capped" as *u8) == 1 { t_red = 1 } } }
147 gv_check("a-written-stamp-carries-failed-and-capped-as-read-back" as *u8, t_red, ctr)
148
149 gv_kv("domains_parsed" as *u8, n)
150 gv_kv("receipt_candidates" as *u8, fb_receipt_int(rcp, rn, "candidates" as *u8))
151 gv_kv("stamp_bytes" as *u8, sn)
152 gv_kv("max_domains" as *u8, FB_MAX_DOMAINS)
153 let rc: i64 = gv_verdict("NX-FIELDBEAT" as *u8, ctr, "the field beat reads its list, its receipts and its own stamp the same way, refuses the empty set, and cannot forge the production heartbeat from a trial run" as *u8)
154 sys_exit(rc)
155 return rc
156}