File size: 4,640 Bytes
543c8e8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
"""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()