1#!/usr/bin/python 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 unittest 18 19import cstruct 20 21 22# These aren't constants, they're classes. So, pylint: disable=invalid-name 23TestStructA = cstruct.Struct("TestStructA", "=BI", "byte1 int2") 24TestStructB = cstruct.Struct("TestStructB", "=BI", "byte1 int2") 25 26 27class CstructTest(unittest.TestCase): 28 29 def CheckEquals(self, a, b): 30 self.assertEquals(a, b) 31 self.assertEquals(b, a) 32 assert a == b 33 assert b == a 34 assert not (a != b) # pylint: disable=g-comparison-negation,superfluous-parens 35 assert not (b != a) # pylint: disable=g-comparison-negation,superfluous-parens 36 37 def CheckNotEquals(self, a, b): 38 self.assertNotEquals(a, b) 39 self.assertNotEquals(b, a) 40 assert a != b 41 assert b != a 42 assert not (a == b) # pylint: disable=g-comparison-negation,superfluous-parens 43 assert not (b == a) # pylint: disable=g-comparison-negation,superfluous-parens 44 45 def testEqAndNe(self): 46 a1 = TestStructA((1, 2)) 47 a2 = TestStructA((2, 3)) 48 a3 = TestStructA((1, 2)) 49 b = TestStructB((1, 2)) 50 self.CheckNotEquals(a1, b) 51 self.CheckNotEquals(a2, b) 52 self.CheckNotEquals(a1, a2) 53 self.CheckNotEquals(a2, a3) 54 for i in [a1, a2, a3, b]: 55 self.CheckEquals(i, i) 56 self.CheckEquals(a1, a3) 57 58 def testNestedStructs(self): 59 Nested = cstruct.Struct("Nested", "!HSSi", 60 "word1 nest2 nest3 int4", 61 [TestStructA, TestStructB]) 62 DoubleNested = cstruct.Struct("DoubleNested", "SSB", 63 "nest1 nest2 byte3", 64 [TestStructA, Nested]) 65 d = DoubleNested((TestStructA((1, 2)), 66 Nested((5, TestStructA((3, 4)), TestStructB((7, 8)), 9)), 67 6)) 68 69 expectedlen = (len(TestStructA) + 70 2 + len(TestStructA) + len(TestStructB) + 4 + 71 1) 72 self.assertEquals(expectedlen, len(DoubleNested)) 73 74 self.assertEquals(7, d.nest2.nest3.byte1) 75 76 d.byte3 = 252 77 d.nest2.word1 = 33214 78 n = d.nest2 79 n.int4 = -55 80 t = n.nest3 81 t.int2 = 33627591 82 83 self.assertEquals(33627591, d.nest2.nest3.int2) 84 85 expected = ( 86 "DoubleNested(nest1=TestStructA(byte1=1, int2=2)," 87 " nest2=Nested(word1=33214, nest2=TestStructA(byte1=3, int2=4)," 88 " nest3=TestStructB(byte1=7, int2=33627591), int4=-55), byte3=252)") 89 self.assertEquals(expected, str(d)) 90 expected = ("01" "02000000" 91 "81be" "03" "04000000" 92 "07" "c71d0102" "ffffffc9" "fc").decode("hex") 93 self.assertEquals(expected, d.Pack()) 94 unpacked = DoubleNested(expected) 95 self.CheckEquals(unpacked, d) 96 97 def testNullTerminatedStrings(self): 98 TestStruct = cstruct.Struct("TestStruct", "B16si16AH", 99 "byte1 string2 int3 ascii4 word5") 100 nullstr = "hello" + (16 - len("hello")) * "\x00" 101 102 t = TestStruct((2, nullstr, 12345, nullstr, 33210)) 103 expected = ("TestStruct(byte1=2, string2=68656c6c6f0000000000000000000000," 104 " int3=12345, ascii4=hello, word5=33210)") 105 self.assertEquals(expected, str(t)) 106 107 embeddednull = "hello\x00visible123" 108 t = TestStruct((2, embeddednull, 12345, embeddednull, 33210)) 109 expected = ("TestStruct(byte1=2, string2=68656c6c6f0076697369626c65313233," 110 " int3=12345, ascii4=hello\x00visible123, word5=33210)") 111 self.assertEquals(expected, str(t)) 112 113 def testZeroInitialization(self): 114 TestStruct = cstruct.Struct("TestStruct", "B16si16AH", 115 "byte1 string2 int3 ascii4 word5") 116 t = TestStruct() 117 self.assertEquals(0, t.byte1) 118 self.assertEquals("\x00" * 16, t.string2) 119 self.assertEquals(0, t.int3) 120 self.assertEquals("\x00" * 16, t.ascii4) 121 self.assertEquals(0, t.word5) 122 self.assertEquals("\x00" * len(TestStruct), t.Pack()) 123 124 def testKeywordInitialization(self): 125 TestStruct = cstruct.Struct("TestStruct", "=B16sIH", 126 "byte1 string2 int3 word4") 127 text = "hello world! ^_^" 128 text_bytes = text.encode("hex") 129 130 # Populate all fields 131 t1 = TestStruct(byte1=1, string2=text, int3=0xFEDCBA98, word4=0x1234) 132 expected = ("01" + text_bytes + "98BADCFE" "3412").decode("hex") 133 self.assertEquals(expected, t1.Pack()) 134 135 # Partially populated 136 t1 = TestStruct(string2=text, word4=0x1234) 137 expected = ("00" + text_bytes + "00000000" "3412").decode("hex") 138 self.assertEquals(expected, t1.Pack()) 139 140 def testCstructOffset(self): 141 TestStruct = cstruct.Struct("TestStruct", "B16si16AH", 142 "byte1 string2 int3 ascii4 word5") 143 nullstr = "hello" + (16 - len("hello")) * "\x00" 144 t = TestStruct((2, nullstr, 12345, nullstr, 33210)) 145 self.assertEquals(0, t.offset("byte1")) 146 self.assertEquals(1, t.offset("string2")) # sizeof(byte) 147 self.assertEquals(17, t.offset("int3")) # sizeof(byte) + 16*sizeof(char) 148 # The integer is automatically padded by the struct module 149 # to match native alignment. 150 # offset = sizeof(byte) + 16*sizeof(char) + padding + sizeof(int) 151 self.assertEquals(24, t.offset("ascii4")) 152 self.assertEquals(40, t.offset("word5")) 153 self.assertRaises(KeyError, t.offset, "random") 154 155 # TODO: Add support for nested struct offset 156 Nested = cstruct.Struct("Nested", "!HSSi", "word1 nest2 nest3 int4", 157 [TestStructA, TestStructB]) 158 DoubleNested = cstruct.Struct("DoubleNested", "SSB", "nest1 nest2 byte3", 159 [TestStructA, Nested]) 160 d = DoubleNested((TestStructA((1, 2)), Nested((5, TestStructA((3, 4)), 161 TestStructB((7, 8)), 9)), 6)) 162 self.assertEqual(0, d.offset("nest1")) 163 self.assertEqual(len(TestStructA), d.offset("nest2")) 164 self.assertEqual(len(TestStructA) + len(Nested), d.offset("byte3")) 165 self.assertRaises(KeyError, t.offset, "word1") 166 167 def testDefinitionFieldMismatch(self): 168 cstruct.Struct("TestA", "=BI", "byte1 int2") 169 cstruct.Struct("TestA", "=BxxxxxIx", "byte1 int2") 170 with self.assertRaises(ValueError): 171 cstruct.Struct("TestA", "=B", "byte1 int2") 172 with self.assertRaises(ValueError): 173 cstruct.Struct("TestA", "=BI", "byte1") 174 175 Nested = cstruct.Struct("Nested", "!II", "int1 int2") 176 cstruct.Struct("TestB", "=BSI", "byte1 nest2 int3", [Nested]) 177 cstruct.Struct("TestB", "=BxSxIx", "byte1 nest2 int3", [Nested]) 178 with self.assertRaises(ValueError): 179 cstruct.Struct("TestB", "=BSI", "byte1 int3", [Nested]) 180 with self.assertRaises(ValueError): 181 cstruct.Struct("TestB", "=BSI", "byte1 nest2", [Nested]) 182 183 cstruct.Struct("TestC", "=BSSI", "byte1 nest2 nest3 int4", [Nested, Nested]) 184 with self.assertRaises(ValueError): 185 cstruct.Struct("TestC", "=BSSI", "byte1 nest2 int4", [Nested, Nested]) 186 187 188if __name__ == "__main__": 189 unittest.main() 190