···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2025 Anil Madhavapeddy <anil@recoil.org>. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(* Test reading from a mock flow *)
77+let test_reader_basic () =
88+ Eio_main.run @@ fun _env ->
99+ let test_data = "Hello, World!" in
1010+ let flow = Eio.Flow.string_source test_data in
1111+ let reader = Bytesrw_eio.bytes_reader_of_flow flow in
1212+1313+ (* Read first slice *)
1414+ let slice1 = Bytesrw.Bytes.Reader.read reader in
1515+ Alcotest.(check bool) "slice is not eod" false (Bytesrw.Bytes.Slice.is_eod slice1);
1616+1717+ let read_data = Bytes.sub_string
1818+ (Bytesrw.Bytes.Slice.bytes slice1)
1919+ (Bytesrw.Bytes.Slice.first slice1)
2020+ (Bytesrw.Bytes.Slice.length slice1) in
2121+ Alcotest.(check string) "data matches" test_data read_data;
2222+2323+ (* Next read should be eod *)
2424+ let slice2 = Bytesrw.Bytes.Reader.read reader in
2525+ Alcotest.(check bool) "second read is eod" true (Bytesrw.Bytes.Slice.is_eod slice2)
2626+2727+(* Test reading with custom slice length *)
2828+let test_reader_custom_slice_length () =
2929+ Eio_main.run @@ fun _env ->
3030+ let test_data = "Hello, World!" in
3131+ let flow = Eio.Flow.string_source test_data in
3232+ let slice_length = 5 in
3333+ let reader = Bytesrw_eio.bytes_reader_of_flow ~slice_length flow in
3434+3535+ (* Read should respect slice_length as maximum *)
3636+ let slice = Bytesrw.Bytes.Reader.read reader in
3737+ Alcotest.(check bool) "slice length <= custom size" true
3838+ (Bytesrw.Bytes.Slice.length slice <= slice_length)
3939+4040+(* Test reading empty flow *)
4141+let test_reader_empty () =
4242+ Eio_main.run @@ fun _env ->
4343+ let flow = Eio.Flow.string_source "" in
4444+ let reader = Bytesrw_eio.bytes_reader_of_flow flow in
4545+4646+ let slice = Bytesrw.Bytes.Reader.read reader in
4747+ Alcotest.(check bool) "empty flow returns eod" true (Bytesrw.Bytes.Slice.is_eod slice)
4848+4949+(* Test writing to a mock flow *)
5050+let test_writer_basic () =
5151+ Eio_main.run @@ fun _env ->
5252+ let buf = Buffer.create 100 in
5353+ let flow = Eio.Flow.buffer_sink buf in
5454+ let writer = Bytesrw_eio.bytes_writer_of_flow flow in
5555+5656+ let test_data = "Hello, World!" in
5757+ let bytes = Bytes.of_string test_data in
5858+ let slice = Bytesrw.Bytes.Slice.make bytes ~first:0 ~length:(Bytes.length bytes) in
5959+6060+ Bytesrw.Bytes.Writer.write writer slice;
6161+6262+ let written = Buffer.contents buf in
6363+ Alcotest.(check string) "written data matches" test_data written
6464+6565+(* Test writing with custom slice length *)
6666+let test_writer_custom_slice_length () =
6767+ Eio_main.run @@ fun _env ->
6868+ let buf = Buffer.create 100 in
6969+ let flow = Eio.Flow.buffer_sink buf in
7070+ let slice_length = 8 in
7171+ let writer = Bytesrw_eio.bytes_writer_of_flow ~slice_length flow in
7272+7373+ let test_data = "Hello, World!" in
7474+ let bytes = Bytes.of_string test_data in
7575+ let slice = Bytesrw.Bytes.Slice.make bytes ~first:0 ~length:(Bytes.length bytes) in
7676+7777+ Bytesrw.Bytes.Writer.write writer slice;
7878+7979+ let written = Buffer.contents buf in
8080+ Alcotest.(check string) "written data matches regardless of slice_length" test_data written
8181+8282+(* Test writing eod slice (should be no-op) *)
8383+let test_writer_eod () =
8484+ Eio_main.run @@ fun _env ->
8585+ let buf = Buffer.create 100 in
8686+ let flow = Eio.Flow.buffer_sink buf in
8787+ let writer = Bytesrw_eio.bytes_writer_of_flow flow in
8888+8989+ Bytesrw.Bytes.Writer.write writer Bytesrw.Bytes.Slice.eod;
9090+9191+ let written = Buffer.contents buf in
9292+ Alcotest.(check string) "eod writes nothing" "" written
9393+9494+(* Test writing partial slice *)
9595+let test_writer_partial_slice () =
9696+ Eio_main.run @@ fun _env ->
9797+ let buf = Buffer.create 100 in
9898+ let flow = Eio.Flow.buffer_sink buf in
9999+ let writer = Bytesrw_eio.bytes_writer_of_flow flow in
100100+101101+ let test_data = "Hello, World!" in
102102+ let bytes = Bytes.of_string test_data in
103103+ (* Write only "World" *)
104104+ let slice = Bytesrw.Bytes.Slice.make bytes ~first:7 ~length:5 in
105105+106106+ Bytesrw.Bytes.Writer.write writer slice;
107107+108108+ let written = Buffer.contents buf in
109109+ Alcotest.(check string) "partial slice written" "World" written
110110+111111+(* Test multiple reads to ensure data isolation - buffers from previous reads
112112+ should not be corrupted by subsequent reads *)
113113+let test_reader_multiple_reads () =
114114+ Eio_main.run @@ fun _env ->
115115+ let test_data = "ABCDEFGHIJ" in (* 10 bytes *)
116116+ let flow = Eio.Flow.string_source test_data in
117117+ let reader = Bytesrw_eio.bytes_reader_of_flow ~slice_length:5 flow in
118118+119119+ (* Read first 5 bytes *)
120120+ let slice1 = Bytesrw.Bytes.Reader.read reader in
121121+ let bytes1 = Bytesrw.Bytes.Slice.bytes slice1 in
122122+ let data1 = Bytes.sub_string bytes1
123123+ (Bytesrw.Bytes.Slice.first slice1)
124124+ (Bytesrw.Bytes.Slice.length slice1) in
125125+126126+ (* Read next 5 bytes *)
127127+ let slice2 = Bytesrw.Bytes.Reader.read reader in
128128+ let data2 = Bytes.sub_string
129129+ (Bytesrw.Bytes.Slice.bytes slice2)
130130+ (Bytesrw.Bytes.Slice.first slice2)
131131+ (Bytesrw.Bytes.Slice.length slice2) in
132132+133133+ (* Critical test: verify first read's data is STILL intact after second read
134134+ This would fail if we were reusing buffers or if Cstruct.to_bytes created a view *)
135135+ let data1_check = Bytes.sub_string bytes1
136136+ (Bytesrw.Bytes.Slice.first slice1)
137137+ (Bytesrw.Bytes.Slice.length slice1) in
138138+139139+ Alcotest.(check string) "first read" "ABCDE" data1;
140140+ Alcotest.(check string) "second read" "FGHIJ" data2;
141141+ Alcotest.(check string) "first read still intact after second" "ABCDE" data1_check
142142+143143+(* Test round-trip: write then read *)
144144+let test_roundtrip () =
145145+ Eio_main.run @@ fun _env ->
146146+ let test_data = "Round-trip test data" in
147147+148148+ (* Write to buffer *)
149149+ let buf = Buffer.create 100 in
150150+ let write_flow = Eio.Flow.buffer_sink buf in
151151+ let writer = Bytesrw_eio.bytes_writer_of_flow write_flow in
152152+153153+ let bytes = Bytes.of_string test_data in
154154+ let slice = Bytesrw.Bytes.Slice.make bytes ~first:0 ~length:(Bytes.length bytes) in
155155+ Bytesrw.Bytes.Writer.write writer slice;
156156+157157+ (* Read back from buffer *)
158158+ let read_flow = Eio.Flow.string_source (Buffer.contents buf) in
159159+ let reader = Bytesrw_eio.bytes_reader_of_flow read_flow in
160160+161161+ let read_slice = Bytesrw.Bytes.Reader.read reader in
162162+ let read_data = Bytes.sub_string
163163+ (Bytesrw.Bytes.Slice.bytes read_slice)
164164+ (Bytesrw.Bytes.Slice.first read_slice)
165165+ (Bytesrw.Bytes.Slice.length read_slice) in
166166+167167+ Alcotest.(check string) "round-trip data matches" test_data read_data
168168+169169+let () =
170170+ Alcotest.run "Bytesrw_eio" [
171171+ "reader", [
172172+ Alcotest.test_case "basic read" `Quick test_reader_basic;
173173+ Alcotest.test_case "custom slice length" `Quick test_reader_custom_slice_length;
174174+ Alcotest.test_case "empty flow" `Quick test_reader_empty;
175175+ Alcotest.test_case "multiple reads data isolation" `Quick test_reader_multiple_reads;
176176+ ];
177177+ "writer", [
178178+ Alcotest.test_case "basic write" `Quick test_writer_basic;
179179+ Alcotest.test_case "custom slice length" `Quick test_writer_custom_slice_length;
180180+ Alcotest.test_case "eod write" `Quick test_writer_eod;
181181+ Alcotest.test_case "partial slice" `Quick test_writer_partial_slice;
182182+ ];
183183+ "integration", [
184184+ Alcotest.test_case "round-trip" `Quick test_roundtrip;
185185+ ];
186186+ ]