code wiki / (root) / nx_iot_watchdog_test.nx

nx_iot_watchdog_test.nx source

↩ module page · 147 lines · 7662 B

1// nx_iot_watchdog_test.nx -- gate for the per-device link healer. 2// 3// Proves the operator's blink loop is closed automatically + MEASURED: 4// - sealed-enum validity (health / observation / action) 5// - NEVER-BRICK: the whole action alphabet is brick-safe + writes no 6// firmware; the classifier is FAIL-SAFE (out-of-range -> RED) 7// - init: default threshold / override / null guard 8// - per-transition coverage of every (health x observation) edge 9// - THE FULL BLINK LOOP end-to-end: HEALTHY -> (misses) UNREACHABLE -> 10// PAIRING -> REPROVISION -> READOPTED -> HEALTHY, with recover_count 11// incrementing once per auto-heal (the manual re-pairs spared) and 12// brick-safety asserted at every emitted action 13// - a SECOND cycle increments the measured count again 14// - self-recovery (UNREACHABLE -> SEEN) revives WITHOUT counting as a 15// re-provision (honest: it came back on its own) 16// - defensive: malformed observation is a no-op; null does not crash 17// 18// expect_exit: 0 19// 20// license_tier: ORIGINAL 21 22import "nx_syscalls_x86_64.nx" 23import "nx_iot_watchdog.nx" 24 25func main() -> i64 { 26 // ---- sealed-enum validity -------------------------------------- 27 if nx_iot_wd_state_is_valid(WD_ST_HEALTHY) != 1 { return 1 } 28 if nx_iot_wd_state_is_valid(WD_ST_RECOVERING) != 1 { return 2 } 29 if nx_iot_wd_state_is_valid(WD_ST_N) != 0 { return 3 } 30 if nx_iot_wd_state_is_valid(-1) != 0 { return 4 } 31 if nx_iot_wd_obs_is_valid(WD_OBS_PAIRING) != 1 { return 5 } 32 if nx_iot_wd_obs_is_valid(WD_OBS_N) != 0 { return 6 } 33 if nx_iot_wd_action_is_valid(WD_ACT_REPROVISION) != 1 { return 7 } 34 if nx_iot_wd_action_is_valid(WD_ACT_N) != 0 { return 8 } 35 36 // ---- NEVER-BRICK: whole action alphabet ------------------------ 37 var a: i64 = 0 38 while a < WD_ACT_N { 39 if nx_iot_wd_action_is_brick_safe(a) != 1 { return 10 } 40 if nx_iot_wd_action_writes_firmware(a) != 0 { return 11 } 41 a = a + 1 42 } 43 if nx_iot_wd_action_writes_firmware(WD_ACT_N) != 1 { return 12 } // fail-safe RED 44 if nx_iot_wd_action_is_brick_safe(WD_ACT_N) != 0 { return 13 } 45 if nx_iot_wd_action_is_brick_safe(-1) != 0 { return 14 } 46 47 // ---- init ------------------------------------------------------ 48 let w: *IotWatchdog = sys_mmap(64) as *IotWatchdog 49 if nx_iot_wd_init(w, 7, 0, 100) != 0 { return 20 } 50 if nx_iot_wd_state(w) != WD_ST_HEALTHY { return 21 } 51 if nx_iot_wd_logical_id(w) != 7 { return 22 } 52 if nx_iot_wd_consec_misses(w) != 0 { return 23 } 53 if nx_iot_wd_recover_count(w) != 0 { return 24 } 54 if nx_iot_wd_tick_count(w) != 0 { return 25 } 55 if w.miss_threshold != NX_IOT_WD_MISS_THRESHOLD { return 26 } 56 if nx_iot_wd_init(w, 7, 5, 100) != 0 { return 27 } 57 if w.miss_threshold != 5 { return 28 } 58 if nx_iot_wd_init(0 as *IotWatchdog, 7, 0, 0) != -1 { return 29 } 59 60 // ---- HEALTHY + SEEN stays healthy; last_seen updates ----------- 61 if nx_iot_wd_init(w, 1, 3, 100) != 0 { return 30 } 62 if nx_iot_wd_observe(w, WD_OBS_SEEN, 200) != WD_ACT_NONE { return 31 } 63 if nx_iot_wd_state(w) != WD_ST_HEALTHY { return 32 } 64 if w.last_seen_ms != 200 { return 33 } 65 if nx_iot_wd_tick_count(w) != 1 { return 34 } 66 67 // ---- HEALTHY + MISS -> MISSED (probe) -------------------------- 68 if nx_iot_wd_observe(w, WD_OBS_MISS, 300) != WD_ACT_PROBE { return 35 } 69 if nx_iot_wd_state(w) != WD_ST_MISSED { return 36 } 70 if nx_iot_wd_consec_misses(w) != 1 { return 37 } 71 72 // ---- MISSED + SEEN -> HEALTHY (transient recovered) ------------ 73 if nx_iot_wd_observe(w, WD_OBS_SEEN, 400) != WD_ACT_NONE { return 38 } 74 if nx_iot_wd_state(w) != WD_ST_HEALTHY { return 39 } 75 if nx_iot_wd_consec_misses(w) != 0 { return 40 } 76 77 // ---- accumulate misses to the threshold -> UNREACHABLE --------- 78 if nx_iot_wd_init(w, 1, 3, 100) != 0 { return 50 } 79 if nx_iot_wd_observe(w, WD_OBS_MISS, 110) != WD_ACT_PROBE { return 51 } // miss 1 80 if nx_iot_wd_observe(w, WD_OBS_MISS, 120) != WD_ACT_PROBE { return 52 } // miss 2 81 if nx_iot_wd_consec_misses(w) != 2 { return 53 } 82 let m3: i64 = nx_iot_wd_observe(w, WD_OBS_MISS, 130) // miss 3 == threshold 83 if m3 != WD_ACT_MARK_UNREACHABLE { return 54 } 84 if nx_iot_wd_state(w) != WD_ST_UNREACHABLE { return 55 } 85 if nx_iot_wd_is_down(w) != 1 { return 56 } 86 if nx_iot_wd_action_is_brick_safe(m3) != 1 { return 57 } 87 88 // ---- THE FULL BLINK LOOP (end to end, the headline) ------------ 89 // HEALTHY -> 3 misses -> UNREACHABLE -> PAIRING(blink) -> 90 // REPROVISION -> READOPTED -> HEALTHY, recover_count = 1. 91 if nx_iot_wd_init(w, 42, 3, 1000) != 0 { return 60 } 92 nx_iot_wd_observe(w, WD_OBS_MISS, 1010) 93 nx_iot_wd_observe(w, WD_OBS_MISS, 1020) 94 let b3: i64 = nx_iot_wd_observe(w, WD_OBS_MISS, 1030) 95 if b3 != WD_ACT_MARK_UNREACHABLE { return 61 } 96 if nx_iot_wd_state(w) != WD_ST_UNREACHABLE { return 62 } 97 // the blink: the fixture reverted to open pairing mode 98 let bp: i64 = nx_iot_wd_observe(w, WD_OBS_PAIRING, 1040) 99 if bp != WD_ACT_REPROVISION { return 63 } 100 if nx_iot_wd_state(w) != WD_ST_RECOVERING { return 64 } 101 if nx_iot_wd_action_is_brick_safe(bp) != 1 { return 65 } 102 // provision_softap re-provisioned + the anchor re-adopted it 103 let br: i64 = nx_iot_wd_observe(w, WD_OBS_READOPTED, 1050) 104 if br != WD_ACT_MARK_CURRENT { return 66 } 105 if nx_iot_wd_state(w) != WD_ST_HEALTHY { return 67 } 106 if nx_iot_wd_is_healthy(w) != 1 { return 68 } 107 if nx_iot_wd_recover_count(w) != 1 { return 69 } // ONE manual re-pair spared 108 if w.last_seen_ms != 1050 { return 70 } 109 110 // ---- a SECOND blink cycle increments the measured count -------- 111 nx_iot_wd_observe(w, WD_OBS_MISS, 2010) 112 nx_iot_wd_observe(w, WD_OBS_MISS, 2020) 113 nx_iot_wd_observe(w, WD_OBS_MISS, 2030) // -> UNREACHABLE 114 nx_iot_wd_observe(w, WD_OBS_PAIRING, 2040) // -> RECOVERING 115 let c2: i64 = nx_iot_wd_observe(w, WD_OBS_SEEN, 2050) // back on LAN -> HEALED 116 if c2 != WD_ACT_MARK_CURRENT { return 71 } 117 if nx_iot_wd_state(w) != WD_ST_HEALTHY { return 72 } 118 if nx_iot_wd_recover_count(w) != 2 { return 73 } // measured exceed grows 119 120 // ---- self-recovery does NOT count as a re-provision ------------ 121 // UNREACHABLE -> SEEN (came back on its own): revive, but it was not 122 // a manual-re-pair scenario, so recover_count is unchanged. 123 if nx_iot_wd_init(w, 9, 2, 100) != 0 { return 80 } 124 nx_iot_wd_observe(w, WD_OBS_MISS, 110) 125 nx_iot_wd_observe(w, WD_OBS_MISS, 120) // threshold 2 -> UNREACHABLE 126 if nx_iot_wd_state(w) != WD_ST_UNREACHABLE { return 81 } 127 let s1: i64 = nx_iot_wd_observe(w, WD_OBS_SEEN, 130) 128 if s1 != WD_ACT_MARK_CURRENT { return 82 } 129 if nx_iot_wd_state(w) != WD_ST_HEALTHY { return 83 } 130 if nx_iot_wd_recover_count(w) != 0 { return 84 } // NOT counted -- honest 131 132 // ---- HEALTHY -> PAIRING directly (sudden revert) --------------- 133 if nx_iot_wd_init(w, 9, 3, 100) != 0 { return 90 } 134 let h1: i64 = nx_iot_wd_observe(w, WD_OBS_PAIRING, 110) 135 if h1 != WD_ACT_REPROVISION { return 91 } 136 if nx_iot_wd_state(w) != WD_ST_RECOVERING { return 92 } 137 138 // ---- defensive: malformed obs no-op; null does not crash ------- 139 if nx_iot_wd_init(w, 9, 3, 100) != 0 { return 95 } 140 let d1: i64 = nx_iot_wd_observe(w, 999, 110) 141 if d1 != WD_ACT_NONE { return 96 } 142 if nx_iot_wd_state(w) != WD_ST_HEALTHY { return 97 } 143 if nx_iot_wd_tick_count(w) != 0 { return 98 } // invalid obs not counted 144 if nx_iot_wd_observe(0 as *IotWatchdog, WD_OBS_SEEN, 0) != WD_ACT_NONE { return 99 } 145 146 return 0 147}