nx_printer_health_test.nx source
↩ module page · 106 lines · 6024 B
1// nx_printer_health_test.nx -- gate for the honest-status / anti-silent-failure model (R1).
2//
3// Builds real IPP responses with the codec and asserts the health verdict for each, with unique exit
4// codes per invariant. This is the proof that we surface the truth a Brother UI hides.
5//
6// 1x idle + accepting + reasons='none' -> CAN_PRINT
7// 2x STOPPED + media-jam(bare) + marker-supply-low-warning -> BLOCKED, worst=ERROR, 2 reasons
8// 3x idle + marker-supply-low-warning + toner-low-report -> DEGRADED, worst=WARNING
9// 4x idle but printer-is-accepting-jobs=false -> BLOCKED
10// 7x per-reason severity classifier (suffix rules + bare/stopped vs bare/idle)
11// 8x malformed reasons (value-length overrun) -> UNKNOWN (never a false "ready")
12//
13// expect_exit: 0
14// license_tier: ORIGINAL
15
16import "nx_syscalls.nx"
17import "nx_ipp_codec.nx"
18import "nx_printer_health.nx"
19
20func t_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
21
22func main() -> i64 {
23 let sc: *i64 = sys_mmap(64) as *i64
24 let o_state: *i64 = sys_mmap(16) as *i64
25 let o_sev: *i64 = sys_mmap(16) as *i64
26 let o_cnt: *i64 = sys_mmap(16) as *i64
27 let o_acc: *i64 = sys_mmap(16) as *i64
28
29 let n_pst: *u8 = "printer-state"
30 let n_acc: *u8 = "printer-is-accepting-jobs"
31 let n_psr: *u8 = "printer-state-reasons"
32
33 // ===== KAT 1: idle, accepting, no reasons -> CAN_PRINT =====
34 let a: *u8 = sys_mmap(2048)
35 var pa: i64 = nx_ipp_begin(a, 1, 1, NX_IPP_STATUS_OK, 1)
36 pa = nx_ipp_group(a, pa, NX_IPP_GRP_PRINTER)
37 pa = nx_ipp_attr_int(a, pa, NX_IPP_VT_ENUM, n_pst, nx_ipp_strlen(n_pst), NX_IPP_PSTATE_IDLE)
38 pa = nx_ipp_attr_bool(a, pa, n_acc, nx_ipp_strlen(n_acc), 1)
39 pa = nx_ipp_attr(a, pa, NX_IPP_VT_KEYWORD, n_psr, nx_ipp_strlen(n_psr), "none", 4)
40 pa = nx_ipp_end(a, pa)
41 if nx_printer_diagnose(a, pa, sc, o_state, o_sev, o_cnt, o_acc) != NX_PR_CAN_PRINT { return 1 }
42 if o_state[0] != NX_IPP_PSTATE_IDLE { return 2 }
43 if o_sev[0] != NX_SEV_NONE { return 3 }
44 if o_acc[0] != 1 { return 4 }
45
46 // ===== KAT 2: STOPPED + bare media-jam + marker-supply-low-warning -> BLOCKED, worst=ERROR =====
47 let b: *u8 = sys_mmap(2048)
48 var pb: i64 = nx_ipp_begin(b, 1, 1, NX_IPP_STATUS_OK, 1)
49 pb = nx_ipp_group(b, pb, NX_IPP_GRP_PRINTER)
50 pb = nx_ipp_attr_int(b, pb, NX_IPP_VT_ENUM, n_pst, nx_ipp_strlen(n_pst), NX_IPP_PSTATE_STOPPED)
51 pb = nx_ipp_attr_bool(b, pb, n_acc, nx_ipp_strlen(n_acc), 1)
52 pb = nx_ipp_attr(b, pb, NX_IPP_VT_KEYWORD, n_psr, nx_ipp_strlen(n_psr), "media-jam", 9)
53 pb = nx_ipp_attr_add(b, pb, NX_IPP_VT_KEYWORD, "marker-supply-low-warning", 25)
54 pb = nx_ipp_end(b, pb)
55 if nx_printer_diagnose(b, pb, sc, o_state, o_sev, o_cnt, o_acc) != NX_PR_BLOCKED { return 20 }
56 if o_state[0] != NX_IPP_PSTATE_STOPPED { return 21 }
57 if o_sev[0] != NX_SEV_ERROR { return 22 } // bare media-jam while stopped = ERROR, beats the warning
58 if o_cnt[0] != 2 { return 23 }
59
60 // ===== KAT 3: idle + marker-supply-low-warning + toner-low-report -> DEGRADED, worst=WARNING =====
61 let c: *u8 = sys_mmap(2048)
62 var pc: i64 = nx_ipp_begin(c, 1, 1, NX_IPP_STATUS_OK, 1)
63 pc = nx_ipp_group(c, pc, NX_IPP_GRP_PRINTER)
64 pc = nx_ipp_attr_int(c, pc, NX_IPP_VT_ENUM, n_pst, nx_ipp_strlen(n_pst), NX_IPP_PSTATE_IDLE)
65 pc = nx_ipp_attr_bool(c, pc, n_acc, nx_ipp_strlen(n_acc), 1)
66 pc = nx_ipp_attr(c, pc, NX_IPP_VT_KEYWORD, n_psr, nx_ipp_strlen(n_psr), "marker-supply-low-warning", 25)
67 pc = nx_ipp_attr_add(c, pc, NX_IPP_VT_KEYWORD, "toner-low-report", 16)
68 pc = nx_ipp_end(c, pc)
69 if nx_printer_diagnose(c, pc, sc, o_state, o_sev, o_cnt, o_acc) != NX_PR_DEGRADED { return 30 }
70 if o_sev[0] != NX_SEV_WARNING { return 31 }
71 if o_cnt[0] != 2 { return 32 }
72
73 // ===== KAT 4: idle but NOT accepting jobs -> BLOCKED =====
74 let d: *u8 = sys_mmap(2048)
75 var pd: i64 = nx_ipp_begin(d, 1, 1, NX_IPP_STATUS_OK, 1)
76 pd = nx_ipp_group(d, pd, NX_IPP_GRP_PRINTER)
77 pd = nx_ipp_attr_int(d, pd, NX_IPP_VT_ENUM, n_pst, nx_ipp_strlen(n_pst), NX_IPP_PSTATE_IDLE)
78 pd = nx_ipp_attr_bool(d, pd, n_acc, nx_ipp_strlen(n_acc), 0)
79 pd = nx_ipp_attr(d, pd, NX_IPP_VT_KEYWORD, n_psr, nx_ipp_strlen(n_psr), "none", 4)
80 pd = nx_ipp_end(d, pd)
81 if nx_printer_diagnose(d, pd, sc, o_state, o_sev, o_cnt, o_acc) != NX_PR_BLOCKED { return 40 }
82 if o_acc[0] != 0 { return 41 }
83
84 // ===== KAT 7: per-reason severity classifier (operate directly on string literals) =====
85 if nx_ph_reason_severity("media-empty-error", 0, 17, NX_IPP_PSTATE_IDLE) != NX_SEV_ERROR { return 70 }
86 if nx_ph_reason_severity("cover-open-warning", 0, 18, NX_IPP_PSTATE_IDLE) != NX_SEV_WARNING { return 71 }
87 if nx_ph_reason_severity("toner-low-report", 0, 16, NX_IPP_PSTATE_IDLE) != NX_SEV_REPORT { return 72 }
88 if nx_ph_reason_severity("none", 0, 4, NX_IPP_PSTATE_STOPPED) != NX_SEV_NONE { return 73 }
89 if nx_ph_reason_severity("media-empty", 0, 11, NX_IPP_PSTATE_STOPPED) != NX_SEV_ERROR { return 74 }
90 if nx_ph_reason_severity("media-empty", 0, 11, NX_IPP_PSTATE_IDLE) != NX_SEV_WARNING { return 75 }
91
92 // ===== KAT 8: malformed reasons (value-length overrun) -> UNKNOWN =====
93 let e: *u8 = sys_mmap(2048)
94 var pe: i64 = nx_ipp_begin(e, 1, 1, NX_IPP_STATUS_OK, 1)
95 pe = nx_ipp_group(e, pe, NX_IPP_GRP_PRINTER)
96 pe = nx_ipp_attr_int(e, pe, NX_IPP_VT_ENUM, n_pst, nx_ipp_strlen(n_pst), NX_IPP_PSTATE_IDLE)
97 e[pe] = NX_IPP_VT_KEYWORD as u8
98 pe = pe + 1
99 pe = nx_ipp_put_u16(e, pe, 21)
100 pe = nx_ipp_memcpy(e, pe, n_psr, 21)
101 pe = nx_ipp_put_u16(e, pe, 0x7fff) // lying value-length, no value bytes
102 if nx_printer_diagnose(e, pe, sc, o_state, o_sev, o_cnt, o_acc) != NX_PR_UNKNOWN { return 80 }
103
104 t_puts("nx_printer_health: 6/6 KAT groups PASS (can_print=ready/degraded/blocked + accepting + severity-suffix + malformed->UNKNOWN)\n")
105 return 0
106}