this repo has no description
1open Swim.Types
2open Swim.Codec
3
4let clamp_int32 n = n land 0x7FFFFFFF
5
6let clamp_incarnation inc =
7 incarnation_of_int (clamp_int32 (incarnation_to_int inc))
8
9let normalize_msg msg =
10 match msg with
11 | Ping { seq; sender } -> Ping { seq = clamp_int32 seq; sender }
12 | Ping_req { seq; target; sender } ->
13 Ping_req { seq = clamp_int32 seq; target; sender }
14 | Ack { seq; responder; payload } ->
15 Ack { seq = clamp_int32 seq; responder; payload }
16 | Alive { node; incarnation } ->
17 Alive { node; incarnation = clamp_incarnation incarnation }
18 | Suspect { node; incarnation; suspector } ->
19 Suspect { node; incarnation = clamp_incarnation incarnation; suspector }
20 | Dead { node; incarnation; declarator } ->
21 Dead { node; incarnation = clamp_incarnation incarnation; declarator }
22 | User_msg _ as msg -> msg
23
24let normalize_packet packet =
25 let primary = normalize_msg packet.primary in
26 let piggyback = List.map normalize_msg packet.piggyback in
27 { packet with primary; piggyback }
28
29let test_roundtrip_msg =
30 QCheck.Test.make ~count:1000 ~name:"codec message roundtrip"
31 Generators.arb_protocol_msg (fun msg ->
32 let msg = normalize_msg msg in
33 let size = encoded_size msg + 100 in
34 let buf = Cstruct.create size in
35 let enc = Encoder.create ~buf in
36 encode_msg enc msg;
37 let encoded = Encoder.to_cstruct enc in
38 let dec = Decoder.create encoded in
39 match decode_msg dec with Ok decoded -> decoded = msg | Error _ -> false)
40
41let test_roundtrip_packet =
42 QCheck.Test.make ~count:500 ~name:"codec packet roundtrip"
43 Generators.arb_packet (fun packet ->
44 let packet = normalize_packet packet in
45 let size =
46 4 + 1 + 2
47 + String.length packet.cluster
48 + 2
49 + encoded_size packet.primary
50 + List.fold_left (fun acc m -> acc + encoded_size m) 0 packet.piggyback
51 + 100
52 in
53 let buf = Cstruct.create size in
54 match encode_packet packet ~buf with
55 | Error _ -> false
56 | Ok len -> (
57 let encoded = Cstruct.sub buf 0 len in
58 match decode_packet encoded with
59 | Ok decoded -> decoded = packet
60 | Error _ -> false))
61
62let test_encoded_size_accurate =
63 QCheck.Test.make ~count:1000 ~name:"encoded_size matches actual encoding"
64 Generators.arb_protocol_msg (fun msg ->
65 let predicted = encoded_size msg in
66 let buf = Cstruct.create (predicted + 100) in
67 let enc = Encoder.create ~buf in
68 encode_msg enc msg;
69 let actual = Encoder.pos enc in
70 predicted = actual)
71
72let make_valid_packet_buf () =
73 let node =
74 make_node_info ~id:(node_id_of_string "n1")
75 ~addr:(`Udp (Eio.Net.Ipaddr.of_raw "\127\000\000\001", 7946))
76 ~meta:""
77 in
78 let packet =
79 {
80 cluster = "test";
81 primary = Ping { seq = 1; sender = node };
82 piggyback = [];
83 }
84 in
85 let buf = Cstruct.create 1000 in
86 match encode_packet packet ~buf with
87 | Ok len -> Cstruct.sub buf 0 len
88 | Error _ -> failwith "encode failed"
89
90let test_invalid_magic_rejected () =
91 let buf = make_valid_packet_buf () in
92 Cstruct.blit_from_string "FAIL" 0 buf 0 4;
93 match decode_packet buf with
94 | Error Invalid_magic -> ()
95 | _ -> Alcotest.fail "expected Invalid_magic error"
96
97let test_unsupported_version_rejected () =
98 let buf = make_valid_packet_buf () in
99 Cstruct.set_uint8 buf 4 0x99;
100 match decode_packet buf with
101 | Error (Unsupported_version 0x99) -> ()
102 | Error (Unsupported_version v) ->
103 Alcotest.failf "expected version 0x99 but got %d" v
104 | _ -> Alcotest.fail "expected Unsupported_version error"
105
106let test_invalid_tag_rejected () =
107 let buf = Cstruct.create 100 in
108 let enc = Encoder.create ~buf in
109 Encoder.write_bytes enc (Cstruct.of_string "SWIM");
110 Encoder.write_byte enc 1;
111 Encoder.write_string enc "default";
112 Encoder.write_int16_be enc 1;
113 Encoder.write_byte enc 0xFF;
114 let encoded = Encoder.to_cstruct enc in
115 match decode_packet encoded with
116 | Error (Invalid_tag 0xFF) -> ()
117 | Error (Invalid_tag t) -> Alcotest.failf "expected tag 0xFF but got %d" t
118 | _ -> Alcotest.fail "expected Invalid_tag error"
119
120let test_encoder_write_byte () =
121 let buf = Cstruct.create 10 in
122 let enc = Encoder.create ~buf in
123 Encoder.write_byte enc 0x42;
124 Encoder.write_byte enc 0xFF;
125 let result = Encoder.to_cstruct enc in
126 Alcotest.(check int) "length" 2 (Cstruct.length result);
127 Alcotest.(check int) "byte 0" 0x42 (Cstruct.get_uint8 result 0);
128 Alcotest.(check int) "byte 1" 0xFF (Cstruct.get_uint8 result 1)
129
130let test_encoder_write_int16_be () =
131 let buf = Cstruct.create 10 in
132 let enc = Encoder.create ~buf in
133 Encoder.write_int16_be enc 0x1234;
134 let result = Encoder.to_cstruct enc in
135 Alcotest.(check int) "length" 2 (Cstruct.length result);
136 Alcotest.(check int) "value" 0x1234 (Cstruct.BE.get_uint16 result 0)
137
138let test_encoder_write_int32_be () =
139 let buf = Cstruct.create 10 in
140 let enc = Encoder.create ~buf in
141 Encoder.write_int32_be enc 0x12345678l;
142 let result = Encoder.to_cstruct enc in
143 Alcotest.(check int) "length" 4 (Cstruct.length result);
144 Alcotest.(check int32) "value" 0x12345678l (Cstruct.BE.get_uint32 result 0)
145
146let test_encoder_write_string () =
147 let buf = Cstruct.create 100 in
148 let enc = Encoder.create ~buf in
149 Encoder.write_string enc "hello";
150 let result = Encoder.to_cstruct enc in
151 Alcotest.(check int) "length" 7 (Cstruct.length result);
152 Alcotest.(check int) "str_len" 5 (Cstruct.BE.get_uint16 result 0);
153 Alcotest.(check string)
154 "content" "hello"
155 (Cstruct.to_string ~off:2 ~len:5 result)
156
157let test_encoder_write_empty_string () =
158 let buf = Cstruct.create 10 in
159 let enc = Encoder.create ~buf in
160 Encoder.write_string enc "";
161 let result = Encoder.to_cstruct enc in
162 Alcotest.(check int) "length" 2 (Cstruct.length result);
163 Alcotest.(check int) "str_len" 0 (Cstruct.BE.get_uint16 result 0)
164
165let test_decoder_read_byte () =
166 let buf = Cstruct.of_string "\x42\xFF" in
167 let dec = Decoder.create buf in
168 Alcotest.(check int) "byte 0" 0x42 (Decoder.read_byte dec);
169 Alcotest.(check int) "byte 1" 0xFF (Decoder.read_byte dec)
170
171let test_decoder_read_int16_be () =
172 let buf = Cstruct.create 2 in
173 Cstruct.BE.set_uint16 buf 0 0x1234;
174 let dec = Decoder.create buf in
175 Alcotest.(check int) "value" 0x1234 (Decoder.read_int16_be dec)
176
177let test_decoder_read_int32_be () =
178 let buf = Cstruct.create 4 in
179 Cstruct.BE.set_uint32 buf 0 0x12345678l;
180 let dec = Decoder.create buf in
181 Alcotest.(check int32) "value" 0x12345678l (Decoder.read_int32_be dec)
182
183let test_decoder_read_string () =
184 let buf = Cstruct.create 10 in
185 Cstruct.BE.set_uint16 buf 0 5;
186 Cstruct.blit_from_string "hello" 0 buf 2 5;
187 let dec = Decoder.create buf in
188 Alcotest.(check string) "value" "hello" (Decoder.read_string dec)
189
190let test_decoder_remaining () =
191 let buf = Cstruct.create 10 in
192 let dec = Decoder.create buf in
193 Alcotest.(check int) "initial" 10 (Decoder.remaining dec);
194 let _ = Decoder.read_byte dec in
195 Alcotest.(check int) "after byte" 9 (Decoder.remaining dec);
196 let _ = Decoder.read_int32_be dec in
197 Alcotest.(check int) "after int32" 5 (Decoder.remaining dec)
198
199let test_decoder_is_empty () =
200 let buf = Cstruct.create 1 in
201 let dec = Decoder.create buf in
202 Alcotest.(check bool) "not empty" false (Decoder.is_empty dec);
203 let _ = Decoder.read_byte dec in
204 Alcotest.(check bool) "empty" true (Decoder.is_empty dec)
205
206let test_empty_piggyback () =
207 let node =
208 make_node_info
209 ~id:(node_id_of_string "node1")
210 ~addr:(`Udp (Eio.Net.Ipaddr.of_raw "\127\000\000\001", 7946))
211 ~meta:""
212 in
213 let packet =
214 {
215 cluster = "test";
216 primary = Ping { seq = 1; sender = node };
217 piggyback = [];
218 }
219 in
220 let buf = Cstruct.create 1000 in
221 match encode_packet packet ~buf with
222 | Error _ -> Alcotest.fail "encode failed"
223 | Ok len -> (
224 let encoded = Cstruct.sub buf 0 len in
225 match decode_packet encoded with
226 | Ok decoded ->
227 Alcotest.(check int)
228 "piggyback count" 0
229 (List.length decoded.piggyback)
230 | Error e -> Alcotest.failf "decode failed: %s" (decode_error_to_string e)
231 )
232
233let test_multiple_piggyback () =
234 let node =
235 make_node_info
236 ~id:(node_id_of_string "node1")
237 ~addr:(`Udp (Eio.Net.Ipaddr.of_raw "\127\000\000\001", 7946))
238 ~meta:""
239 in
240 let alive_state : member_state = Alive in
241 let _ = alive_state in
242 let piggyback =
243 [
244 Alive { node; incarnation = incarnation_of_int 1 };
245 Suspect
246 {
247 node = node_id_of_string "node2";
248 incarnation = incarnation_of_int 2;
249 suspector = node_id_of_string "node1";
250 };
251 Dead
252 {
253 node = node_id_of_string "node3";
254 incarnation = incarnation_of_int 3;
255 declarator = node_id_of_string "node1";
256 };
257 ]
258 in
259 let packet =
260 { cluster = "test"; primary = Ping { seq = 1; sender = node }; piggyback }
261 in
262 let buf = Cstruct.create 2000 in
263 match encode_packet packet ~buf with
264 | Error _ -> Alcotest.fail "encode failed"
265 | Ok len -> (
266 let encoded = Cstruct.sub buf 0 len in
267 match decode_packet encoded with
268 | Ok decoded ->
269 Alcotest.(check int)
270 "piggyback count" 3
271 (List.length decoded.piggyback)
272 | Error e -> Alcotest.failf "decode failed: %s" (decode_error_to_string e)
273 )
274
275let qcheck_tests =
276 List.map QCheck_alcotest.to_alcotest
277 [ test_roundtrip_msg; test_roundtrip_packet; test_encoded_size_accurate ]
278
279let unit_tests =
280 [
281 ("invalid_magic_rejected", `Quick, test_invalid_magic_rejected);
282 ("unsupported_version_rejected", `Quick, test_unsupported_version_rejected);
283 ("invalid_tag_rejected", `Quick, test_invalid_tag_rejected);
284 ("encoder_write_byte", `Quick, test_encoder_write_byte);
285 ("encoder_write_int16_be", `Quick, test_encoder_write_int16_be);
286 ("encoder_write_int32_be", `Quick, test_encoder_write_int32_be);
287 ("encoder_write_string", `Quick, test_encoder_write_string);
288 ("encoder_write_empty_string", `Quick, test_encoder_write_empty_string);
289 ("decoder_read_byte", `Quick, test_decoder_read_byte);
290 ("decoder_read_int16_be", `Quick, test_decoder_read_int16_be);
291 ("decoder_read_int32_be", `Quick, test_decoder_read_int32_be);
292 ("decoder_read_string", `Quick, test_decoder_read_string);
293 ("decoder_remaining", `Quick, test_decoder_remaining);
294 ("decoder_is_empty", `Quick, test_decoder_is_empty);
295 ("empty_piggyback", `Quick, test_empty_piggyback);
296 ("multiple_piggyback", `Quick, test_multiple_piggyback);
297 ]
298
299let () =
300 Alcotest.run "codec" [ ("property", qcheck_tests); ("unit", unit_tests) ]