code wiki / (root) / nx_iot_clock_master_test.nx

nx_iot_clock_master_test.nx source

↩ module page · 217 lines · 9491 B

1// nx_iot_clock_master_test.nx -- end-to-end smoke for the clock- 2// master atomic-scene scheduler. 3// 4// S-class invariants exercised: 5// S0 -- determinism: same input produces same drained order 6// S3 -- atomicity: drain_due returns ALL entries due in one call 7// S7 -- sealed-enum verdicts: every operation reports a verdict 8// 9// Coverage: 10// - tick verdict validity gate (full sealed enum) 11// - init writes a coherent state 12// - tick_advance: no-advance when wall-ms hasn't moved 13// - tick_advance: single-tick on exactly period_ms elapsed 14// - tick_advance: multi-tick on multiple periods elapsed 15// - schedule: into empty queue lands at pos 0 16// - schedule: sorted-by-target_tick insertion preserves order 17// - schedule: FIFO within equal target_tick 18// - schedule: overflow when queue saturated 19// - drain_due: nothing when no entries due 20// - drain_due: all-or-nothing atomic dispatch in target-tick order 21// - drain_due: surviving entries shift left correctly 22// - cancel: removes a specific entry by id 23// - cancel: not-found verdict on miss 24// - peek_next: empty queue returns -1 + NOT_FOUND 25// - peek_next: populated queue returns head target_tick 26// 27// expect_exit: 0 28// 29// license_tier: ORIGINAL 30 31import "nx_syscalls_x86_64.nx" 32import "nx_iot_clock_master.nx" 33 34func main() -> i64 { 35 // ---- Verdict validity gate ------------------------------------- 36 if nx_iot_tick_verdict_is_valid(NX_IOT_TICK_VERDICT_UNKNOWN) != 1 { return 1 } 37 if nx_iot_tick_verdict_is_valid(NX_IOT_TICK_VERDICT_OK) != 1 { return 2 } 38 if nx_iot_tick_verdict_is_valid(NX_IOT_TICK_VERDICT_NO_ADVANCE) != 1 { return 3 } 39 if nx_iot_tick_verdict_is_valid(NX_IOT_TICK_VERDICT_OVERFLOW) != 1 { return 4 } 40 if nx_iot_tick_verdict_is_valid(NX_IOT_TICK_VERDICT_NOT_FOUND) != 1 { return 5 } 41 if nx_iot_tick_verdict_is_valid(NX_IOT_TICK_VERDICT_BAD_ARG) != 1 { return 6 } 42 if nx_iot_tick_verdict_is_valid(NX_IOT_TICK_VERDICT_N) != 0 { return 7 } 43 if nx_iot_tick_verdict_is_valid(-1) != 0 { return 8 } 44 45 // ---- min helper ------------------------------------------------- 46 if nx_iot_tick_min(3, 5) != 3 { return 10 } 47 if nx_iot_tick_min(5, 3) != 3 { return 11 } 48 if nx_iot_tick_min(7, 7) != 7 { return 12 } 49 50 // ---- Init writes a coherent state ------------------------------ 51 let st: *IotClockState = sys_mmap(64) as *IotClockState 52 nx_iot_clock_init(st, 32, 100000) 53 if st.tick_n != 0 { return 20 } 54 if st.anchor_ms != 100000 { return 21 } 55 if st.period_ms != NX_IOT_TICK_PERIOD_MS { return 22 } 56 if st.queue_count != 0 { return 23 } 57 if st.queue_cap != 32 { return 24 } 58 59 let qv: *i64 = sys_mmap(8) as *i64 60 61 // ---- Tick advance: no-advance when wall-ms hasn't moved -------- 62 let na: i64 = nx_iot_clock_tick(st, 100000, qv) 63 if na != 0 { return 30 } 64 if qv[0] != NX_IOT_TICK_VERDICT_NO_ADVANCE { return 31 } 65 if st.tick_n != 0 { return 32 } 66 67 // ---- Tick advance: just under one period -> no-advance -------- 68 let na2: i64 = nx_iot_clock_tick(st, 100000 + NX_IOT_TICK_PERIOD_MS - 1, qv) 69 if na2 != 0 { return 33 } 70 if qv[0] != NX_IOT_TICK_VERDICT_NO_ADVANCE { return 34 } 71 if st.tick_n != 0 { return 35 } 72 73 // ---- Tick advance: exactly one period -> 1 tick ---------------- 74 let na3: i64 = nx_iot_clock_tick(st, 100000 + NX_IOT_TICK_PERIOD_MS, qv) 75 if na3 != 1 { return 36 } 76 if qv[0] != NX_IOT_TICK_VERDICT_OK { return 37 } 77 if st.tick_n != 1 { return 38 } 78 if st.anchor_ms != 100000 + NX_IOT_TICK_PERIOD_MS { return 39 } 79 80 // ---- Tick advance: 5 periods elapsed -> 5 ticks at once ------- 81 let na4: i64 = nx_iot_clock_tick(st, st.anchor_ms + 5 * NX_IOT_TICK_PERIOD_MS, qv) 82 if na4 != 5 { return 40 } 83 if st.tick_n != 6 { return 41 } 84 85 // ---- Schedule + drain workflow --------------------------------- 86 let st2: *IotClockState = sys_mmap(64) as *IotClockState 87 nx_iot_clock_init(st2, 16, 0) 88 let q: *IotClockEntry = sys_mmap(16 * 24) as *IotClockEntry 89 90 // Schedule one entry at tick 10 91 let pos0: i64 = nx_iot_clock_schedule(st2, q, 10, 1001, 0xAAAA, qv) 92 if pos0 != 0 { return 50 } 93 if qv[0] != NX_IOT_TICK_VERDICT_OK { return 51 } 94 if st2.queue_count != 1 { return 52 } 95 96 // Schedule another at tick 5 -- should go to pos 0 (sorted) 97 let pos1: i64 = nx_iot_clock_schedule(st2, q, 5, 1002, 0xBBBB, qv) 98 if pos1 != 0 { return 53 } 99 if st2.queue_count != 2 { return 54 } 100 101 // Head should be the tick-5 entry 102 let head: *IotClockEntry = q 103 if head.target_tick != 5 { return 55 } 104 if head.entry_id != 1002 { return 56 } 105 let slot1_addr: i64 = (q as i64) + 24 106 let slot1: *IotClockEntry = slot1_addr as *IotClockEntry 107 if slot1.target_tick != 10 { return 57 } 108 if slot1.entry_id != 1001 { return 58 } 109 110 // Schedule tick-7 -- should land between (pos 1) 111 let pos2: i64 = nx_iot_clock_schedule(st2, q, 7, 1003, 0xCCCC, qv) 112 if pos2 != 1 { return 59 } 113 if st2.queue_count != 3 { return 60 } 114 let slot1b_addr: i64 = (q as i64) + 24 115 let slot1b: *IotClockEntry = slot1b_addr as *IotClockEntry 116 if slot1b.target_tick != 7 { return 61 } 117 if slot1b.entry_id != 1003 { return 62 } 118 119 // FIFO at equal target_tick: schedule another at tick 7 -- should 120 // go AFTER the first tick-7 entry (insertion finds first slot with 121 // target_tick > 7, which is the tick-10 slot at pos 2; new entry 122 // lands at pos 2) 123 let pos3: i64 = nx_iot_clock_schedule(st2, q, 7, 1004, 0xDDDD, qv) 124 if pos3 != 2 { return 63 } 125 if st2.queue_count != 4 { return 64 } 126 127 // Order should now be: [5/1002, 7/1003, 7/1004, 10/1001] 128 let s0: *IotClockEntry = q 129 if s0.entry_id != 1002 { return 65 } 130 let s1_addr: i64 = (q as i64) + 24 131 let s1: *IotClockEntry = s1_addr as *IotClockEntry 132 if s1.entry_id != 1003 { return 66 } 133 let s2_addr: i64 = (q as i64) + 48 134 let s2: *IotClockEntry = s2_addr as *IotClockEntry 135 if s2.entry_id != 1004 { return 67 } 136 let s3_addr: i64 = (q as i64) + 72 137 let s3: *IotClockEntry = s3_addr as *IotClockEntry 138 if s3.entry_id != 1001 { return 68 } 139 140 // ---- peek_next returns 5 --------------------------------------- 141 if nx_iot_clock_peek_next(st2, q, qv) != 5 { return 70 } 142 if qv[0] != NX_IOT_TICK_VERDICT_OK { return 71 } 143 144 // ---- drain_due: tick still 0, nothing due ---------------------- 145 let out_buf: *IotClockEntry = sys_mmap(16 * 24) as *IotClockEntry 146 let drained0: i64 = nx_iot_clock_drain_due(st2, q, out_buf, 16, qv) 147 if drained0 != 0 { return 80 } 148 if st2.queue_count != 4 { return 81 } 149 150 // ---- Advance tick to 7; drain should return 3 entries ---------- 151 st2.tick_n = 7 152 let drained1: i64 = nx_iot_clock_drain_due(st2, q, out_buf, 16, qv) 153 if drained1 != 3 { return 90 } 154 if qv[0] != NX_IOT_TICK_VERDICT_OK { return 91 } 155 if st2.queue_count != 1 { return 92 } 156 157 // Drained entries should be [5/1002, 7/1003, 7/1004] 158 let d0: *IotClockEntry = out_buf 159 if d0.entry_id != 1002 { return 93 } 160 let d1_addr: i64 = (out_buf as i64) + 24 161 let d1: *IotClockEntry = d1_addr as *IotClockEntry 162 if d1.entry_id != 1003 { return 94 } 163 let d2_addr: i64 = (out_buf as i64) + 48 164 let d2: *IotClockEntry = d2_addr as *IotClockEntry 165 if d2.entry_id != 1004 { return 95 } 166 167 // Remaining queue head should be the tick-10 entry 168 let remaining_head: *IotClockEntry = q 169 if remaining_head.entry_id != 1001 { return 96 } 170 171 // ---- Cancel: hit by id ----------------------------------------- 172 // Schedule a few more for the cancel test 173 nx_iot_clock_schedule(st2, q, 12, 2001, 0x1111, qv) 174 nx_iot_clock_schedule(st2, q, 15, 2002, 0x2222, qv) 175 nx_iot_clock_schedule(st2, q, 20, 2003, 0x3333, qv) 176 // Queue: [10/1001, 12/2001, 15/2002, 20/2003] 177 if st2.queue_count != 4 { return 100 } 178 179 let cr: i64 = nx_iot_clock_cancel(st2, q, 2002, qv) 180 if cr != 0 { return 101 } 181 if qv[0] != NX_IOT_TICK_VERDICT_OK { return 102 } 182 if st2.queue_count != 3 { return 103 } 183 // Queue should now be: [10/1001, 12/2001, 20/2003] 184 let post_cancel_2_addr: i64 = (q as i64) + 48 185 let post_cancel_2: *IotClockEntry = post_cancel_2_addr as *IotClockEntry 186 if post_cancel_2.entry_id != 2003 { return 104 } 187 188 // ---- Cancel: not found ------------------------------------------ 189 let cr2: i64 = nx_iot_clock_cancel(st2, q, 9999, qv) 190 if cr2 != -1 { return 105 } 191 if qv[0] != NX_IOT_TICK_VERDICT_NOT_FOUND { return 106 } 192 if st2.queue_count != 3 { return 107 } 193 194 // ---- Overflow: schedule until cap (cap=16, we have 3 left, 195 // capacity left = 13) -------------------------------------------- 196 var i: i64 = 0 197 while i < 13 { 198 nx_iot_clock_schedule(st2, q, 100 + i, 3000 + i, 0xEEEE, qv) 199 i = i + 1 200 } 201 if st2.queue_count != 16 { return 110 } 202 // One more should overflow 203 let over: i64 = nx_iot_clock_schedule(st2, q, 200, 9001, 0xFFFF, qv) 204 if over != -1 { return 111 } 205 if qv[0] != NX_IOT_TICK_VERDICT_OVERFLOW { return 112 } 206 if st2.queue_count != 16 { return 113 } 207 208 // ---- peek_next on empty queue ---------------------------------- 209 let st3: *IotClockState = sys_mmap(64) as *IotClockState 210 nx_iot_clock_init(st3, 4, 0) 211 let q3: *IotClockEntry = sys_mmap(4 * 24) as *IotClockEntry 212 let pn: i64 = nx_iot_clock_peek_next(st3, q3, qv) 213 if pn != -1 { return 120 } 214 if qv[0] != NX_IOT_TICK_VERDICT_NOT_FOUND { return 121 } 215 216 return 0 217}