nx_stream_multiplexer_test.nx source
↩ module page · 152 lines · 7980 B
1// nx_stream_multiplexer_test.nx -- smoke for nx_stream_multiplexer.
2
3import "nx_syscalls.nx"
4import "nx_stream_multiplexer.nx"
5
6func main() -> i64 {
7 let now: nx_size = 1000000
8
9 // Out-params for nx_sm_next
10 let out_class_buf: *u8 = sys_mmap(24)
11 let out_class: *i64 = out_class_buf as *i64
12 let out_seq: *i64 = (out_class_buf + 8) as *i64
13 let out_stream: *i64 = (out_class_buf + 16) as *i64
14
15 // 1: enum validity
16 if nx_sm_class_is_valid(NX_SM_CLASS_LLM_TEXT) != 1 { return 1 }
17 if nx_sm_class_is_valid(NX_SM_CLASS_SYSTEM_EVENT) != 1 { return 2 }
18 if nx_sm_class_is_valid(-1) != 0 { return 3 }
19 if nx_sm_class_is_valid(7) != 0 { return 4 }
20 if NX_SM_CLASS_N != 7 { return 5 }
21
22 if nx_sm_v_is_valid(NX_SM_V_DELIVERED) != 1 { return 6 }
23 if nx_sm_v_is_valid(NX_SM_V_STARVED) != 1 { return 7 }
24
25 // 2: priority ordering: SYSTEM > GAME_ACTION > LLM > AUDIO > TOOL > IMAGE > VIDEO
26 if nx_sm_class_priority(NX_SM_CLASS_SYSTEM_EVENT) <= nx_sm_class_priority(NX_SM_CLASS_GAME_ACTION) { return 8 }
27 if nx_sm_class_priority(NX_SM_CLASS_GAME_ACTION) <= nx_sm_class_priority(NX_SM_CLASS_LLM_TEXT) { return 9 }
28 if nx_sm_class_priority(NX_SM_CLASS_LLM_TEXT) <= nx_sm_class_priority(NX_SM_CLASS_AUDIO_TTS) { return 10 }
29 if nx_sm_class_priority(NX_SM_CLASS_AUDIO_TTS) <= nx_sm_class_priority(NX_SM_CLASS_TOOL_RESULT) { return 11 }
30 if nx_sm_class_priority(NX_SM_CLASS_TOOL_RESULT) <= nx_sm_class_priority(NX_SM_CLASS_IMAGE_TILE) { return 12 }
31 if nx_sm_class_priority(NX_SM_CLASS_IMAGE_TILE) <= nx_sm_class_priority(NX_SM_CLASS_VIDEO_FRAME) { return 13 }
32
33 // 3: construction
34 let m: *NxStreamMultiplexer = nx_sm_new(32, 50000) // 50ms starvation threshold
35 if m.capacity != 32 { return 14 }
36 if m.n_pending != 0 { return 15 }
37 if m.starvation_threshold_us != 50000 { return 16 }
38
39 if nx_sm_new(0, 50000) != (0 as *NxStreamMultiplexer) { return 17 }
40
41 // 4: empty next returns EMPTY
42 if nx_sm_next(m, out_class, out_seq, out_stream, now) != NX_SM_V_EMPTY { return 18 }
43
44 // 5: push chunks of varying priority then verify the highest-priority
45 // one comes out first (no starvation/budget effects yet)
46 nx_sm_push(m, 1, NX_SM_CLASS_VIDEO_FRAME, 100, 1024, 0xCA01, now)
47 nx_sm_push(m, 2, NX_SM_CLASS_IMAGE_TILE, 200, 1024, 0xCA02, now)
48 nx_sm_push(m, 3, NX_SM_CLASS_LLM_TEXT, 300, 64, 0xCA03, now)
49 nx_sm_push(m, 4, NX_SM_CLASS_AUDIO_TTS, 400, 32, 0xCA04, now)
50 nx_sm_push(m, 5, NX_SM_CLASS_GAME_ACTION, 500, 16, 0xCA05, now)
51 nx_sm_push(m, 6, NX_SM_CLASS_SYSTEM_EVENT, 600, 8, 0xCA06, now)
52 if m.n_pending != 6 { return 19 }
53 if nx_sm_pending_count(m) != 6 { return 20 }
54
55 // 6: first next should be SYSTEM_EVENT
56 if nx_sm_next(m, out_class, out_seq, out_stream, now) != NX_SM_V_DELIVERED { return 21 }
57 if out_class[0] != (NX_SM_CLASS_SYSTEM_EVENT as i64) { return 22 }
58 if out_seq[0] != 600 { return 23 }
59 if m.n_pending != 5 { return 24 }
60
61 // 7: second next should be GAME_ACTION
62 nx_sm_next(m, out_class, out_seq, out_stream, now)
63 if out_class[0] != (NX_SM_CLASS_GAME_ACTION as i64) { return 25 }
64 if out_seq[0] != 500 { return 26 }
65
66 // 8: third = LLM_TEXT
67 nx_sm_next(m, out_class, out_seq, out_stream, now)
68 if out_class[0] != (NX_SM_CLASS_LLM_TEXT as i64) { return 27 }
69
70 // 9: fourth = AUDIO_TTS
71 nx_sm_next(m, out_class, out_seq, out_stream, now)
72 if out_class[0] != (NX_SM_CLASS_AUDIO_TTS as i64) { return 28 }
73
74 // 10: fifth = IMAGE_TILE
75 nx_sm_next(m, out_class, out_seq, out_stream, now)
76 if out_class[0] != (NX_SM_CLASS_IMAGE_TILE as i64) { return 29 }
77
78 // 11: sixth = VIDEO_FRAME
79 nx_sm_next(m, out_class, out_seq, out_stream, now)
80 if out_class[0] != (NX_SM_CLASS_VIDEO_FRAME as i64) { return 30 }
81
82 // 12: empty again
83 if m.n_pending != 0 { return 31 }
84 if nx_sm_next(m, out_class, out_seq, out_stream, now) != NX_SM_V_EMPTY { return 32 }
85
86 // 13: starvation boost -- push a VIDEO_FRAME at t=0, then LLM_TEXT at t=100ms.
87 // LLM normally wins by priority. But if VIDEO has waited > 50ms (threshold),
88 // it gets +30 score boost, may now beat LLM (30 priority + 30 boost = 60 < 80).
89 // LLM (80) still wins. Let me design a scenario where boost matters:
90 // image_tile (40) + 30 boost = 70 > audio_tts (70 base) -- ties happen.
91 // Cleanest: push 5x AUDIO chunks so AUDIO hits its budget of 16... no, budget is 16.
92 // Test: push old IMAGE_TILE (40) + recent VIDEO_FRAME (30); IMAGE has +30 boost
93 // becomes 70 > VIDEO 30 -> IMAGE wins. Need to verify that without boost
94 // IMAGE still wins anyway (40 > 30). So the boost test is invariant.
95 // Force test: old VIDEO (30+30=60) vs recent IMAGE (40+0=40) -> VIDEO wins.
96 let m2: *NxStreamMultiplexer = nx_sm_new(8, 50000)
97 nx_sm_push(m2, 1, NX_SM_CLASS_VIDEO_FRAME, 100, 1024, 0, now)
98 nx_sm_push(m2, 2, NX_SM_CLASS_IMAGE_TILE, 200, 1024, 0, now + 60000)
99 // Read at t = now + 60000 -- video has waited 60ms > 50ms threshold
100 nx_sm_next(m2, out_class, out_seq, out_stream, now + 60000)
101 if out_class[0] != (NX_SM_CLASS_VIDEO_FRAME as i64) { return 33 }
102
103 // 14: budget exhaustion -- IMAGE_TILE budget is 2 per epoch. Push 3 IMAGEs
104 // before any LLM. IMAGE serves twice then backoff kicks in (priority 40 - 50 = -10).
105 // If we now push 1 LLM_TEXT, LLM should win even though IMAGE is older.
106 let m3: *NxStreamMultiplexer = nx_sm_new(8, 1000000) // huge starvation threshold so no boost
107 nx_sm_push(m3, 1, NX_SM_CLASS_IMAGE_TILE, 101, 1024, 0, now)
108 nx_sm_push(m3, 2, NX_SM_CLASS_IMAGE_TILE, 102, 1024, 0, now)
109 nx_sm_next(m3, out_class, out_seq, out_stream, now) // IMAGE 1
110 nx_sm_next(m3, out_class, out_seq, out_stream, now) // IMAGE 2
111 if nx_sm_served_count(m3, NX_SM_CLASS_IMAGE_TILE) != 2 { return 34 }
112 // Now push another image + an LLM
113 nx_sm_push(m3, 3, NX_SM_CLASS_IMAGE_TILE, 103, 1024, 0, now)
114 nx_sm_push(m3, 4, NX_SM_CLASS_LLM_TEXT, 104, 64, 0, now)
115 // Image budget=2 already used, so IMAGE score = 40-50 = -10. LLM = 80. LLM wins.
116 nx_sm_next(m3, out_class, out_seq, out_stream, now)
117 if out_class[0] != (NX_SM_CLASS_LLM_TEXT as i64) { return 35 }
118
119 // 15: epoch_tick resets served counts
120 if nx_sm_epoch_tick(m3) != NX_SM_V_DELIVERED { return 36 }
121 if nx_sm_served_count(m3, NX_SM_CLASS_IMAGE_TILE) != 0 { return 37 }
122
123 // 16: after reset, IMAGE_TILE wins again over... wait, IMAGE has priority 40 and
124 // remaining queue has IMAGE seq=103. Push nothing else -- IMAGE wins by default.
125 nx_sm_next(m3, out_class, out_seq, out_stream, now)
126 if out_class[0] != (NX_SM_CLASS_IMAGE_TILE as i64) { return 38 }
127
128 // 17: capacity exceeded
129 let m_full: *NxStreamMultiplexer = nx_sm_new(2, 50000)
130 nx_sm_push(m_full, 1, NX_SM_CLASS_LLM_TEXT, 1, 64, 0, now)
131 nx_sm_push(m_full, 2, NX_SM_CLASS_LLM_TEXT, 2, 64, 0, now)
132 if nx_sm_push(m_full, 3, NX_SM_CLASS_LLM_TEXT, 3, 64, 0, now) != NX_SM_V_INVALID { return 39 }
133
134 // 18: invalid class refused
135 let m_v: *NxStreamMultiplexer = nx_sm_new(4, 50000)
136 if nx_sm_push(m_v, 1, 99, 1, 64, 0, now) != NX_SM_V_INVALID { return 40 }
137 if nx_sm_push(m_v, 1, -1, 1, 64, 0, now) != NX_SM_V_INVALID { return 41 }
138
139 // 19: served_count for invalid class returns 0
140 if nx_sm_served_count(m_v, 99) != 0 { return 42 }
141
142 // 20: null handling
143 let null_m: *NxStreamMultiplexer = (0 as i64) as *NxStreamMultiplexer
144 if nx_sm_push(null_m, 1, NX_SM_CLASS_LLM_TEXT, 1, 64, 0, now) != NX_SM_V_NULL { return 43 }
145 if nx_sm_next(null_m, out_class, out_seq, out_stream, now) != NX_SM_V_NULL { return 44 }
146 if nx_sm_epoch_tick(null_m) != NX_SM_V_NULL { return 45 }
147 if nx_sm_pending_count(null_m) != 0 { return 46 }
148 if nx_sm_served_count(null_m, NX_SM_CLASS_LLM_TEXT) != 0 { return 47 }
149 if (nx_sm_peek_next(null_m, now) as i64) != 0 { return 48 }
150
151 return 0
152}