//! Tests for the buffered io found in `std.io.read` and `std.io.write`. Because //! the tests run in the IO Monad, these tests currently don't play nice with //! `std.test`. With effects, this should be updated to run with `std.test`. let { assert_eq } = import! std.test let { wrap, (<*) } = import! std.applicative let { (<|), (|>) } = import! std.function let { (=<<) } = import! std.monad let { ? } = import! std.io let io_read @ { Read, default_read_to_end, read, read_to_end, ? } = import! std.io.read let io_write @ { Write, write_slice, write, write_all, flush, ? } = import! std.io.write let { Reference, ref, (<-), load } = import! std.reference let { min } = import! std.cmp let { ? } = import! std.byte let array @ { ? } = import! std.array let { ? } = import! std.effect let { lift } = import! std.effect.lift let { test, group } = import! std.test type Cursor = { pos : Reference Int, buf : Array Byte } let cursor buf : Array Byte -> IO Cursor = do pos = ref 0 wrap { pos, buf, } let read_cursor : Read Cursor = let read cursor num_bytes : Cursor -> Int -> IO (Option (Array Byte)) = do start = load cursor.pos let end = min (array.len cursor.buf) (start + num_bytes) let read_bytes = array.slice cursor.buf start end cursor.pos <- (start + array.len read_bytes) if array.is_empty read_bytes then wrap None else wrap (Some read_bytes) { read, read_to_end = default_read_to_end read } let write_array_ref : Write (Reference (Array Byte)) = { write_slice = \array_ref buf start end -> do array_buf = load array_ref let written = array.append array_buf (array.slice buf start end) array_ref <- written wrap (end - start), flush = \_ -> wrap () } let test_read = [ test "basic" <| \_ -> do reader = lift ( do c = cursor [1b, 2b, 3b, 4b, 5b] io_read.buffered_with_capacity 4 c) do bytes = lift <| read reader 2 do _ = assert_eq bytes (Some [1b, 2b]) do bytes = lift <| read reader 3 do _ = assert_eq bytes (Some [3b, 4b]) do bytes = lift <| read reader 9000 do _ = assert_eq bytes (Some [5b]) do bytes = lift <| read reader 9000 assert_eq (bytes) None, test "read directly if buffer is empty" <| \_ -> do reader = lift <| io_read.buffered_with_capacity 2 =<< cursor [1b, 2b, 3b, 4b, 5b] do bytes = lift <| read reader 5 assert_eq bytes (Some [1b, 2b, 3b, 4b, 5b]), test "read rest of buffer if it still holds data" <| \_ -> do reader = lift <| io_read.buffered_with_capacity 2 =<< cursor [1b, 2b, 3b, 4b, 5b] do _ = lift <| read reader 1 do bytes = lift <| read reader 4 assert_eq bytes (Some [2b]), ] let test_read_to_end = [ test "read all in one go" <| \_ -> do reader = lift <| io_read.buffered_with_capacity 5 =<< cursor [1b, 2b, 3b, 4b, 5b] do bytes = lift <| read_to_end reader assert_eq (bytes) [1b, 2b, 3b, 4b, 5b], test "read with small buffer" <| \_ -> do reader = lift <| io_read.buffered_with_capacity 1 =<< cursor [1b, 2b, 3b, 4b, 5b] do bytes = lift <| read_to_end reader assert_eq (bytes) [1b, 2b, 3b, 4b, 5b], test "read with part of buffer already read" <| \_ -> do reader = lift <| io_read.buffered_with_capacity 2 =<< cursor [1b, 2b, 3b, 4b, 5b] do _ = lift <| read reader 1 do bytes = lift <| read_to_end reader assert_eq bytes [2b, 3b, 4b, 5b], ] let test_write_and_flush = [ test "buffer all data if it fits" <| \_ -> do written : Reference (Array Byte) = lift <| ref [] do writer = lift <| io_write.buffered_with_capacity 5 written do bytes_written = lift <| write writer [1b, 2b] do _ = assert_eq bytes_written 2 do w = lift <| load written do _ = assert_eq w [] do _ = lift <| flush writer do w = lift <| load written assert_eq w [1b, 2b], test "flush immediately if buffer is empty and new data wouldn't fit" <| \_ -> do written : Reference (Array Byte) = lift <| ref [] do writer = lift <| io_write.buffered_with_capacity 2 written do bytes_written = lift <| write writer [1b, 2b, 3b] do _ = assert_eq bytes_written 3 do w = lift <| load written do _ = assert_eq w [1b, 2b, 3b] do _ = lift <| flush writer do w = lift <| load written assert_eq w [1b, 2b, 3b], test "flush buffer and then write new data if it wouldn't fit the buffer" <| \_ -> do written : Reference (Array Byte) = lift <| ref [] do writer = lift <| io_write.buffered_with_capacity 2 written do bytes_written = lift <| write writer [1b] do _ = assert_eq (bytes_written) 1 do w = lift <| load written do _ = assert_eq w [] do bytes_written = lift <| write writer [2b, 3b] do _ = assert_eq (bytes_written) 2 do w = lift <| load written do _ = assert_eq w [1b, 2b, 3b] do _ = lift <| flush writer do w = lift <| load written assert_eq w [1b, 2b, 3b], test "flush buffer if new data doesn't fit, then buffer the data" <| \_ -> do written : Reference (Array Byte) = lift <| ref [] do writer = lift <| io_write.buffered_with_capacity 5 written do bytes_written = lift <| write writer [1b, 2b, 3b] do _ = assert_eq (bytes_written) 3 do w = lift <| load written do _ = assert_eq w [] do bytes_written = lift <| write writer [4b, 5b, 6b] do _ = assert_eq (bytes_written) 3 do w = lift <| load written do _ = assert_eq w [1b, 2b, 3b] do _ = lift <| flush writer do w = lift <| load written assert_eq w [1b, 2b, 3b, 4b, 5b, 6b] ] let test_write_slice = [ test "empty slice" <| \_ -> do written : Reference (Array Byte) = lift <| ref [] do writer = lift <| io_write.buffered_with_capacity 5 written do bytes_written = lift <| write_slice writer [1b, 2b, 3b, 4b] 0 0 do _ = assert_eq (bytes_written) 0 do _ = lift <| flush writer do w = lift <| load written assert_eq w [], test "slice partial" <| \_ -> do written : Reference (Array Byte) = lift <| ref [] do writer = lift (written |> io_write.buffered_with_capacity 5) do bytes_written = lift <| write_slice writer [1b, 2b, 3b, 4b] 1 3 do _ = assert_eq (bytes_written) 2 do _ = lift <| flush writer do w = lift <| load written assert_eq w [2b, 3b], test "slice everything" <| \_ -> do written : Reference (Array Byte) = lift <| ref [] do writer = lift (written |> io_write.buffered_with_capacity 5) do bytes_written = lift <| write_slice writer [1b, 2b, 3b, 4b] 0 4 do _ = assert_eq (bytes_written) 4 do _ = lift <| flush writer do w = lift <| load written assert_eq w [1b, 2b, 3b, 4b], ] let test_write_all = [ test "basic" <| \_ -> do written : Reference (Array Byte) = lift <| ref [] do writer = lift (written |> io_write.buffered_with_capacity 1) do _ = lift <| write_all writer [1b, 2b, 3b, 4b] do _ = lift <| flush writer do w = lift <| load written assert_eq w [1b, 2b, 3b, 4b], test "write the data that's already buffered too" <| \_ -> do written : Reference (Array Byte) = lift <| ref [] do writer = lift (written |> io_write.buffered_with_capacity 2) do _ = lift <| write writer [1b] do _ = lift <| write_all writer [2b, 3b, 4b] do _ = lift <| flush writer do w = lift <| load written assert_eq w [1b, 2b, 3b, 4b], ] group "buffered_io" [ group "test_read" test_read, group "test_read_to_end" test_read_to_end, group "test_write_and_flush" test_write_and_flush, group "test_write_slice" test_write_slice, group "test_write_all" test_write_all, ]