Licklider Transmission Protocol (CCSDS 734.1-B) for reliable DTN links
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Eliminate manual byte-picking across 8 packages

Migrate all remaining binary protocol packages to use Wire codecs
for encode/decode, replacing manual Char.code / set_u8 / get_u16_be
/ put_u32_be byte-picking with Wire.Codec, Wire.map, Wire.Param,
and Wire.optional.

Packages migrated:

- cfdp: Wire codecs for 6 directive types (EOF, Finished, ACK,
Metadata, Prompt, Keep-Alive) with Param.input for large_file
flag and fss_size = 4 + 4*large_file. NAK uses be_bytes_to_int64
for the gap list (Wire.repeat blocked on byte budget from outer
PDU). 41 lines of byte helpers removed.

- sdls: Wire codecs for MC status reply, log/erase/self-test
replies, TLV event encoding. Security header/trailer use
Param.input for iv_len, sn_len, mac_len with byte_array ~size.
85 lines of byte helpers removed.

- ax25: FCS encode/decode via Wire.uint16 (LE). Extension tag
and cancel reason via Wire.map. 14 lines removed.

- proximity1: Staged Wire.Codec.get for frame-type bitfield
extraction, replacing manual bitmask. 4 lines removed.

- ltp: Wire.map for extension_tag and cancel_reason enum fields.
SDNV codec kept (variable-length encoding, not expressible as
Wire type). 3 lines removed.

- pus: Wire codecs for HK parameter, u16be field access. All
PUS secondary header encode/decode through Wire. 7 lines removed.

- mbr: Full 512-byte MBR Wire.Codec with embedded Wire.codec
for partition entries. 18 lines of byte helpers removed.

- pid1: Direct Bytes.of_string to Wire.Codec.decode, eliminating
intermediate buffer copy. 1 line removed.

- uslp: Wire.map helpers (be_uint) for variable-length integer
conversions; fecf_of_string/fecf_to_string cleaned up.

Total: ~170 lines of manual byte-picking eliminated across the
monorepo. Every binary protocol now uses Wire for format parsing.
The remaining Char.code usage is in: SDNV codec (LTP, inherently
variable-length), crypto operations (SDLS CMAC/hex), and value
conversions on Wire-decoded byte arrays.

All tests pass across all packages.

