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}