1# Copyright (c) Meta Platforms, Inc. and affiliates. 2# All rights reserved. 3# 4# This source code is licensed under the BSD-style license found in the 5# LICENSE file in the root directory of this source tree. 6 7import unittest 8 9from executorch.backends.xnnpack.serialization.xnnpack_graph_serialize import XNNHeader 10 11EXAMPLE_FLATBUFFER_OFFSET: int = 0x11223344 12EXAMPLE_FLATBUFFER_SIZE: int = 0x55667788 13EXAMPLE_CONSTANT_DATA_OFFSET: int = EXAMPLE_FLATBUFFER_OFFSET + EXAMPLE_FLATBUFFER_SIZE 14EXAMPLE_CONSTANT_DATA_SIZE: int = 0x99AABBCC99AABBCC 15 16# If header layout or magic changes, this test must change too. 17# The layout of the header is a contract, not an implementation detail 18EXAMPLE_HEADER_DATA: bytes = ( 19 # zeros 20 b"\x00\x00\x00\x00" 21 # magic 22 + b"XH00" 23 # All Values below are littl Endian 24 # header length 25 + b"\x1E\x00" 26 # Flatbuffer Offset 27 + b"\x44\x33\x22\x11" 28 # Flatbuffer Size 29 + b"\x88\x77\x66\x55" 30 # Constant Data Offset 31 + b"\xCC\xAA\x88\x66" 32 # Constant Data Size 33 + b"\xCC\xBB\xAA\x99\xCC\xBB\xAA\x99" 34) 35 36 37class TestXNNHeader(unittest.TestCase): 38 def test_to_bytes(self) -> None: 39 header = XNNHeader( 40 EXAMPLE_FLATBUFFER_OFFSET, 41 EXAMPLE_FLATBUFFER_SIZE, 42 EXAMPLE_CONSTANT_DATA_OFFSET, 43 EXAMPLE_CONSTANT_DATA_SIZE, 44 ) 45 self.assertEqual(header.to_bytes(), EXAMPLE_HEADER_DATA) 46 self.assertTrue(header.is_valid()) 47 48 def test_from_bytes(self) -> None: 49 header = XNNHeader.from_bytes(EXAMPLE_HEADER_DATA) 50 self.assertEqual(header.flatbuffer_offset, EXAMPLE_FLATBUFFER_OFFSET) 51 self.assertEqual(header.flatbuffer_size, EXAMPLE_FLATBUFFER_SIZE) 52 self.assertEqual(header.constant_data_offset, EXAMPLE_CONSTANT_DATA_OFFSET) 53 self.assertEqual(header.constant_data_size, EXAMPLE_CONSTANT_DATA_SIZE) 54 55 def test_invalid_metadata(self) -> None: 56 WRONG_MAGIC_DATA = EXAMPLE_HEADER_DATA[0:4] + b"YT01" + EXAMPLE_HEADER_DATA[8:] 57 with self.assertRaisesRegex( 58 ValueError, 59 "Invalid XNNHeader: invalid magic bytes b'YT01', expected b'XH00'", 60 ): 61 XNNHeader.from_bytes(WRONG_MAGIC_DATA) 62 63 WRONG_LENGTH_DATA = ( 64 EXAMPLE_HEADER_DATA[0:8] + b"\x1D\x00" + EXAMPLE_HEADER_DATA[10:] 65 ) 66 with self.assertRaisesRegex( 67 ValueError, 68 "Invalid XNNHeader: Invalid parsed length: data given was 30 bytes, parsed length was 29 bytes", 69 ): 70 XNNHeader.from_bytes(WRONG_LENGTH_DATA) 71 72 with self.assertRaisesRegex( 73 ValueError, 74 "Invalid XNNHeader: expected no more than 30 bytes, got 31", 75 ): 76 XNNHeader.from_bytes(EXAMPLE_HEADER_DATA + b"\x00") 77 78 def test_invalid_flatbuffer_size(self) -> None: 79 header = XNNHeader( 80 EXAMPLE_FLATBUFFER_OFFSET, 81 0, 82 EXAMPLE_CONSTANT_DATA_OFFSET, 83 EXAMPLE_CONSTANT_DATA_SIZE, 84 ) 85 86 with self.assertRaises(ValueError): 87 header.to_bytes() 88 89 def test_invalid_constant_data_offset(self) -> None: 90 header = XNNHeader( 91 EXAMPLE_FLATBUFFER_OFFSET, 92 EXAMPLE_FLATBUFFER_SIZE, 93 EXAMPLE_FLATBUFFER_OFFSET + EXAMPLE_FLATBUFFER_SIZE - 1, 94 EXAMPLE_CONSTANT_DATA_SIZE, 95 ) 96 97 with self.assertRaises(ValueError): 98 header.to_bytes() 99 100 def test_to_bytes_same_as_from_bytes(self) -> None: 101 header = XNNHeader.from_bytes(EXAMPLE_HEADER_DATA) 102 103 to_bytes = header.to_bytes() 104 self.assertEqual(EXAMPLE_HEADER_DATA, to_bytes) 105