"""Small independent byte fixtures for the E02 inspector's validation boundaries.""" import struct import unittest from inspect_mb3dat import InvalidDAT, inspect from analyze_converter import cases, dat, i32, methods, vecs, TRIPLE class DATInspectorTests(unittest.TestCase): def test_two_distinct_header_layouts(self): for version, size in [(201501280, 21), (201512200, 23)]: data = dat(version) self.assertEqual(len(data), size) result = inspect(data) self.assertEqual(result['version'], version) self.assertEqual(result['consumed_bytes'], size) self.assertEqual(result['brushes'], []) def test_origin_and_direction_vectors(self): d = inspect(dat(201512200, b'\0\1' + vecs(TRIPLE), 1)) p = d['brushes'][0]['planes'][0] self.assertEqual(p['offset'], 21) self.assertEqual(p['p0'], [1, 2, 3]) self.assertEqual(p['u'], [4, 5, 6]) self.assertEqual(p['v'], [7, 8, 9]) def test_sparse_strings_and_binary_duplicate(self): body = b'\0\1\0first\0\0last\0\xff' + vecs(TRIPLE) d = inspect(dat(201512200, body, 1, tables=b'\1name\0\0')) self.assertEqual(d['string_properties'], ['name']) self.assertEqual(d['brushes'][0]['planes'][0]['strings'], {'0': 'last'}) def test_uv_wire_order_is_not_text_tuple_order(self): body = b'\0\1\0' + struct.pack('<10f', *range(1, 11)) + b'\xff' + vecs(TRIPLE) d = inspect(dat(201512200, body, 1, tables=b'\0\1uv\0')) self.assertEqual(d['brushes'][0]['planes'][0]['uv']['0'], list(range(1, 11))) def test_width_three_uses_four_bytes(self): data = dat(201512200, tables=i32(0) + i32(0), widths=30) self.assertEqual(inspect(data)['consumed_bytes'], 29) def test_legacy_signed_16_count_is_rejected(self): with self.assertRaisesRegex(InvalidDAT, 'invalid/excessive count -32768'): inspect(dat(201501280, b'\1\0\x80', 1)) def test_computed_contour(self): body = b'\0\1' + vecs(TRIPLE) + b'\1' + vecs([(9, 8, 7)]) d = inspect(dat(201512200, body, 1, computed=True)) self.assertEqual(d['brushes'][0]['planes'][0]['polygon'], [[9, 8, 7]]) def test_patch_implicit_grid_size(self): body = b'\0\3\3\2\2' + vecs([(1, 2, 3)] * 9) + vecs([(4, 5, 6)] * 9) + vecs([(0, 1, 0)] * 9) d = inspect(dat(201512200, patch_records=body, patch_count=1, computed=True)) self.assertEqual(len(d['patches'][0]['points']), 9) self.assertEqual(d['patches'][0]['normals'][8], [0, 1, 0]) def test_all_prefix_truncations_rejected(self): data = dat(201512200, b'\0\1' + vecs(TRIPLE), 1) for length in range(len(data)): with self.subTest(length=length), self.assertRaises(InvalidDAT): inspect(data[:length]) def test_unterminated_string_and_bad_index_rejected(self): with self.assertRaisesRegex(InvalidDAT, 'unterminated'): inspect(b'Mb3World' + i32(201512200) + b'\0\1no-terminator') with self.assertRaisesRegex(InvalidDAT, 'index 1 out of range'): inspect(dat(201512200, b'\0\1\1bad\0\xff' + vecs(TRIPLE), 1, tables=b'\1p\0\0')) def test_allocation_limit_and_count_overflow(self): with self.assertRaises(InvalidDAT): inspect(dat(201512200, brush_count=2**31 - 1)) with self.assertRaises(InvalidDAT): inspect(dat(201512200, patch_records=b'\x0a' + i32(65536) * 4, patch_count=1)) def test_trailing_bytes_reported(self): self.assertEqual(inspect(dat(201512200) + b'extra')['trailing_bytes'], 5) def test_wrong_magic_and_unknown_version(self): for data in [b'NOTWORLD' + dat(201512200)[8:], b'Mb3World' + i32(1) + b'\0' * 20]: with self.assertRaises(InvalidDAT): inspect(data) class ResearchHarnessTests(unittest.TestCase): def test_unique_case_names_and_chained_inputs(self): for version in [201501280, 201512200]: seen = set() for case in cases(version): self.assertNotIn(case['name'], seen) if 'from_case' in case: self.assertIn(case['from_case'], seen) seen.add(case['name']) def test_il_anchor_index(self): il = '.method public static void Sample () cil managed\n{\n// Method begins at RVA 0x2050\n// Code size: 7 (0x7)\n} // end of method Example::Sample\n' self.assertEqual(methods(il), [{'name': 'Example::Sample', 'rva': '0x2050', 'il_bytes': 7, 'il_line': 1}]) if __name__ == '__main__': unittest.main()