1+ const std = @import ("std" );
2+ const testing = std .testing ;
3+
4+ // ============================================================================
5+ // C bindings for schema types
6+ // ============================================================================
7+
8+ const c = @cImport ({
9+ @cInclude ("colyseus/schema/types.h" );
10+ @cInclude ("colyseus/schema/decode.h" );
11+ });
12+
13+ // ============================================================================
14+ // Test helpers
15+ // ============================================================================
16+
17+ fn expectEqualStrings (expected : []const u8 , actual : [* c ]const u8 ) ! void {
18+ if (actual == null ) {
19+ return error .NullPointer ;
20+ }
21+ const actual_slice = std .mem .span (actual );
22+ try testing .expectEqualStrings (expected , actual_slice );
23+ }
24+
25+ // ============================================================================
26+ // Decode primitive tests
27+ // ============================================================================
28+
29+ test "decode_uint8" {
30+ const bytes = [_ ]u8 {0x42 };
31+ var it = c.colyseus_iterator_t { .offset = 0 };
32+
33+ const result = c .colyseus_decode_uint8 (& bytes , & it );
34+
35+ try testing .expectEqual (@as (u8 , 0x42 ), result );
36+ try testing .expectEqual (@as (c_int , 1 ), it .offset );
37+ }
38+
39+ test "decode_int8_positive" {
40+ const bytes = [_ ]u8 {0x7F };
41+ var it = c.colyseus_iterator_t { .offset = 0 };
42+
43+ const result = c .colyseus_decode_int8 (& bytes , & it );
44+
45+ try testing .expectEqual (@as (i8 , 127 ), result );
46+ }
47+
48+ test "decode_int8_negative" {
49+ const bytes = [_ ]u8 {0xFF };
50+ var it = c.colyseus_iterator_t { .offset = 0 };
51+
52+ const result = c .colyseus_decode_int8 (& bytes , & it );
53+
54+ try testing .expectEqual (@as (i8 , -1 ), result );
55+ }
56+
57+ test "decode_uint16" {
58+ // Little-endian: 0x0102 = 258
59+ const bytes = [_ ]u8 { 0x02 , 0x01 };
60+ var it = c.colyseus_iterator_t { .offset = 0 };
61+
62+ const result = c .colyseus_decode_uint16 (& bytes , & it );
63+
64+ try testing .expectEqual (@as (u16 , 258 ), result );
65+ try testing .expectEqual (@as (c_int , 2 ), it .offset );
66+ }
67+
68+ test "decode_int16" {
69+ // Little-endian: -1 = 0xFFFF
70+ const bytes = [_ ]u8 { 0xFF , 0xFF };
71+ var it = c.colyseus_iterator_t { .offset = 0 };
72+
73+ const result = c .colyseus_decode_int16 (& bytes , & it );
74+
75+ try testing .expectEqual (@as (i16 , -1 ), result );
76+ }
77+
78+ test "decode_uint32" {
79+ // Little-endian: 0x01020304 = 16909060
80+ const bytes = [_ ]u8 { 0x04 , 0x03 , 0x02 , 0x01 };
81+ var it = c.colyseus_iterator_t { .offset = 0 };
82+
83+ const result = c .colyseus_decode_uint32 (& bytes , & it );
84+
85+ try testing .expectEqual (@as (u32 , 16909060 ), result );
86+ try testing .expectEqual (@as (c_int , 4 ), it .offset );
87+ }
88+
89+ test "decode_int32" {
90+ // Little-endian: -1
91+ const bytes = [_ ]u8 { 0xFF , 0xFF , 0xFF , 0xFF };
92+ var it = c.colyseus_iterator_t { .offset = 0 };
93+
94+ const result = c .colyseus_decode_int32 (& bytes , & it );
95+
96+ try testing .expectEqual (@as (i32 , -1 ), result );
97+ }
98+
99+ test "decode_float32" {
100+ // IEEE 754: 1.0f = 0x3F800000
101+ const bytes = [_ ]u8 { 0x00 , 0x00 , 0x80 , 0x3F };
102+ var it = c.colyseus_iterator_t { .offset = 0 };
103+
104+ const result = c .colyseus_decode_float32 (& bytes , & it );
105+
106+ try testing .expectApproxEqAbs (@as (f32 , 1.0 ), result , 0.0001 );
107+ }
108+
109+ test "decode_float64" {
110+ // IEEE 754: 1.0 = 0x3FF0000000000000
111+ const bytes = [_ ]u8 { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0xF0 , 0x3F };
112+ var it = c.colyseus_iterator_t { .offset = 0 };
113+
114+ const result = c .colyseus_decode_float64 (& bytes , & it );
115+
116+ try testing .expectApproxEqAbs (@as (f64 , 1.0 ), result , 0.0001 );
117+ }
118+
119+ test "decode_boolean_true" {
120+ const bytes = [_ ]u8 {0x01 };
121+ var it = c.colyseus_iterator_t { .offset = 0 };
122+
123+ const result = c .colyseus_decode_boolean (& bytes , & it );
124+
125+ try testing .expect (result );
126+ }
127+
128+ test "decode_boolean_false" {
129+ const bytes = [_ ]u8 {0x00 };
130+ var it = c.colyseus_iterator_t { .offset = 0 };
131+
132+ const result = c .colyseus_decode_boolean (& bytes , & it );
133+
134+ try testing .expect (! result );
135+ }
136+
137+ // ============================================================================
138+ // Decode number (msgpack format) tests
139+ // ============================================================================
140+
141+ test "decode_number_positive_fixint" {
142+ // Positive fixint: 0x00-0x7F
143+ const bytes = [_ ]u8 {0x2A }; // 42
144+ var it = c.colyseus_iterator_t { .offset = 0 };
145+
146+ const result = c .colyseus_decode_number (& bytes , & it );
147+
148+ try testing .expectApproxEqAbs (@as (f32 , 42.0 ), result , 0.0001 );
149+ }
150+
151+ test "decode_number_negative_fixint" {
152+ // Negative fixint: 0xE0-0xFF (-1 to -32)
153+ const bytes = [_ ]u8 {0xFF }; // -1
154+ var it = c.colyseus_iterator_t { .offset = 0 };
155+
156+ const result = c .colyseus_decode_number (& bytes , & it );
157+
158+ try testing .expectApproxEqAbs (@as (f32 , -1.0 ), result , 0.0001 );
159+ }
160+
161+ test "decode_number_uint8" {
162+ // 0xCC prefix + uint8
163+ const bytes = [_ ]u8 { 0xCC , 0xFF }; // 255
164+ var it = c.colyseus_iterator_t { .offset = 0 };
165+
166+ const result = c .colyseus_decode_number (& bytes , & it );
167+
168+ try testing .expectApproxEqAbs (@as (f32 , 255.0 ), result , 0.0001 );
169+ }
170+
171+ test "decode_number_uint16" {
172+ // 0xCD prefix + uint16 LE
173+ const bytes = [_ ]u8 { 0xCD , 0x00 , 0x01 }; // 256
174+ var it = c.colyseus_iterator_t { .offset = 0 };
175+
176+ const result = c .colyseus_decode_number (& bytes , & it );
177+
178+ try testing .expectApproxEqAbs (@as (f32 , 256.0 ), result , 0.0001 );
179+ }
180+
181+ test "decode_number_float32" {
182+ // 0xCA prefix + float32 LE (1.5)
183+ const bytes = [_ ]u8 { 0xCA , 0x00 , 0x00 , 0xC0 , 0x3F };
184+ var it = c.colyseus_iterator_t { .offset = 0 };
185+
186+ const result = c .colyseus_decode_number (& bytes , & it );
187+
188+ try testing .expectApproxEqAbs (@as (f32 , 1.5 ), result , 0.0001 );
189+ }
190+
191+ // ============================================================================
192+ // Decode string tests
193+ // ============================================================================
194+
195+ test "decode_string_fixstr" {
196+ // fixstr: 0xA0-0xBF, length in low 5 bits
197+ // 0xA5 = 0xA0 | 5, followed by "hello"
198+ const bytes = [_ ]u8 { 0xA5 , 'h' , 'e' , 'l' , 'l' , 'o' };
199+ var it = c.colyseus_iterator_t { .offset = 0 };
200+
201+ const result = c .colyseus_decode_string (& bytes , & it );
202+ defer std .c .free (result );
203+
204+ try testing .expect (result != null );
205+ try expectEqualStrings ("hello" , result );
206+ try testing .expectEqual (@as (c_int , 6 ), it .offset );
207+ }
208+
209+ test "decode_string_str8" {
210+ // str8: 0xD9 prefix + 1-byte length
211+ const bytes = [_ ]u8 { 0xD9 , 0x05 , 'w' , 'o' , 'r' , 'l' , 'd' };
212+ var it = c.colyseus_iterator_t { .offset = 0 };
213+
214+ const result = c .colyseus_decode_string (& bytes , & it );
215+ defer std .c .free (result );
216+
217+ try testing .expect (result != null );
218+ try expectEqualStrings ("world" , result );
219+ }
220+
221+ test "decode_string_empty" {
222+ // fixstr with length 0
223+ const bytes = [_ ]u8 {0xA0 };
224+ var it = c.colyseus_iterator_t { .offset = 0 };
225+
226+ const result = c .colyseus_decode_string (& bytes , & it );
227+ defer std .c .free (result );
228+
229+ try testing .expect (result != null );
230+ try expectEqualStrings ("" , result );
231+ }
232+
233+ // ============================================================================
234+ // Iterator offset tests
235+ // ============================================================================
236+
237+ test "iterator_multiple_decodes" {
238+ // Decode multiple values sequentially
239+ const bytes = [_ ]u8 {
240+ 0x01 , // uint8: 1
241+ 0x02 , // uint8: 2
242+ 0x03 , // uint8: 3
243+ };
244+ var it = c.colyseus_iterator_t { .offset = 0 };
245+
246+ const v1 = c .colyseus_decode_uint8 (& bytes , & it );
247+ try testing .expectEqual (@as (u8 , 1 ), v1 );
248+ try testing .expectEqual (@as (c_int , 1 ), it .offset );
249+
250+ const v2 = c .colyseus_decode_uint8 (& bytes , & it );
251+ try testing .expectEqual (@as (u8 , 2 ), v2 );
252+ try testing .expectEqual (@as (c_int , 2 ), it .offset );
253+
254+ const v3 = c .colyseus_decode_uint8 (& bytes , & it );
255+ try testing .expectEqual (@as (u8 , 3 ), v3 );
256+ try testing .expectEqual (@as (c_int , 3 ), it .offset );
257+ }
258+
259+ test "iterator_mixed_types" {
260+ // uint8 + uint16 LE + uint8
261+ const bytes = [_ ]u8 {
262+ 0xFF , // uint8: 255
263+ 0x34 , 0x12 , // uint16 LE: 0x1234 = 4660
264+ 0xAB , // uint8: 171
265+ };
266+ var it = c.colyseus_iterator_t { .offset = 0 };
267+
268+ const v1 = c .colyseus_decode_uint8 (& bytes , & it );
269+ try testing .expectEqual (@as (u8 , 255 ), v1 );
270+
271+ const v2 = c .colyseus_decode_uint16 (& bytes , & it );
272+ try testing .expectEqual (@as (u16 , 0x1234 ), v2 );
273+
274+ const v3 = c .colyseus_decode_uint8 (& bytes , & it );
275+ try testing .expectEqual (@as (u8 , 0xAB ), v3 );
276+
277+ try testing .expectEqual (@as (c_int , 4 ), it .offset );
278+ }
279+
280+ // ============================================================================
281+ // Switch check test
282+ // ============================================================================
283+
284+ test "decode_switch_check" {
285+ const bytes_with_switch = [_ ]u8 { 0xFF , 0x00 };
286+ var it1 = c.colyseus_iterator_t { .offset = 0 };
287+ try testing .expect (c .colyseus_decode_switch_check (& bytes_with_switch , & it1 ));
288+
289+ const bytes_without_switch = [_ ]u8 { 0xFE , 0x00 };
290+ var it2 = c.colyseus_iterator_t { .offset = 0 };
291+ try testing .expect (! c .colyseus_decode_switch_check (& bytes_without_switch , & it2 ));
292+ }
293+
294+ // ============================================================================
295+ // Operation code tests
296+ // ============================================================================
297+
298+ test "operation_codes_defined" {
299+ try testing .expectEqual (@as (c_int , 128 ), c .COLYSEUS_OP_ADD );
300+ try testing .expectEqual (@as (c_int , 0 ), c .COLYSEUS_OP_REPLACE );
301+ try testing .expectEqual (@as (c_int , 64 ), c .COLYSEUS_OP_DELETE );
302+ try testing .expectEqual (@as (c_int , 192 ), c .COLYSEUS_OP_DELETE_AND_ADD );
303+ try testing .expectEqual (@as (c_int , 10 ), c .COLYSEUS_OP_CLEAR );
304+ }
305+
306+ test "special_bytes_defined" {
307+ try testing .expectEqual (@as (c_int , 255 ), c .COLYSEUS_SPEC_SWITCH_TO_STRUCTURE );
308+ try testing .expectEqual (@as (c_int , 213 ), c .COLYSEUS_SPEC_TYPE_ID );
309+ }
310+
311+ // ============================================================================
312+ // Field type enum tests
313+ // ============================================================================
314+
315+ test "field_types_defined" {
316+ try testing .expectEqual (@as (c_uint , 0 ), c .COLYSEUS_FIELD_STRING );
317+ try testing .expectEqual (@as (c_uint , 1 ), c .COLYSEUS_FIELD_NUMBER );
318+ try testing .expectEqual (@as (c_uint , 2 ), c .COLYSEUS_FIELD_BOOLEAN );
319+ try testing .expectEqual (@as (c_uint , 13 ), c .COLYSEUS_FIELD_REF );
320+ try testing .expectEqual (@as (c_uint , 14 ), c .COLYSEUS_FIELD_ARRAY );
321+ try testing .expectEqual (@as (c_uint , 15 ), c .COLYSEUS_FIELD_MAP );
322+ }
323+
324+ // ============================================================================
325+ // Number check test
326+ // ============================================================================
327+
328+ test "decode_number_check" {
329+ // Positive fixint should be a number
330+ const bytes_fixint = [_ ]u8 {0x42 };
331+ var it1 = c.colyseus_iterator_t { .offset = 0 };
332+ try testing .expect (c .colyseus_decode_number_check (& bytes_fixint , & it1 ));
333+
334+ // Float32 prefix should be a number
335+ const bytes_float = [_ ]u8 {0xCA };
336+ var it2 = c.colyseus_iterator_t { .offset = 0 };
337+ try testing .expect (c .colyseus_decode_number_check (& bytes_float , & it2 ));
338+
339+ // String prefix should not be a number
340+ const bytes_string = [_ ]u8 {0xA5 };
341+ var it3 = c.colyseus_iterator_t { .offset = 0 };
342+ try testing .expect (! c .colyseus_decode_number_check (& bytes_string , & it3 ));
343+ }
0 commit comments