+592 -5
+20 -5
lib/ltp.ml
··· 286 286 | Ok n -> Some n 287 287 | Error _ -> None 288 288 289 + (* {1 Wire types for single-byte fields} *) 290 + 291 + let extension_tag_typ = 292 + Wire.map ~decode:extension_tag_of_int ~encode:extension_tag_to_int Wire.uint8 293 + 294 + let cancel_reason_typ = 295 + Wire.map ~decode:cancel_reason_of_int ~encode:cancel_reason_to_int Wire.uint8 296 + 289 297 (* {1 Encoding} *) 290 298 291 299 let encode_extension ext = 292 - let tag = Char.chr (extension_tag_to_int ext.tag) in 300 + let tag = Wire.encode_to_string extension_tag_typ ext.tag in 293 301 let len_sdnv = encode_sdnv (Int64.of_int (String.length ext.value)) in 294 - String.concat "" [ String.make 1 tag; len_sdnv; ext.value ] 302 + String.concat "" [ tag; len_sdnv; ext.value ] 295 303 296 304 let encode_extensions exts = String.concat "" (List.map encode_extension exts) 297 305 ··· 344 352 | Data ds -> Buffer.add_string buf (encode_data_segment seg.segment_type ds) 345 353 | Report rs -> Buffer.add_string buf (encode_report_segment rs) 346 354 | Report_ack ra -> Buffer.add_string buf (encode_sdnv ra.report_serial) 347 - | Cancel cs -> Buffer.add_char buf (Char.chr (cancel_reason_to_int cs.reason)) 355 + | Cancel cs -> 356 + Buffer.add_string buf (Wire.encode_to_string cancel_reason_typ cs.reason) 348 357 | Cancel_ack () -> ()); 349 358 (* Trailer extensions *) 350 359 Buffer.add_string buf (encode_extensions seg.trailer_extensions); ··· 359 368 if remaining = 0 then Ok (List.rev acc, off) 360 369 else if off >= String.length buf then Error (Extension_error "truncated") 361 370 else 362 - let tag = extension_tag_of_int (Char.code (String.get buf off)) in 371 + let* tag = 372 + Wire.decode_string extension_tag_typ (String.sub buf off 1) 373 + |> Result.map_error (fun _ -> Extension_error "invalid tag") 374 + in 363 375 let* len, off = 364 376 decode_sdnv buf (off + 1) |> Result.map_error (fun _ -> Invalid_sdnv) 365 377 in ··· 498 510 | Cancel_from_sender | Cancel_from_receiver -> 499 511 if off >= len then Error (Content_error "missing cancel reason") 500 512 else 501 - let reason = cancel_reason_of_int (Char.code (String.get buf off)) in 513 + let* reason = 514 + Wire.decode_string cancel_reason_typ (String.sub buf off 1) 515 + |> Result.map_error (fun _ -> Content_error "invalid cancel reason") 516 + in 502 517 Ok (Cancel { reason }, off + 1) 503 518 | Cancel_ack_to_sender | Cancel_ack_to_receiver -> Ok (Cancel_ack (), off) 504 519 in
+19
test/interop/python/dune
··· 1 + (test 2 + (name test) 3 + (libraries ltp csvt alcotest) 4 + (deps 5 + (source_tree traces) 6 + (source_tree scripts))) 7 + 8 + ; Regenerate traces: REGEN_TRACES=1 dune build @regen-traces 9 + 10 + (rule 11 + (alias regen-traces) 12 + (enabled_if 13 + (= %{env:REGEN_TRACES=0} 1)) 14 + (deps 15 + (source_tree scripts)) 16 + (action 17 + (chdir 18 + scripts 19 + (run ./generate.sh))))
+268
test/interop/python/scripts/generate.py
··· 1 + #!/usr/bin/env python3 2 + """Generate LTP interop traces for ocaml-ltp. 3 + 4 + Oracle: Python (no deps needed -- pure RFC implementation) 5 + Install: no dependencies 6 + 7 + Tests SDNV encoding (RFC 6256) and LTP segment encoding (RFC 5326). 8 + 9 + SDNV encoding (RFC 6256): MSB continuation bit (1=more, 0=last), 10 + 7 payload bits per byte, big-endian. 11 + 12 + LTP segment format (RFC 5326 Section 3): 13 + - Control byte: version(4 high bits) | type_code(4 low bits) 14 + - Session ID: originator(SDNV) + session_number(SDNV) 15 + - Extension counts: hdr_count(4 high) | trl_count(4 low) 16 + - Header extensions (if any) 17 + - Content (depends on segment type) 18 + - Trailer extensions (if any) 19 + 20 + Traces are committed to git. Only re-run when changing inputs 21 + or upgrading the oracle. 22 + """ 23 + import os 24 + import sys 25 + import struct 26 + 27 + 28 + # {1 SDNV codec (RFC 6256)} 29 + 30 + def encode_sdnv(n): 31 + """Encode a non-negative integer as an SDNV (RFC 6256).""" 32 + if n == 0: 33 + return b'\x00' 34 + result = [] 35 + while n > 0: 36 + result.append(n & 0x7F) 37 + n >>= 7 38 + result.reverse() 39 + for i in range(len(result) - 1): 40 + result[i] |= 0x80 41 + return bytes(result) 42 + 43 + 44 + def decode_sdnv(buf, off=0): 45 + """Decode an SDNV from buf at offset. Returns (value, new_offset).""" 46 + value = 0 47 + while off < len(buf): 48 + byte = buf[off] 49 + value = (value << 7) | (byte & 0x7F) 50 + off += 1 51 + if byte & 0x80 == 0: 52 + return value, off 53 + raise ValueError("truncated SDNV") 54 + 55 + 56 + # {1 LTP segment builder (RFC 5326)} 57 + 58 + # Segment type codes 59 + TYPE_RED_DATA = 0 60 + TYPE_RED_CHECKPOINT = 1 61 + TYPE_RED_CHECKPOINT_EORP = 2 62 + TYPE_RED_CHECKPOINT_EORP_EOB = 3 63 + TYPE_GREEN_DATA = 4 64 + TYPE_GREEN_EOB = 7 65 + TYPE_REPORT = 8 66 + TYPE_REPORT_ACK = 9 67 + TYPE_CANCEL_FROM_SENDER = 12 68 + TYPE_CANCEL_ACK_TO_SENDER = 13 69 + TYPE_CANCEL_FROM_RECEIVER = 14 70 + TYPE_CANCEL_ACK_TO_RECEIVER = 15 71 + 72 + 73 + def encode_nibbles(high, low): 74 + """Encode two 4-bit values into a single byte.""" 75 + return bytes([(high & 0x0F) << 4 | (low & 0x0F)]) 76 + 77 + 78 + def encode_extension(tag, value): 79 + """Encode a single LTP extension TLV.""" 80 + return bytes([tag]) + encode_sdnv(len(value)) + value 81 + 82 + 83 + def encode_segment(type_code, originator, session_number, 84 + content, header_exts=None, trailer_exts=None): 85 + """Build a complete LTP segment.""" 86 + header_exts = header_exts or [] 87 + trailer_exts = trailer_exts or [] 88 + 89 + buf = bytearray() 90 + # Control byte: version 0 | type_code 91 + buf += encode_nibbles(0, type_code) 92 + # Session ID 93 + buf += encode_sdnv(originator) 94 + buf += encode_sdnv(session_number) 95 + # Extension counts 96 + buf += encode_nibbles(len(header_exts), len(trailer_exts)) 97 + # Header extensions 98 + for tag, val_ in header_exts: 99 + buf += encode_extension(tag, val_) 100 + # Content 101 + buf += content 102 + # Trailer extensions 103 + for tag, val_ in trailer_exts: 104 + buf += encode_extension(tag, val_) 105 + 106 + return bytes(buf) 107 + 108 + 109 + def encode_data_content(client_service_id, block_offset, data, 110 + checkpoint_serial=None, report_serial=None): 111 + """Encode data segment content (RFC 5326 Section 3.2.1).""" 112 + buf = bytearray() 113 + buf += encode_sdnv(client_service_id) 114 + buf += encode_sdnv(block_offset) 115 + buf += encode_sdnv(len(data)) 116 + if checkpoint_serial is not None: 117 + buf += encode_sdnv(checkpoint_serial) 118 + buf += encode_sdnv(report_serial if report_serial is not None else 0) 119 + buf += data 120 + return bytes(buf) 121 + 122 + 123 + def encode_report_content(report_serial, checkpoint_serial, 124 + upper_bound, lower_bound, claims): 125 + """Encode report segment content (RFC 5326 Section 3.2.4).""" 126 + buf = bytearray() 127 + buf += encode_sdnv(report_serial) 128 + buf += encode_sdnv(checkpoint_serial) 129 + buf += encode_sdnv(upper_bound) 130 + buf += encode_sdnv(lower_bound) 131 + buf += encode_sdnv(len(claims)) 132 + for offset, length in claims: 133 + buf += encode_sdnv(offset) 134 + buf += encode_sdnv(length) 135 + return bytes(buf) 136 + 137 + 138 + def encode_report_ack_content(report_serial): 139 + """Encode report ack content.""" 140 + return encode_sdnv(report_serial) 141 + 142 + 143 + def encode_cancel_content(reason_code): 144 + """Encode cancel segment content (single byte reason code).""" 145 + return bytes([reason_code]) 146 + 147 + 148 + # {1 SDNV test vectors} 149 + 150 + sdnv_vectors = [ 151 + ("zero", 0), 152 + ("one", 1), 153 + ("max_single_byte", 127), 154 + ("min_two_byte", 128), 155 + ("byte_255", 255), 156 + ("byte_256", 256), 157 + ("max_two_byte", 16383), 158 + ("min_three_byte", 16384), 159 + ("medium", 1000000), 160 + ("uint32_max", 2**32 - 1), 161 + ("large_40bit", 0x1234567890), 162 + ("int64_max", 2**63 - 1), 163 + ] 164 + 165 + 166 + # {1 LTP segment test vectors} 167 + 168 + def make_segment_vectors(): 169 + vectors = [] 170 + 171 + # Green data segment (type 4) 172 + payload = b"Hello, LTP!" 173 + content = encode_data_content(1, 0, payload) 174 + seg = encode_segment(TYPE_GREEN_DATA, 10, 1000, content) 175 + vectors.append(("green_data_simple", seg)) 176 + 177 + # Green EOB (type 7) 178 + payload = b"\xde\xad\xbe\xef" 179 + content = encode_data_content(2, 512, payload) 180 + seg = encode_segment(TYPE_GREEN_EOB, 42, 99, content) 181 + vectors.append(("green_eob", seg)) 182 + 183 + # Red checkpoint (type 1) 184 + payload = b"checkpoint data" 185 + content = encode_data_content(1, 0, payload, 186 + checkpoint_serial=5, report_serial=0) 187 + seg = encode_segment(TYPE_RED_CHECKPOINT, 1, 100, content) 188 + vectors.append(("red_checkpoint", seg)) 189 + 190 + # Red checkpoint EORP+EOB (type 3) 191 + payload = b"final red" 192 + content = encode_data_content(3, 256, payload, 193 + checkpoint_serial=10, report_serial=2) 194 + seg = encode_segment(TYPE_RED_CHECKPOINT_EORP_EOB, 1, 200, content) 195 + vectors.append(("red_checkpoint_eorp_eob", seg)) 196 + 197 + # Report segment (type 8) with claims 198 + content = encode_report_content( 199 + report_serial=7, 200 + checkpoint_serial=5, 201 + upper_bound=1024, 202 + lower_bound=0, 203 + claims=[(0, 512), (600, 200)] 204 + ) 205 + seg = encode_segment(TYPE_REPORT, 1, 100, content) 206 + vectors.append(("report_two_claims", seg)) 207 + 208 + # Report segment with single claim, large offsets 209 + content = encode_report_content( 210 + report_serial=1, 211 + checkpoint_serial=1, 212 + upper_bound=100000, 213 + lower_bound=50000, 214 + claims=[(50000, 50000)] 215 + ) 216 + seg = encode_segment(TYPE_REPORT, 99, 5555, content) 217 + vectors.append(("report_large_offsets", seg)) 218 + 219 + # Report ack (type 9) 220 + content = encode_report_ack_content(7) 221 + seg = encode_segment(TYPE_REPORT_ACK, 1, 100, content) 222 + vectors.append(("report_ack", seg)) 223 + 224 + # Cancel from sender (type 12), reason=Rlexc (2) 225 + content = encode_cancel_content(2) 226 + seg = encode_segment(TYPE_CANCEL_FROM_SENDER, 1, 100, content) 227 + vectors.append(("cancel_sender_rlexc", seg)) 228 + 229 + # Cancel from receiver (type 14), reason=User_cancelled (0) 230 + content = encode_cancel_content(0) 231 + seg = encode_segment(TYPE_CANCEL_FROM_RECEIVER, 5, 42, content) 232 + vectors.append(("cancel_receiver_user", seg)) 233 + 234 + # Cancel ack to sender (type 13) 235 + seg = encode_segment(TYPE_CANCEL_ACK_TO_SENDER, 1, 100, b"") 236 + vectors.append(("cancel_ack_to_sender", seg)) 237 + 238 + # Cancel ack to receiver (type 15) 239 + seg = encode_segment(TYPE_CANCEL_ACK_TO_RECEIVER, 5, 42, b"") 240 + vectors.append(("cancel_ack_to_receiver", seg)) 241 + 242 + return vectors 243 + 244 + 245 + # {1 Main} 246 + 247 + def main(): 248 + trace_dir = os.path.join(os.path.dirname(__file__), "..", "traces") 249 + os.makedirs(trace_dir, exist_ok=True) 250 + 251 + # Write SDNV traces 252 + with open(os.path.join(trace_dir, "sdnv.csv"), "w") as f: 253 + f.write("name,value,encoded_hex\n") 254 + for name, value in sdnv_vectors: 255 + encoded = encode_sdnv(value) 256 + f.write(f"{name},{value},{encoded.hex()}\n") 257 + print(f"Wrote {trace_dir}/sdnv.csv") 258 + 259 + # Write segment traces 260 + with open(os.path.join(trace_dir, "segments.csv"), "w") as f: 261 + f.write("name,segment_hex\n") 262 + for name, seg_bytes in make_segment_vectors(): 263 + f.write(f"{name},{seg_bytes.hex()}\n") 264 + print(f"Wrote {trace_dir}/segments.csv") 265 + 266 + 267 + if __name__ == "__main__": 268 + main()
+6
test/interop/python/scripts/generate.sh
··· 1 + #!/bin/bash 2 + set -euo pipefail 3 + SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" 4 + 5 + cd "$SCRIPT_DIR" 6 + python3 generate.py
+254
test/interop/python/test.ml
··· 1 + (** Python interop tests for ocaml-ltp. 2 + 3 + Tests SDNV encoding (RFC 6256) and LTP segment encoding (RFC 5326) 4 + against a Python reference implementation. 5 + 6 + Traces generated by: Python (pure RFC 6256 / RFC 5326 implementation) 7 + Regenerate: REGEN_TRACES=1 dune build @regen-traces *) 8 + 9 + let trace path = Filename.concat "traces" path 10 + 11 + (* {1 Hex Helpers} *) 12 + 13 + let hex_to_string hex = 14 + let len = String.length hex / 2 in 15 + String.init len (fun i -> 16 + let digit c = 17 + if c >= '0' && c <= '9' then Char.code c - Char.code '0' 18 + else if c >= 'a' && c <= 'f' then Char.code c - Char.code 'a' + 10 19 + else Char.code c - Char.code 'A' + 10 20 + in 21 + Char.chr ((digit hex.[i * 2] lsl 4) lor digit hex.[(i * 2) + 1])) 22 + 23 + let string_to_hex s = 24 + let buf = Buffer.create (String.length s * 2) in 25 + String.iter 26 + (fun c -> Buffer.add_string buf (Printf.sprintf "%02x" (Char.code c))) 27 + s; 28 + Buffer.contents buf 29 + 30 + (* {1 SDNV Trace Type} *) 31 + 32 + type sdnv_row = { name : string; value : string; encoded_hex : string } 33 + 34 + let sdnv_codec = 35 + Csvt.( 36 + Row.( 37 + obj (fun name value encoded_hex -> { name; value; encoded_hex }) 38 + |> col "name" string ~enc:(fun r -> r.name) 39 + |> col "value" string ~enc:(fun r -> r.value) 40 + |> col "encoded_hex" string ~enc:(fun r -> r.encoded_hex))) 41 + 42 + let sdnv_codec = Csvt.Row.finish sdnv_codec 43 + 44 + (* {1 Segment Trace Type} *) 45 + 46 + type segment_row = { name : string; segment_hex : string } 47 + 48 + let segment_codec = 49 + Csvt.( 50 + Row.( 51 + obj (fun name segment_hex -> { name; segment_hex }) 52 + |> col "name" string ~enc:(fun r -> r.name) 53 + |> col "segment_hex" string ~enc:(fun r -> r.segment_hex) 54 + |> finish)) 55 + 56 + (* {1 SDNV Tests} *) 57 + 58 + let load_sdnv_rows () = 59 + match Csvt.decode_file sdnv_codec (trace "sdnv.csv") with 60 + | Ok rows -> rows 61 + | Error e -> Alcotest.failf "CSV decode: %a" Csvt.pp_error e 62 + 63 + let test_sdnv_encode row () = 64 + let value = Int64.of_string row.value in 65 + let encoded = Ltp.encode_sdnv value in 66 + let got_hex = string_to_hex encoded in 67 + Alcotest.(check string) 68 + (row.name ^ ": encode") row.encoded_hex got_hex 69 + 70 + let test_sdnv_decode row () = 71 + let ref_bytes = hex_to_string row.encoded_hex in 72 + match Ltp.decode_sdnv ref_bytes 0 with 73 + | Error e -> Alcotest.failf "%s: decode failed: %s" row.name e 74 + | Ok (decoded, off) -> 75 + let expected = Int64.of_string row.value in 76 + Alcotest.(check int64) (row.name ^ ": value") expected decoded; 77 + Alcotest.(check int) 78 + (row.name ^ ": consumed all bytes") 79 + (String.length ref_bytes) off 80 + 81 + (* {1 Segment Tests} *) 82 + 83 + let load_segment_rows () = 84 + match Csvt.decode_file segment_codec (trace "segments.csv") with 85 + | Ok rows -> rows 86 + | Error e -> Alcotest.failf "CSV decode: %a" Csvt.pp_error e 87 + 88 + let test_segment_decode row () = 89 + let ref_bytes = hex_to_string row.segment_hex in 90 + match Ltp.decode_segment ref_bytes with 91 + | Error e -> 92 + Alcotest.failf "%s: decode failed: %a" row.name Ltp.pp_error e 93 + | Ok seg -> 94 + (* Re-encode and compare to reference *) 95 + let re_encoded = Ltp.encode_segment seg in 96 + let got_hex = string_to_hex re_encoded in 97 + Alcotest.(check string) 98 + (row.name ^ ": decode-reencode roundtrip") row.segment_hex got_hex 99 + 100 + let test_segment_encode row () = 101 + (* Decode the reference to get the segment, then encode and compare *) 102 + let ref_bytes = hex_to_string row.segment_hex in 103 + match Ltp.decode_segment ref_bytes with 104 + | Error e -> 105 + Alcotest.failf "%s: decode failed: %a" row.name Ltp.pp_error e 106 + | Ok seg -> 107 + let encoded = Ltp.encode_segment seg in 108 + let got_hex = string_to_hex encoded in 109 + Alcotest.(check string) (row.name ^ ": encode exact bytes") row.segment_hex got_hex 110 + 111 + let test_segment_fields row () = 112 + let ref_bytes = hex_to_string row.segment_hex in 113 + match Ltp.decode_segment ref_bytes with 114 + | Error e -> 115 + Alcotest.failf "%s: decode failed: %a" row.name Ltp.pp_error e 116 + | Ok seg -> ( 117 + (* Verify key fields based on test vector name *) 118 + match row.name with 119 + | "green_data_simple" -> 120 + Alcotest.(check bool) 121 + "is green" true (Ltp.is_green_segment seg); 122 + Alcotest.(check int64) 123 + "originator" 10L seg.session_id.originator; 124 + Alcotest.(check int64) 125 + "session number" 1000L seg.session_id.number; 126 + (match seg.content with 127 + | Ltp.Data ds -> 128 + Alcotest.(check int64) 129 + "client_service_id" 1L ds.client_service_id; 130 + Alcotest.(check int64) "block_offset" 0L ds.block_offset; 131 + Alcotest.(check string) 132 + "data" "Hello, LTP!" ds.data 133 + | _ -> Alcotest.fail "expected Data content") 134 + | "green_eob" -> 135 + Alcotest.(check bool) "is eob" true (Ltp.is_eob seg); 136 + Alcotest.(check bool) 137 + "is green" true (Ltp.is_green_segment seg); 138 + (match seg.content with 139 + | Ltp.Data ds -> 140 + Alcotest.(check int64) "block_offset" 512L ds.block_offset 141 + | _ -> Alcotest.fail "expected Data content") 142 + | "red_checkpoint" -> 143 + Alcotest.(check bool) 144 + "is checkpoint" true (Ltp.is_checkpoint seg); 145 + Alcotest.(check bool) 146 + "is red" true (Ltp.is_red_segment seg); 147 + (match seg.content with 148 + | Ltp.Data ds -> 149 + Alcotest.(check (option int64)) 150 + "checkpoint_serial" (Some 5L) ds.checkpoint_serial; 151 + Alcotest.(check (option int64)) 152 + "report_serial" (Some 0L) ds.report_serial; 153 + Alcotest.(check string) "data" "checkpoint data" ds.data 154 + | _ -> Alcotest.fail "expected Data content") 155 + | "red_checkpoint_eorp_eob" -> 156 + Alcotest.(check bool) "is eorp" true (Ltp.is_eorp seg); 157 + Alcotest.(check bool) "is eob" true (Ltp.is_eob seg); 158 + (match seg.content with 159 + | Ltp.Data ds -> 160 + Alcotest.(check int64) "block_offset" 256L ds.block_offset; 161 + Alcotest.(check (option int64)) 162 + "checkpoint_serial" (Some 10L) ds.checkpoint_serial; 163 + Alcotest.(check (option int64)) 164 + "report_serial" (Some 2L) ds.report_serial; 165 + Alcotest.(check string) "data" "final red" ds.data 166 + | _ -> Alcotest.fail "expected Data content") 167 + | "report_two_claims" -> 168 + (match seg.content with 169 + | Ltp.Report rs -> 170 + Alcotest.(check int64) "report_serial" 7L rs.report_serial; 171 + Alcotest.(check int64) 172 + "checkpoint_serial" 5L rs.checkpoint_serial; 173 + Alcotest.(check int64) "upper_bound" 1024L rs.upper_bound; 174 + Alcotest.(check int64) "lower_bound" 0L rs.lower_bound; 175 + Alcotest.(check int) "claims" 2 (List.length rs.claims); 176 + let c0 = List.nth rs.claims 0 in 177 + Alcotest.(check int64) "claim0.offset" 0L c0.offset; 178 + Alcotest.(check int64) "claim0.length" 512L c0.length; 179 + let c1 = List.nth rs.claims 1 in 180 + Alcotest.(check int64) "claim1.offset" 600L c1.offset; 181 + Alcotest.(check int64) "claim1.length" 200L c1.length 182 + | _ -> Alcotest.fail "expected Report content") 183 + | "report_large_offsets" -> 184 + (match seg.content with 185 + | Ltp.Report rs -> 186 + Alcotest.(check int64) "upper_bound" 100000L rs.upper_bound; 187 + Alcotest.(check int64) "lower_bound" 50000L rs.lower_bound; 188 + Alcotest.(check int) "claims" 1 (List.length rs.claims); 189 + let c0 = List.nth rs.claims 0 in 190 + Alcotest.(check int64) "claim0.offset" 50000L c0.offset; 191 + Alcotest.(check int64) "claim0.length" 50000L c0.length 192 + | _ -> Alcotest.fail "expected Report content") 193 + | "report_ack" -> 194 + (match seg.content with 195 + | Ltp.Report_ack ra -> 196 + Alcotest.(check int64) "report_serial" 7L ra.report_serial 197 + | _ -> Alcotest.fail "expected Report_ack content") 198 + | "cancel_sender_rlexc" -> 199 + Alcotest.(check int) 200 + "segment_type" 12 201 + (Ltp.segment_type_to_int seg.segment_type); 202 + (match seg.content with 203 + | Ltp.Cancel cs -> 204 + Alcotest.(check int) 205 + "reason" 2 (Ltp.cancel_reason_to_int cs.reason) 206 + | _ -> Alcotest.fail "expected Cancel content") 207 + | "cancel_receiver_user" -> 208 + Alcotest.(check int) 209 + "segment_type" 14 210 + (Ltp.segment_type_to_int seg.segment_type); 211 + (match seg.content with 212 + | Ltp.Cancel cs -> 213 + Alcotest.(check int) 214 + "reason" 0 (Ltp.cancel_reason_to_int cs.reason) 215 + | _ -> Alcotest.fail "expected Cancel content") 216 + | "cancel_ack_to_sender" -> 217 + Alcotest.(check int) 218 + "segment_type" 13 219 + (Ltp.segment_type_to_int seg.segment_type); 220 + (match seg.content with 221 + | Ltp.Cancel_ack () -> () 222 + | _ -> Alcotest.fail "expected Cancel_ack content") 223 + | "cancel_ack_to_receiver" -> 224 + Alcotest.(check int) 225 + "segment_type" 15 226 + (Ltp.segment_type_to_int seg.segment_type); 227 + (match seg.content with 228 + | Ltp.Cancel_ack () -> () 229 + | _ -> Alcotest.fail "expected Cancel_ack content") 230 + | name -> Alcotest.failf "unknown test vector: %s" name) 231 + 232 + (* {1 Test Runner} *) 233 + 234 + let () = 235 + let sdnv_rows = load_sdnv_rows () in 236 + let segment_rows = load_segment_rows () in 237 + let sdnv_cases f = 238 + List.map 239 + (fun (r : sdnv_row) -> Alcotest.test_case r.name `Quick (f r)) 240 + sdnv_rows 241 + in 242 + let seg_cases f = 243 + List.map 244 + (fun (r : segment_row) -> Alcotest.test_case r.name `Quick (f r)) 245 + segment_rows 246 + in 247 + Alcotest.run "ltp-interop-python" 248 + [ 249 + ("sdnv-encode", sdnv_cases test_sdnv_encode); 250 + ("sdnv-decode", sdnv_cases test_sdnv_decode); 251 + ("segment-decode", seg_cases test_segment_decode); 252 + ("segment-encode", seg_cases test_segment_encode); 253 + ("segment-fields", seg_cases test_segment_fields); 254 + ]
+13
test/interop/python/traces/sdnv.csv
··· 1 + name,value,encoded_hex 2 + zero,0,00 3 + one,1,01 4 + max_single_byte,127,7f 5 + min_two_byte,128,8100 6 + byte_255,255,817f 7 + byte_256,256,8200 8 + max_two_byte,16383,ff7f 9 + min_three_byte,16384,818000 10 + medium,1000000,bd8440 11 + uint32_max,4294967295,8fffffff7f 12 + large_40bit,78187493520,82a3a2d9f110 13 + int64_max,9223372036854775807,ffffffffffffffff7f
+12
test/interop/python/traces/segments.csv
··· 1 + name,segment_hex 2 + green_data_simple,040a87680001000b48656c6c6f2c204c545021 3 + green_eob,072a630002840004deadbeef 4 + red_checkpoint,0101640001000f0500636865636b706f696e742064617461 5 + red_checkpoint_eorp_eob,0301814800038200090a0266696e616c20726564 6 + report_two_claims,0801640007058800000200840084588148 7 + report_large_offsets,0863ab33000101868d2083865001838650838650 8 + report_ack,0901640007 9 + cancel_sender_rlexc,0c01640002 10 + cancel_receiver_user,0e052a0000 11 + cancel_ack_to_sender,0d016400 12 + cancel_ack_to_receiver,0f052a00