blob: b2a61d38f65119e0f9380565047d7612ca262c68 [file]
# Copyright 2026 The Pigweed Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
"""Tests for pw_bytes.io"""
import io
import os
import unittest
import unittest.mock
from pw_bytes.io import BinaryReader, BinaryWriter
class BinaryReaderTest(unittest.TestCase):
"""BinaryReader tests."""
def test_tell(self) -> None:
f = io.BytesIO(b"12345")
br = BinaryReader(f, "little")
self.assertEqual(br.tell(), 0)
f.seek(3)
self.assertEqual(br.tell(), 3)
def test_seek(self) -> None:
f = io.BytesIO(b"12345")
br = BinaryReader(f, "little")
# 1. Verify standard absolute seek return value
self.assertEqual(br.seek(3), 3)
self.assertEqual(br.tell(), 3)
# 2. Verify relative seek using SEEK_CUR
self.assertEqual(br.seek(-1, os.SEEK_CUR), 2)
self.assertEqual(br.tell(), 2)
# 3. Verify relative seek using SEEK_END
self.assertEqual(br.seek(-1, os.SEEK_END), 4)
self.assertEqual(br.tell(), 4)
# 4. Verify returning to start using f.seek directly
f.seek(0)
self.assertEqual(br.tell(), 0)
def test_read(self) -> None:
f = io.BytesIO(b"12345")
br = BinaryReader(f, "little")
self.assertEqual(br.read(2), b"12")
self.assertEqual(br.read(3), b"345")
def test_read_then_tell(self) -> None:
f = io.BytesIO(b"12345")
br = BinaryReader(f, "little")
self.assertEqual(br.tell(), 0)
br.read(2)
self.assertEqual(br.tell(), 2)
def test_read_uint(self) -> None:
f = io.BytesIO(b"\x11\x22\x33")
br = BinaryReader(f, "little")
self.assertEqual(br.read_uint(3), 0x332211)
def test_read_uint_big(self) -> None:
f = io.BytesIO(b"\x11\x22\x33\x44")
br = BinaryReader(f, "little")
self.assertEqual(br.read_uint(3, "big"), 0x112233)
def test_read_u8(self) -> None:
f = io.BytesIO(b"\x11")
br = BinaryReader(f, "little")
self.assertEqual(br.read_u8(), 0x11)
def test_read_u16(self) -> None:
f = io.BytesIO(b"\x11\x22")
br = BinaryReader(f, "little")
self.assertEqual(br.read_u16(), 0x2211)
def test_read_u32(self) -> None:
f = io.BytesIO(b"\x11\x22\x33\x44")
br = BinaryReader(f, "little")
self.assertEqual(br.read_u32(), 0x44332211)
def test_read_u64(self) -> None:
f = io.BytesIO(b"\x11\x22\x33\x44\x55\x66\x77\x88")
br = BinaryReader(f, "little")
self.assertEqual(br.read_u64(), 0x8877665544332211)
class BinaryWriterTest(unittest.TestCase):
"""BinaryWriter tests."""
def test_tell(self) -> None:
f = io.BytesIO(b"12345")
bw = BinaryWriter(f, "little")
self.assertEqual(bw.tell(), 0)
f.seek(3)
self.assertEqual(bw.tell(), 3)
def test_seek(self) -> None:
f = io.BytesIO(b"12345")
bw = BinaryWriter(f, "little")
# 1. Verify standard absolute seek return value
self.assertEqual(bw.seek(3), 3)
self.assertEqual(bw.tell(), 3)
# 2. Verify relative seek using SEEK_CUR
self.assertEqual(bw.seek(-1, os.SEEK_CUR), 2)
self.assertEqual(bw.tell(), 2)
# 3. Verify relative seek using SEEK_END
self.assertEqual(bw.seek(-1, os.SEEK_END), 4)
self.assertEqual(bw.tell(), 4)
# 4. Verify returning to start using f.seek directly
f.seek(0)
self.assertEqual(bw.tell(), 0)
def test_write(self) -> None:
f = io.BytesIO()
bw = BinaryWriter(f, "little")
bw.write(b"hello")
self.assertEqual(f.getvalue(), b"hello")
def test_write_then_tell(self) -> None:
f = io.BytesIO()
bw = BinaryWriter(f, "little")
self.assertEqual(bw.tell(), 0)
bw.write(b"hello")
self.assertEqual(bw.tell(), 5)
def test_write_uint(self) -> None:
f = io.BytesIO()
bw = BinaryWriter(f, "little")
bw.write_uint(0x332211, 3)
bw.write_uint(0x44, 1)
bw.write_uint(0x5566778899, 5, "big")
self.assertEqual(f.getvalue(), b"\x11\x22\x33\x44\x55\x66\x77\x88\x99")
def test_write_u8(self) -> None:
f = io.BytesIO()
bw = BinaryWriter(f, "little")
bw.write_u8(0x11)
self.assertEqual(f.getvalue(), b"\x11")
def test_write_u16(self) -> None:
f = io.BytesIO()
bw = BinaryWriter(f, "little")
bw.write_u16(0x2211)
self.assertEqual(f.getvalue(), b"\x11\x22")
def test_write_u32(self) -> None:
f = io.BytesIO()
bw = BinaryWriter(f, "little")
bw.write_u32(0x44332211)
self.assertEqual(f.getvalue(), b"\x11\x22\x33\x44")
def test_write_u64(self) -> None:
f = io.BytesIO()
bw = BinaryWriter(f, "little")
bw.write_u64(0x8877665544332211)
self.assertEqual(f.getvalue(), b"\x11\x22\x33\x44\x55\x66\x77\x88")
class BinaryReaderRobustTest(unittest.TestCase):
"""Tests for BinaryReader robust behavior."""
def test_read_eof(self) -> None:
f = io.BytesIO(b"12")
br = BinaryReader(f, "little")
with self.assertRaises(EOFError) as ctx:
br.read(3)
self.assertIn("Reached EOF: read 2 of 3 bytes", str(ctx.exception))
def test_read_blocking(self) -> None:
reader = unittest.mock.Mock()
reader.read.return_value = None
br = BinaryReader(reader, "little")
with self.assertRaises(BlockingIOError):
br.read(3)
def test_read_partial(self) -> None:
reader = unittest.mock.Mock()
reader.read.side_effect = [b"1", b"2", b"3"]
br = BinaryReader(reader, "little")
self.assertEqual(br.read(3), b"123")
class BinaryWriterRobustTest(unittest.TestCase):
"""Tests for BinaryWriter robust behavior."""
def test_write_blocking(self) -> None:
writer = unittest.mock.Mock()
writer.write.return_value = None
bw = BinaryWriter(writer, "little")
with self.assertRaises(BlockingIOError):
bw.write(b"123")
def test_write_zero_bytes(self) -> None:
writer = unittest.mock.Mock()
writer.write.return_value = 0
bw = BinaryWriter(writer, "little")
with self.assertRaises(OSError) as ctx:
bw.write(b"123")
self.assertIn("Zero bytes written", str(ctx.exception))
def test_write_partial(self) -> None:
writer = unittest.mock.Mock()
writer.write.side_effect = lambda data: len(data[:1])
bw = BinaryWriter(writer, "little")
bw.write(b"123")
self.assertEqual(
writer.write.call_args_list,
[
unittest.mock.call(b"123"),
unittest.mock.call(b"23"),
unittest.mock.call(b"3"),
],
)
if __name__ == "__main__":
unittest.main()