| # Copyright 2024 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 gonk binary data""" |
| |
| import unittest |
| |
| from gonk_tools.binary_handler import ( |
| BIN_LOG_SYNC_START_BYTES, |
| BIN_LOG_SYNC_START_STR, |
| BytearrayLoop, |
| decode_one_varint, |
| ) |
| from gonk_tools.gonk_log_stream import GonkLogStream |
| |
| # pylint: disable=line-too-long |
| _SAMPLE_ENCODED_LOGS = [ |
| b'~$llsrqA==E\xc4\xe2\x93~', |
| b'~$w4ZyKAI=0\x8f\xbf!~', |
| b'~$w4ZyKAQ=i\xa4\xa3~', |
| b'~$w4ZyKAY=a\x9d}]k~', |
| b'~$w4ZyKAg=\x86\xb8*~', |
| b'~$YNjZuQ==X\xb2\xc1@~', |
| b'~$J+09dYBA\xb2\xf8^~', |
| ] |
| |
| _SAMPLE_PROTO_PACKETS = [ |
| bytes.fromhex( |
| f'{BIN_LOG_SYNC_START_STR}122310d380ef020a04080010030a04080010020a040800104e0a040845104f0a0408001002' |
| ), |
| bytes.fromhex( |
| f'{BIN_LOG_SYNC_START_STR}122110bf010a04080010030a04080010020a040800104e0a04080010020a040800104f' |
| ), |
| bytes.fromhex( |
| f'{BIN_LOG_SYNC_START_STR}122a10b9010a04080010030a04080010020a04080010030a04080010020a0d080010b6ffffffffffffffff01' |
| ), |
| bytes.fromhex( |
| f'{BIN_LOG_SYNC_START_STR}122a10d1010a04080010030a04080010020a04084710030a0d080010b7ffffffffffffffff010a0408001002' |
| ), |
| bytes.fromhex( |
| f'{BIN_LOG_SYNC_START_STR}123310df010a04080010030a0d080010b6ffffffffffffffff010a04080010030a0d080010b7ffffffffffffffff010a0408001002' |
| ), |
| bytes.fromhex( |
| f'{BIN_LOG_SYNC_START_STR}127210ffffffff0f0a04080010030a04080010020a04080010030a04080010020a04080010020a0d080010b7ffffffffffffffff010a0d080010b7ffffffffffffffff010a04084710020a04084710030a04080010030a04080010020a1608ffffffffffffffffff0110ffffffffffffffffff01' |
| ), |
| bytes.fromhex( |
| f'{BIN_LOG_SYNC_START_STR}127b10ffffffff0f0a04080010030a04080010020a04080010030a04080010020a0d080010b6ffffffffffffffff010a0d080010b7ffffffffffffffff010a0d080010b7ffffffffffffffff010a04080010020a04080010030a04080010030a04080010020a1608ffffffffffffffffff0110ffffffffffffffffff01' |
| ), |
| bytes.fromhex( |
| f'{BIN_LOG_SYNC_START_STR}127210ffffffff0f0a04080010030a04080010020a04080010030a04084510020a04080010020a04080010020a0d080010b7ffffffffffffffff010a04084610020a04080010030a04080010030a0d084810b6ffffffffffffffff010a1608ffffffffffffffffff0110ffffffffffffffffff01' |
| ), |
| ] |
| _SAMPLE_LONG_PROTO_PACKETS = [ |
| # Artificially increased size > 127 (uses two bytes for the packet size varint) |
| bytes.fromhex( |
| f'{BIN_LOG_SYNC_START_STR}12F20110ffffffff0f0a04080010030a04080010020a04080010030a04084510020a04080010020a04080010020a0d080010b7ffffffffffffffff010a04084610020a04080010030a04080010030a0d084810b6ffffffffffffffff010a1608ffffffffffffffffff0110ffffffffffffffffff0110ffffffff0f0a04080010030a04080010020a04080010030a04084510020a04080010020a04080010020a0d080010b7ffffffffffffffff010a04084610020a04080010030a04080010030a0d084810b6ffffffffffffffff010a1608ffffffffffffffffff0110ffffffffffffffffff01ffffffffffffffffff01ffffffff' |
| ), |
| ] |
| |
| |
| # pylint: enable=line-too-long |
| class TestHandleBinaryData(unittest.TestCase): |
| """Tests for binary log data.""" |
| |
| def setUp(self): |
| # Show large diffs |
| self.maxDiff = None # pylint: disable=invalid-name |
| |
| def test_gonk_stream_write(self) -> None: |
| """Test GonkLogStream.write handles protos and logs correctly.""" |
| log_data = b''.join( |
| [ |
| _SAMPLE_ENCODED_LOGS[1], |
| ] |
| ) |
| proto_data1 = _SAMPLE_LONG_PROTO_PACKETS[0] |
| |
| gl = GonkLogStream() |
| gl.write(log_data) |
| # No proto bytes yet. |
| self.assertEqual(None, gl.remaining_proto_bytes) |
| |
| gl.write(proto_data1 + log_data) |
| # One log data section should be in the log_buffer |
| self.assertEqual(log_data, gl.log_buffer.getbuffer()) |
| # One proto packet should be consumed. |
| self.assertEqual(None, gl.remaining_proto_bytes) |
| self.assertEqual(log_data, gl.input_buffer.getbuffer()) |
| |
| # Write a third log |
| gl.write(log_data) |
| # Log buffer should have 3 logs |
| self.assertEqual(log_data * 3, gl.log_buffer.getbuffer()) |
| # Input buffer now empty |
| self.assertEqual(b'', gl.input_buffer.getbuffer()) |
| |
| def test_read_until(self) -> None: |
| """Test BytearrayLoop.read_until.""" |
| log_data = b''.join( |
| [ |
| _SAMPLE_ENCODED_LOGS[1], |
| _SAMPLE_ENCODED_LOGS[2], |
| ] |
| ) |
| proto_data = b''.join( |
| [ |
| _SAMPLE_PROTO_PACKETS[0], |
| _SAMPLE_PROTO_PACKETS[1], |
| ] |
| ) |
| |
| with self.subTest(msg="Matching sub sequence exists; read until that."): |
| merged_data = log_data + proto_data |
| bl = BytearrayLoop(merged_data) |
| self.assertEqual(log_data, bl.read_until(BIN_LOG_SYNC_START_BYTES)) |
| self.assertEqual(proto_data, bl.getbuffer()) |
| |
| with self.subTest( |
| msg="No matching sub sequence exists; read all data." |
| ): |
| bl = BytearrayLoop(log_data) |
| self.assertEqual(log_data, bl.read_until(BIN_LOG_SYNC_START_BYTES)) |
| # Remaining buffer should be empty |
| self.assertEqual(b'', bl.getbuffer()) |
| |
| with self.subTest( |
| msg="Matching sub sequence at the start, read nothing." |
| ): |
| bl = BytearrayLoop(proto_data) |
| self.assertEqual(b'', bl.read_until(BIN_LOG_SYNC_START_BYTES)) |
| # Remaining buffer should be full |
| self.assertEqual(proto_data, bl.getbuffer()) |
| |
| with self.subTest(msg="Subsequence is longer than the buffer."): |
| # Searching for a subsequece longer than the buffer should return an |
| # empty result. |
| too_short_buffer = BytearrayLoop(bytes.fromhex('abc123')) |
| self.assertEqual( |
| b'', too_short_buffer.read_until(bytes.fromhex('abc123456789')) |
| ) |
| |
| def test_proto_packet_length(self) -> None: |
| """Test decode_one_varint on remaining protobuf lengths.""" |
| for packet in _SAMPLE_PROTO_PACKETS: |
| bl = BytearrayLoop() |
| |
| bl.write(packet) |
| self.assertEqual(packet, bl.getbuffer()) |
| |
| original_size = len(packet) |
| packet_size = original_size |
| |
| self.assertEqual(BIN_LOG_SYNC_START_BYTES, bl.peek(5)) |
| # Read magic bytes fixed32 tag + 4 bytes. |
| magic_start = bl.read(5) |
| self.assertEqual(BIN_LOG_SYNC_START_BYTES, magic_start) |
| packet_size -= 5 |
| |
| # Read the tag. |
| _tag = bl.read(1) |
| packet_size -= 1 |
| |
| # Read the varint with the remaining packet size. |
| original_size = len(bl.getbuffer()) |
| decoded_remaining_size = decode_one_varint(bl) |
| # Subtract bytes used by the varint |
| packet_size -= original_size - len(bl.getbuffer()) |
| |
| self.assertEqual(decoded_remaining_size, packet_size) |
| |
| |
| if __name__ == '__main__': |
| unittest.main() |