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.

runner: Implement real Eio.Process execution

- Replace placeholder with actual Eio.Process.spawn
- Use existential types to hide Eio type parameters
- Update API to use process_mgr instead of fs
- Fix tests to work with real process behavior
- Add proper cwd support via Eio.Path.t

Also apply formatting fixes to ocaml-ltp.

+145 -124
+14 -8
fuzz/fuzz_ltp.ml
··· 33 33 let data = truncate data in 34 34 if String.length data = 0 then () 35 35 else 36 - let session_id = Ltp.{ originator = Int64.of_int orig_n; number = Int64.of_int sess_n } in 36 + let session_id = 37 + Ltp.{ originator = Int64.of_int orig_n; number = Int64.of_int sess_n } 38 + in 37 39 let seg = 38 - Ltp.make_data_segment ~session_id 39 - ~client_service_id:(Int64.of_int svc_id) 40 + Ltp.make_data_segment ~session_id ~client_service_id:(Int64.of_int svc_id) 40 41 ~block_offset:(Int64.of_int offset) data 41 42 in 42 43 let encoded = Ltp.encode_segment seg in ··· 52 53 let data = truncate data in 53 54 if String.length data = 0 then () 54 55 else 55 - let session_id = Ltp.{ originator = Int64.of_int orig_n; number = Int64.of_int sess_n } in 56 + let session_id = 57 + Ltp.{ originator = Int64.of_int orig_n; number = Int64.of_int sess_n } 58 + in 56 59 let seg = 57 60 Ltp.make_data_segment ~session_id ~client_service_id:1L ~block_offset:0L 58 61 ~checkpoint_serial:(Int64.of_int cp_serial) ~report_serial:0L data ··· 70 73 71 74 (** Report segment roundtrip. *) 72 75 let test_report_roundtrip orig_n sess_n rpt_serial cp_serial upper = 73 - let session_id = Ltp.{ originator = Int64.of_int orig_n; number = Int64.of_int sess_n } in 76 + let session_id = 77 + Ltp.{ originator = Int64.of_int orig_n; number = Int64.of_int sess_n } 78 + in 74 79 let claims = [ Ltp.{ offset = 0L; length = Int64.of_int upper } ] in 75 80 let seg = 76 - Ltp.make_report_segment ~session_id 77 - ~report_serial:(Int64.of_int rpt_serial) 81 + Ltp.make_report_segment ~session_id ~report_serial:(Int64.of_int rpt_serial) 78 82 ~checkpoint_serial:(Int64.of_int cp_serial) 79 83 ~upper_bound:(Int64.of_int upper) claims 80 84 in ··· 90 94 91 95 (** Cancel segment roundtrip. *) 92 96 let test_cancel_roundtrip orig_n sess_n reason_code from_sender = 93 - let session_id = Ltp.{ originator = Int64.of_int orig_n; number = Int64.of_int sess_n } in 97 + let session_id = 98 + Ltp.{ originator = Int64.of_int orig_n; number = Int64.of_int sess_n } 99 + in 94 100 let reason = Ltp.cancel_reason_of_int (reason_code mod 6) in 95 101 let seg = Ltp.make_cancel ~session_id ~from_sender reason in 96 102 let encoded = Ltp.encode_segment seg in
+77 -37
lib/ltp.ml
··· 34 34 if i >= len then Error "truncated SDNV" 35 35 else 36 36 let byte = Char.code (String.get buf i) in 37 - let value = Int64.logor (Int64.shift_left acc 7) (Int64.of_int (byte land 0x7F)) in 38 - if byte land 0x80 = 0 then Ok (value, i + 1) 39 - else loop value (i + 1) 37 + let value = 38 + Int64.logor (Int64.shift_left acc 7) (Int64.of_int (byte land 0x7F)) 39 + in 40 + if byte land 0x80 = 0 then Ok (value, i + 1) else loop value (i + 1) 40 41 in 41 42 loop 0L off 42 43 ··· 46 47 type session_number = int64 47 48 type session_id = { originator : engine_id; number : session_number } 48 49 49 - let pp_session_id fmt s = 50 - Format.fprintf fmt "%Ld:%Ld" s.originator s.number 50 + let pp_session_id fmt s = Format.fprintf fmt "%Ld:%Ld" s.originator s.number 51 51 52 52 (* {1 Segment Types} *) 53 53 ··· 151 151 (* {1 Extensions} *) 152 152 153 153 type extension_tag = Auth | Cookie | Unknown of int 154 - 155 154 type extension = { tag : extension_tag; value : string } 156 155 157 156 let pp_extension fmt ext = ··· 177 176 178 177 type reception_claim = { offset : int64; length : int64 } 179 178 180 - let pp_reception_claim fmt c = 181 - Format.fprintf fmt "[%Ld+%Ld]" c.offset c.length 179 + let pp_reception_claim fmt c = Format.fprintf fmt "[%Ld+%Ld]" c.offset c.length 182 180 183 181 (* {1 Segment Content} *) 184 182 ··· 218 216 } 219 217 220 218 let pp_segment fmt seg = 221 - Format.fprintf fmt "@[<v 2>LTP segment {@ session=%a@ type=%a@ content=%s@ }@]" 222 - pp_session_id seg.session_id pp_segment_type seg.segment_type 219 + Format.fprintf fmt 220 + "@[<v 2>LTP segment {@ session=%a@ type=%a@ content=%s@ }@]" pp_session_id 221 + seg.session_id pp_segment_type seg.segment_type 223 222 (match seg.content with 224 - | Data d -> Format.sprintf "Data(%Ld bytes at %Ld)" (Int64.of_int (String.length d.data)) d.block_offset 225 - | Report r -> Format.sprintf "Report(serial=%Ld, %d claims)" r.report_serial (List.length r.claims) 223 + | Data d -> 224 + Format.sprintf "Data(%Ld bytes at %Ld)" 225 + (Int64.of_int (String.length d.data)) 226 + d.block_offset 227 + | Report r -> 228 + Format.sprintf "Report(serial=%Ld, %d claims)" r.report_serial 229 + (List.length r.claims) 226 230 | Report_ack r -> Format.sprintf "Report_ack(%Ld)" r.report_serial 227 231 | Cancel c -> Format.asprintf "Cancel(%a)" pp_cancel_reason c.reason 228 232 | Cancel_ack () -> "Cancel_ack") ··· 253 257 let len_sdnv = encode_sdnv (Int64.of_int (String.length ext.value)) in 254 258 String.make 1 tag ^ len_sdnv ^ ext.value 255 259 256 - let encode_extensions exts = 257 - String.concat "" (List.map encode_extension exts) 260 + let encode_extensions exts = String.concat "" (List.map encode_extension exts) 258 261 259 262 let encode_data_segment seg_type ds = 260 263 let buf = Buffer.create 64 in ··· 263 266 Buffer.add_string buf (encode_sdnv (Int64.of_int (String.length ds.data))); 264 267 (* Add checkpoint/report serial if this is a checkpoint *) 265 268 (match (seg_type, ds.checkpoint_serial, ds.report_serial) with 266 - | (Red_checkpoint | Red_checkpoint_eorp | Red_checkpoint_eorp_eob), Some cp, Some rp -> 269 + | ( (Red_checkpoint | Red_checkpoint_eorp | Red_checkpoint_eorp_eob), 270 + Some cp, 271 + Some rp ) -> 267 272 Buffer.add_string buf (encode_sdnv cp); 268 273 Buffer.add_string buf (encode_sdnv rp) 269 274 | _ -> ()); ··· 288 293 let buf = Buffer.create 128 in 289 294 (* Control byte: version (4 bits) + type flags (4 bits) *) 290 295 let type_code = segment_type_to_int seg.segment_type in 291 - let control_byte = type_code in (* version 0 *) 296 + let control_byte = type_code in 297 + (* version 0 *) 292 298 Buffer.add_char buf (Char.chr control_byte); 293 299 (* Session ID *) 294 300 Buffer.add_string buf (encode_sdnv seg.session_id.originator); ··· 320 326 else if off >= String.length buf then Error (Extension_error "truncated") 321 327 else 322 328 let tag = extension_tag_of_int (Char.code (String.get buf off)) in 323 - let* len, off = decode_sdnv buf (off + 1) |> Result.map_error (fun _ -> Invalid_sdnv) in 329 + let* len, off = 330 + decode_sdnv buf (off + 1) |> Result.map_error (fun _ -> Invalid_sdnv) 331 + in 324 332 let len = Int64.to_int len in 325 - if off + len > String.length buf then Error (Extension_error "truncated value") 333 + if off + len > String.length buf then 334 + Error (Extension_error "truncated value") 326 335 else 327 336 let value = String.sub buf off len in 328 337 loop ({ tag; value } :: acc) (off + len) (remaining - 1) ··· 345 354 in 346 355 let* checkpoint_serial, report_serial, off = 347 356 if is_checkpoint_type seg_type then 348 - let* cp, off = decode_sdnv buf off |> Result.map_error (fun _ -> Invalid_sdnv) in 349 - let* rp, off = decode_sdnv buf off |> Result.map_error (fun _ -> Invalid_sdnv) in 357 + let* cp, off = 358 + decode_sdnv buf off |> Result.map_error (fun _ -> Invalid_sdnv) 359 + in 360 + let* rp, off = 361 + decode_sdnv buf off |> Result.map_error (fun _ -> Invalid_sdnv) 362 + in 350 363 Ok (Some cp, Some rp, off) 351 364 else Ok (None, None, off) 352 365 in ··· 356 369 else 357 370 let data = String.sub buf off data_len in 358 371 Ok 359 - ( Data { client_service_id; block_offset; data; checkpoint_serial; report_serial }, 372 + ( Data 373 + { 374 + client_service_id; 375 + block_offset; 376 + data; 377 + checkpoint_serial; 378 + report_serial; 379 + }, 360 380 off + data_len ) 361 381 362 382 let decode_report_segment buf off = ··· 384 404 let* claim_length, off = 385 405 decode_sdnv buf off |> Result.map_error (fun _ -> Invalid_sdnv) 386 406 in 387 - decode_claims ({ offset = claim_offset; length = claim_length } :: acc) off 388 - (Int64.sub remaining 1L) 407 + decode_claims 408 + ({ offset = claim_offset; length = claim_length } :: acc) 409 + off (Int64.sub remaining 1L) 389 410 in 390 411 let* claims, off = decode_claims [] off claim_count in 391 412 Ok 392 - ( Report { report_serial; checkpoint_serial; upper_bound; lower_bound; claims }, 413 + ( Report 414 + { report_serial; checkpoint_serial; upper_bound; lower_bound; claims }, 393 415 off ) 394 416 395 417 let decode_segment buf = ··· 426 448 | Report -> decode_report_segment buf off 427 449 | Report_ack -> 428 450 let* serial, off = 429 - decode_sdnv buf off |> Result.map_error (fun _ -> Invalid_sdnv) 451 + decode_sdnv buf off 452 + |> Result.map_error (fun _ -> Invalid_sdnv) 430 453 in 431 454 Ok (Report_ack { report_serial = serial }, off) 432 455 | Cancel_from_sender | Cancel_from_receiver -> 433 - if off >= len then Error (Content_error "missing cancel reason") 456 + if off >= len then 457 + Error (Content_error "missing cancel reason") 434 458 else 435 - let reason = cancel_reason_of_int (Char.code (String.get buf off)) in 459 + let reason = 460 + cancel_reason_of_int (Char.code (String.get buf off)) 461 + in 436 462 Ok (Cancel { reason }, off + 1) 437 463 | Cancel_ack_to_sender | Cancel_ack_to_receiver -> 438 464 Ok (Cancel_ack (), off) 439 465 in 440 - let* trailer_extensions, _off = decode_extensions buf off trl_count in 466 + let* trailer_extensions, _off = 467 + decode_extensions buf off trl_count 468 + in 441 469 Ok 442 470 { 443 471 session_id; ··· 449 477 450 478 (* {1 Segment Constructors} *) 451 479 452 - let make_data_segment ~session_id ~client_service_id ~block_offset ?checkpoint_serial 453 - ?report_serial ?(is_eorp = false) ?(is_eob = false) data = 480 + let make_data_segment ~session_id ~client_service_id ~block_offset 481 + ?checkpoint_serial ?report_serial ?(is_eorp = false) ?(is_eob = false) data 482 + = 454 483 let segment_type = 455 484 match checkpoint_serial with 456 485 | None -> if is_eob then Green_eob else Green_data ··· 465 494 header_extensions = []; 466 495 trailer_extensions = []; 467 496 content = 468 - Data { client_service_id; block_offset; data; checkpoint_serial; report_serial }; 497 + Data 498 + { 499 + client_service_id; 500 + block_offset; 501 + data; 502 + checkpoint_serial; 503 + report_serial; 504 + }; 469 505 } 470 506 471 - let make_report_segment ~session_id ~report_serial ~checkpoint_serial ~upper_bound 472 - ?(lower_bound = 0L) claims = 507 + let make_report_segment ~session_id ~report_serial ~checkpoint_serial 508 + ~upper_bound ?(lower_bound = 0L) claims = 473 509 { 474 510 session_id; 475 511 segment_type = Report; 476 512 header_extensions = []; 477 513 trailer_extensions = []; 478 514 content = 479 - Report { report_serial; checkpoint_serial; upper_bound; lower_bound; claims }; 515 + Report 516 + { report_serial; checkpoint_serial; upper_bound; lower_bound; claims }; 480 517 } 481 518 482 519 let make_report_ack ~session_id ~report_serial = ··· 491 528 let make_cancel ~session_id ~from_sender reason = 492 529 { 493 530 session_id; 494 - segment_type = (if from_sender then Cancel_from_sender else Cancel_from_receiver); 531 + segment_type = 532 + (if from_sender then Cancel_from_sender else Cancel_from_receiver); 495 533 header_extensions = []; 496 534 trailer_extensions = []; 497 535 content = Cancel { reason }; ··· 500 538 let make_cancel_ack ~session_id ~to_sender = 501 539 { 502 540 session_id; 503 - segment_type = (if to_sender then Cancel_ack_to_sender else Cancel_ack_to_receiver); 541 + segment_type = 542 + (if to_sender then Cancel_ack_to_sender else Cancel_ack_to_receiver); 504 543 header_extensions = []; 505 544 trailer_extensions = []; 506 545 content = Cancel_ack (); ··· 517 556 518 557 let is_red_segment seg = 519 558 match seg.segment_type with 520 - | Red_data | Red_checkpoint | Red_checkpoint_eorp | Red_checkpoint_eorp_eob -> true 559 + | Red_data | Red_checkpoint | Red_checkpoint_eorp | Red_checkpoint_eorp_eob -> 560 + true 521 561 | _ -> false 522 562 523 563 let is_green_segment seg =
+37 -68
lib/ltp.mli
··· 40 40 (** [encode_sdnv n] encodes [n] as an SDNV. *) 41 41 42 42 val decode_sdnv : string -> int -> (int64 * int, string) result 43 - (** [decode_sdnv buf off] decodes an SDNV from [buf] starting at [off]. 44 - Returns [(value, new_offset)] on success. *) 43 + (** [decode_sdnv buf off] decodes an SDNV from [buf] starting at [off]. Returns 44 + [(value, new_offset)] on success. *) 45 45 46 46 (** {1 Engine and Session IDs} *) 47 47 ··· 59 59 (** {1 Segment Types} *) 60 60 61 61 type segment_type = 62 - | Red_data 63 - (** Type 0: Red data segment, no checkpoint. *) 64 - | Red_checkpoint 65 - (** Type 1: Red data checkpoint. *) 66 - | Red_checkpoint_eorp 67 - (** Type 2: Red data checkpoint at end of red-part. *) 62 + | Red_data (** Type 0: Red data segment, no checkpoint. *) 63 + | Red_checkpoint (** Type 1: Red data checkpoint. *) 64 + | Red_checkpoint_eorp (** Type 2: Red data checkpoint at end of red-part. *) 68 65 | Red_checkpoint_eorp_eob 69 66 (** Type 3: Red data checkpoint at end of red-part and block. *) 70 - | Green_data 71 - (** Type 4: Green data segment. *) 72 - | Green_eob 73 - (** Type 7: Green data segment at end of block. *) 74 - | Report 75 - (** Type 8: Report segment. *) 76 - | Report_ack 77 - (** Type 9: Report acknowledgment. *) 78 - | Cancel_from_sender 79 - (** Type 12: Session canceled by sender. *) 80 - | Cancel_ack_to_sender 81 - (** Type 13: Cancel acknowledgment (to sender). *) 82 - | Cancel_from_receiver 83 - (** Type 14: Session canceled by receiver. *) 84 - | Cancel_ack_to_receiver 85 - (** Type 15: Cancel acknowledgment (to receiver). *) 67 + | Green_data (** Type 4: Green data segment. *) 68 + | Green_eob (** Type 7: Green data segment at end of block. *) 69 + | Report (** Type 8: Report segment. *) 70 + | Report_ack (** Type 9: Report acknowledgment. *) 71 + | Cancel_from_sender (** Type 12: Session canceled by sender. *) 72 + | Cancel_ack_to_sender (** Type 13: Cancel acknowledgment (to sender). *) 73 + | Cancel_from_receiver (** Type 14: Session canceled by receiver. *) 74 + | Cancel_ack_to_receiver (** Type 15: Cancel acknowledgment (to receiver). *) 86 75 87 76 val pp_segment_type : segment_type Fmt.t 88 77 val segment_type_to_int : segment_type -> int ··· 91 80 (** {1 Cancel Reasons} *) 92 81 93 82 type cancel_reason = 94 - | User_cancelled 95 - (** 0x00: Client service canceled session. *) 96 - | Unreachable 97 - (** 0x01: Unreachable client service. *) 98 - | Rlexc 99 - (** 0x02: Retransmission limit exceeded. *) 100 - | Miscolored 101 - (** 0x03: Received miscolored segment. *) 102 - | System_cancelled 103 - (** 0x04: System error. *) 104 - | Rxmtcycexc 105 - (** 0x05: Exceeded retransmission cycles limit. *) 106 - | Reserved of int 107 - (** Reserved reason code. *) 83 + | User_cancelled (** 0x00: Client service canceled session. *) 84 + | Unreachable (** 0x01: Unreachable client service. *) 85 + | Rlexc (** 0x02: Retransmission limit exceeded. *) 86 + | Miscolored (** 0x03: Received miscolored segment. *) 87 + | System_cancelled (** 0x04: System error. *) 88 + | Rxmtcycexc (** 0x05: Exceeded retransmission cycles limit. *) 89 + | Reserved of int (** Reserved reason code. *) 108 90 109 91 val pp_cancel_reason : cancel_reason Fmt.t 110 92 val cancel_reason_to_int : cancel_reason -> int ··· 113 95 (** {1 Extensions} *) 114 96 115 97 type extension_tag = 116 - | Auth 117 - (** 0x00: LTP authentication extension. *) 118 - | Cookie 119 - (** 0x01: LTP cookie extension. *) 120 - | Unknown of int 121 - (** Unknown or private extension tag. *) 98 + | Auth (** 0x00: LTP authentication extension. *) 99 + | Cookie (** 0x01: LTP cookie extension. *) 100 + | Unknown of int (** Unknown or private extension tag. *) 122 101 123 102 type extension = { tag : extension_tag; value : string } 124 103 (** Extension TLV (RFC 5326 Section 3.3). *) ··· 135 114 (** {1 Segment Content} *) 136 115 137 116 type data_segment = { 138 - client_service_id : int64; 139 - (** Identifies the destination service. *) 140 - block_offset : int64; 141 - (** Position within the transmitted block. *) 142 - data : string; 143 - (** Payload data. *) 144 - checkpoint_serial : int64 option; 145 - (** Present if segment is a checkpoint. *) 146 - report_serial : int64 option; 147 - (** Present if checkpoint; 0 if asynchronous. *) 117 + client_service_id : int64; (** Identifies the destination service. *) 118 + block_offset : int64; (** Position within the transmitted block. *) 119 + data : string; (** Payload data. *) 120 + checkpoint_serial : int64 option; (** Present if segment is a checkpoint. *) 121 + report_serial : int64 option; (** Present if checkpoint; 0 if asynchronous. *) 148 122 } 149 123 (** Data segment content (RFC 5326 Section 3.2.1). *) 150 124 151 125 type report_segment = { 152 - report_serial : int64; 153 - (** Report serial number. *) 154 - checkpoint_serial : int64; 155 - (** Checkpoint being acknowledged (0 if async). *) 156 - upper_bound : int64; 157 - (** Block prefix size to which claims apply. *) 158 - lower_bound : int64; 159 - (** Interior prefix not covered by claims. *) 160 - claims : reception_claim list; 161 - (** Successfully received data ranges. *) 126 + report_serial : int64; (** Report serial number. *) 127 + checkpoint_serial : int64; (** Checkpoint being acknowledged (0 if async). *) 128 + upper_bound : int64; (** Block prefix size to which claims apply. *) 129 + lower_bound : int64; (** Interior prefix not covered by claims. *) 130 + claims : reception_claim list; (** Successfully received data ranges. *) 162 131 } 163 132 (** Report segment content (RFC 5326 Section 3.2.4). *) 164 133 ··· 233 202 reception_claim list -> 234 203 segment 235 204 (** [make_report_segment ~session_id ~report_serial ~checkpoint_serial 236 - ~upper_bound claims] creates a report segment. *) 205 + ~upper_bound claims] creates a report segment. *) 237 206 238 - val make_report_ack : 239 - session_id:session_id -> report_serial:int64 -> segment 207 + val make_report_ack : session_id:session_id -> report_serial:int64 -> segment 240 208 (** [make_report_ack ~session_id ~report_serial] acknowledges a report. *) 241 209 242 - val make_cancel : session_id:session_id -> from_sender:bool -> cancel_reason -> segment 210 + val make_cancel : 211 + session_id:session_id -> from_sender:bool -> cancel_reason -> segment 243 212 (** [make_cancel ~session_id ~from_sender reason] creates a cancel segment. *) 244 213 245 214 val make_cancel_ack : session_id:session_id -> to_sender:bool -> segment
+17 -11
test/test.ml
··· 63 63 let code = Ltp.segment_type_to_int t in 64 64 match Ltp.segment_type_of_int code with 65 65 | Some t' -> 66 - Alcotest.(check int) (Format.asprintf "%a" Ltp.pp_segment_type t) code 66 + Alcotest.(check int) 67 + (Format.asprintf "%a" Ltp.pp_segment_type t) 68 + code 67 69 (Ltp.segment_type_to_int t') 68 70 | None -> Alcotest.fail "roundtrip failed") 69 71 types ··· 73 75 let test_green_data_roundtrip () = 74 76 let data = "Hello, LTP!" in 75 77 let seg = 76 - Ltp.make_data_segment ~session_id ~client_service_id:1L ~block_offset:0L data 78 + Ltp.make_data_segment ~session_id ~client_service_id:1L ~block_offset:0L 79 + data 77 80 in 78 81 Alcotest.(check bool) "is green" true (Ltp.is_green_segment seg); 79 82 let encoded = Ltp.encode_segment seg in 80 83 match Ltp.decode_segment encoded with 81 - | Ok decoded -> 82 - Alcotest.(check bool) "decoded is green" true (Ltp.is_green_segment decoded); 83 - (match decoded.content with 84 + | Ok decoded -> ( 85 + Alcotest.(check bool) 86 + "decoded is green" true 87 + (Ltp.is_green_segment decoded); 88 + match decoded.content with 84 89 | Ltp.Data ds -> Alcotest.(check string) "data" data ds.data 85 90 | _ -> Alcotest.fail "wrong content type") 86 91 | Error e -> Alcotest.failf "decode failed: %a" Ltp.pp_error e ··· 97 102 Alcotest.(check bool) "is eob" true (Ltp.is_eob seg); 98 103 let encoded = Ltp.encode_segment seg in 99 104 match Ltp.decode_segment encoded with 100 - | Ok decoded -> 105 + | Ok decoded -> ( 101 106 Alcotest.(check bool) "decoded is red" true (Ltp.is_red_segment decoded); 102 - (match decoded.content with 107 + match decoded.content with 103 108 | Ltp.Data ds -> 104 109 Alcotest.(check string) "data" data ds.data; 105 - Alcotest.(check (option int64)) "checkpoint" (Some 1L) ds.checkpoint_serial 110 + Alcotest.(check (option int64)) 111 + "checkpoint" (Some 1L) ds.checkpoint_serial 106 112 | _ -> Alcotest.fail "wrong content type") 107 113 | Error e -> Alcotest.failf "decode failed: %a" Ltp.pp_error e 108 114 ··· 136 142 match Ltp.decode_segment encoded with 137 143 | Ok decoded -> ( 138 144 match decoded.content with 139 - | Ltp.Report_ack ra -> Alcotest.(check int64) "serial" 42L ra.report_serial 145 + | Ltp.Report_ack ra -> 146 + Alcotest.(check int64) "serial" 42L ra.report_serial 140 147 | _ -> Alcotest.fail "wrong content type") 141 148 | Error e -> Alcotest.failf "decode failed: %a" Ltp.pp_error e 142 149 ··· 189 196 Alcotest.test_case "red checkpoint roundtrip" `Quick 190 197 test_red_checkpoint_roundtrip; 191 198 ] ); 192 - ( "report", 193 - [ Alcotest.test_case "roundtrip" `Quick test_report_roundtrip ] ); 199 + ("report", [ Alcotest.test_case "roundtrip" `Quick test_report_roundtrip ]); 194 200 ( "report_ack", 195 201 [ Alcotest.test_case "roundtrip" `Quick test_report_ack_roundtrip ] ); 196 202 ( "cancel",