• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python3
2#
3# Copyright 2016 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17import binascii
18import unittest
19
20import cstruct
21
22
23# These aren't constants, they're classes. So, pylint: disable=invalid-name
24TestStructA = cstruct.Struct("TestStructA", "=BI", "byte1 int2")
25TestStructB = cstruct.Struct("TestStructB", "=BI", "byte1 int2")
26
27
28class CstructTest(unittest.TestCase):
29
30  def CheckEquals(self, a, b):
31    self.assertEqual(a, b)
32    self.assertEqual(b, a)
33    assert a == b
34    assert b == a
35    assert not (a != b)  # pylint: disable=g-comparison-negation,superfluous-parens
36    assert not (b != a)  # pylint: disable=g-comparison-negation,superfluous-parens
37
38  def CheckNotEquals(self, a, b):
39    self.assertNotEqual(a, b)
40    self.assertNotEqual(b, a)
41    assert a != b
42    assert b != a
43    assert not (a == b)  # pylint: disable=g-comparison-negation,superfluous-parens
44    assert not (b == a)  # pylint: disable=g-comparison-negation,superfluous-parens
45
46  def testEqAndNe(self):
47    a1 = TestStructA((1, 2))
48    a2 = TestStructA((2, 3))
49    a3 = TestStructA((1, 2))
50    b = TestStructB((1, 2))
51    self.CheckNotEquals(a1, b)
52    self.CheckNotEquals(a2, b)
53    self.CheckNotEquals(a1, a2)
54    self.CheckNotEquals(a2, a3)
55    for i in [a1, a2, a3, b]:
56      self.CheckEquals(i, i)
57    self.CheckEquals(a1, a3)
58
59  def testNestedStructs(self):
60    Nested = cstruct.Struct("Nested", "!HSSi",
61                            "word1 nest2 nest3 int4",
62                            [TestStructA, TestStructB])
63    DoubleNested = cstruct.Struct("DoubleNested", "SSB",
64                                  "nest1 nest2 byte3",
65                                  [TestStructA, Nested])
66    d = DoubleNested((TestStructA((1, 2)),
67                      Nested((5, TestStructA((3, 4)), TestStructB((7, 8)), 9)),
68                      6))
69
70    expectedlen = (len(TestStructA) +
71                   2 + len(TestStructA) + len(TestStructB) + 4 +
72                   1)
73    self.assertEqual(expectedlen, len(DoubleNested))
74
75    self.assertEqual(7, d.nest2.nest3.byte1)
76
77    d.byte3 = 252
78    d.nest2.word1 = 33214
79    n = d.nest2
80    n.int4 = -55
81    t = n.nest3
82    t.int2 = 33627591
83
84    self.assertEqual(33627591, d.nest2.nest3.int2)
85
86    expected = (
87        "DoubleNested(nest1=TestStructA(byte1=1, int2=2),"
88        " nest2=Nested(word1=33214, nest2=TestStructA(byte1=3, int2=4),"
89        " nest3=TestStructB(byte1=7, int2=33627591), int4=-55), byte3=252)")
90    self.assertEqual(expected, str(d))
91    expected = binascii.unhexlify("01" "02000000"
92                                  "81be" "03" "04000000"
93                                  "07" "c71d0102" "ffffffc9" "fc")
94    self.assertEqual(expected, d.Pack())
95    unpacked = DoubleNested(expected)
96    self.CheckEquals(unpacked, d)
97
98  def testNullTerminatedStrings(self):
99    TestStruct = cstruct.Struct("TestStruct", "B16si16AH",
100                                "byte1 string2 int3 ascii4 word5")
101    nullstr = b"hello" + (16 - len("hello")) * b"\x00"
102
103    t = TestStruct((2, nullstr, 12345, nullstr, 33210))
104    expected = ("TestStruct(byte1=2, string2=68656c6c6f0000000000000000000000,"
105                " int3=12345, ascii4=hello, word5=33210)")
106    self.assertEqual(expected, str(t))
107
108    embeddednull = b"hello\x00visible123"
109    t = TestStruct((2, embeddednull, 12345, embeddednull, 33210))
110    expected = ("TestStruct(byte1=2, string2=68656c6c6f0076697369626c65313233,"
111                " int3=12345, ascii4=hello\x00visible123, word5=33210)")
112    self.assertEqual(expected, str(t))
113
114    embedded_non_ascii = b"hello\xc0visible123"
115    t = TestStruct((2, embedded_non_ascii, 12345, embeddednull, 33210))
116    expected = ("TestStruct(byte1=2, string2=68656c6c6fc076697369626c65313233,"
117                " int3=12345, ascii4=hello\x00visible123, word5=33210)")
118    self.assertEqual(expected, str(t))
119
120  def testZeroInitialization(self):
121    TestStruct = cstruct.Struct("TestStruct", "B16si16AH",
122                                "byte1 string2 int3 ascii4 word5")
123    t = TestStruct()
124    self.assertEqual(0, t.byte1)
125    self.assertEqual(b"\x00" * 16, t.string2)
126    self.assertEqual(0, t.int3)
127    self.assertEqual(b"\x00" * 16, t.ascii4)
128    self.assertEqual(0, t.word5)
129    self.assertEqual(b"\x00" * len(TestStruct), t.Pack())
130
131  def testKeywordInitialization(self):
132    TestStruct = cstruct.Struct("TestStruct", "=B16sIH",
133                                "byte1 string2 int3 word4")
134    bytes = b"hello world! ^_^"
135    hex_bytes = binascii.hexlify(bytes)
136
137    # Populate all fields
138    t1 = TestStruct(byte1=1, string2=bytes, int3=0xFEDCBA98, word4=0x1234)
139    expected = binascii.unhexlify(b"01" + hex_bytes + b"98BADCFE" b"3412")
140    self.assertEqual(expected, t1.Pack())
141
142    # Partially populated
143    t1 = TestStruct(string2=bytes, word4=0x1234)
144    expected = binascii.unhexlify(b"00" + hex_bytes + b"00000000" b"3412")
145    self.assertEqual(expected, t1.Pack())
146
147  def testCstructOffset(self):
148    TestStruct = cstruct.Struct("TestStruct", "B16si16AH",
149                                "byte1 string2 int3 ascii4 word5")
150    nullstr = b"hello" + (16 - len("hello")) * b"\x00"
151    t = TestStruct((2, nullstr, 12345, nullstr, 33210))
152    self.assertEqual(0, t.offset("byte1"))
153    self.assertEqual(1, t.offset("string2"))  # sizeof(byte)
154    self.assertEqual(17, t.offset("int3"))    # sizeof(byte) + 16*sizeof(char)
155    # The integer is automatically padded by the struct module
156    # to match native alignment.
157    # offset = sizeof(byte) + 16*sizeof(char) + padding + sizeof(int)
158    self.assertEqual(24, t.offset("ascii4"))
159    self.assertEqual(40, t.offset("word5"))
160    self.assertRaises(KeyError, t.offset, "random")
161
162    # TODO: Add support for nested struct offset
163    Nested = cstruct.Struct("Nested", "!HSSi", "word1 nest2 nest3 int4",
164                            [TestStructA, TestStructB])
165    DoubleNested = cstruct.Struct("DoubleNested", "SSB", "nest1 nest2 byte3",
166                                  [TestStructA, Nested])
167    d = DoubleNested((TestStructA((1, 2)), Nested((5, TestStructA((3, 4)),
168                                                   TestStructB((7, 8)), 9)), 6))
169    self.assertEqual(0, d.offset("nest1"))
170    self.assertEqual(len(TestStructA), d.offset("nest2"))
171    self.assertEqual(len(TestStructA) + len(Nested), d.offset("byte3"))
172    self.assertRaises(KeyError, t.offset, "word1")
173
174  def testDefinitionFieldMismatch(self):
175    cstruct.Struct("TestA", "=BI", "byte1 int2")
176    cstruct.Struct("TestA", "=BxxxxxIx", "byte1 int2")
177    with self.assertRaises(ValueError):
178      cstruct.Struct("TestA", "=B", "byte1 int2")
179    with self.assertRaises(ValueError):
180      cstruct.Struct("TestA", "=BI", "byte1")
181
182    Nested = cstruct.Struct("Nested", "!II", "int1 int2")
183    cstruct.Struct("TestB", "=BSI", "byte1 nest2 int3", [Nested])
184    cstruct.Struct("TestB", "=BxSxIx", "byte1 nest2 int3", [Nested])
185    with self.assertRaises(ValueError):
186      cstruct.Struct("TestB", "=BSI", "byte1 int3", [Nested])
187    with self.assertRaises(ValueError):
188      cstruct.Struct("TestB", "=BSI", "byte1 nest2", [Nested])
189
190    cstruct.Struct("TestC", "=BSSI", "byte1 nest2 nest3 int4", [Nested, Nested])
191    with self.assertRaises(ValueError):
192      cstruct.Struct("TestC", "=BSSI", "byte1 nest2 int4", [Nested, Nested])
193
194
195if __name__ == "__main__":
196  unittest.main()
197