Skip to content

Commit 9aeae00

Browse files
committed
test: add bitfield tests
1 parent 54ef86a commit 9aeae00

1 file changed

Lines changed: 143 additions & 0 deletions

File tree

test/scripts/bitfield_unit_test.py

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
#
2+
# This file is part of libdestruct (https://github.com/mrindeciso/libdestruct).
3+
# Copyright (c) 2026 Roberto Alessandro Bertolini. All rights reserved.
4+
# Licensed under the MIT license. See LICENSE file in the project root for details.
5+
#
6+
7+
import unittest
8+
9+
from libdestruct import c_int, c_uint, c_long, struct, bitfield_of
10+
from libdestruct.c.struct_parser import definition_to_type
11+
12+
13+
class BitfieldReadTest(unittest.TestCase):
14+
"""Bitfield read operations."""
15+
16+
def test_single_bitfield_read(self):
17+
class test_t(struct):
18+
flags: c_uint = bitfield_of(c_uint, 3)
19+
20+
# 0b00000101 = 5, low 3 bits = 5
21+
memory = (0b00000101).to_bytes(4, "little")
22+
test = test_t.from_bytes(memory)
23+
self.assertEqual(test.flags.value, 5)
24+
25+
def test_multiple_bitfields_packing(self):
26+
class test_t(struct):
27+
a: c_uint = bitfield_of(c_uint, 3)
28+
b: c_uint = bitfield_of(c_uint, 5)
29+
30+
# a uses bits 0-2, b uses bits 3-7
31+
# a=5 (0b101), b=10 (0b01010) -> combined: 0b01010_101 = 0x55
32+
memory = (0b01010_101).to_bytes(4, "little")
33+
test = test_t.from_bytes(memory)
34+
self.assertEqual(test.a.value, 5)
35+
self.assertEqual(test.b.value, 10)
36+
37+
# Struct should be 4 bytes total (both share one c_uint)
38+
self.assertEqual(test.to_bytes(), memory)
39+
40+
def test_bitfield_signed(self):
41+
class test_t(struct):
42+
val: c_int = bitfield_of(c_int, 4)
43+
44+
# 4-bit signed: 0b1111 = -1
45+
memory = (0b1111).to_bytes(4, "little")
46+
test = test_t.from_bytes(memory)
47+
self.assertEqual(test.val.value, -1)
48+
49+
# 4-bit signed: 0b0111 = 7
50+
memory2 = (0b0111).to_bytes(4, "little")
51+
test2 = test_t.from_bytes(memory2)
52+
self.assertEqual(test2.val.value, 7)
53+
54+
def test_bitfield_full_width(self):
55+
class test_t(struct):
56+
val: c_uint = bitfield_of(c_uint, 32)
57+
58+
memory = (0xDEADBEEF).to_bytes(4, "little")
59+
test = test_t.from_bytes(memory)
60+
self.assertEqual(test.val.value, 0xDEADBEEF)
61+
62+
63+
class BitfieldWriteTest(unittest.TestCase):
64+
"""Bitfield write operations."""
65+
66+
def test_bitfield_write(self):
67+
class test_t(struct):
68+
a: c_uint = bitfield_of(c_uint, 3)
69+
b: c_uint = bitfield_of(c_uint, 5)
70+
71+
memory = bytearray(4)
72+
from libdestruct import inflater
73+
lib = inflater(memory)
74+
test = lib.inflate(test_t, 0)
75+
76+
test.a.value = 7 # 0b111
77+
test.b.value = 15 # 0b01111
78+
79+
self.assertEqual(test.a.value, 7)
80+
self.assertEqual(test.b.value, 15)
81+
82+
# Verify only relevant bits changed
83+
raw = int.from_bytes(memory[:4], "little")
84+
self.assertEqual(raw & 0b111, 7) # bits 0-2
85+
self.assertEqual((raw >> 3) & 0b11111, 15) # bits 3-7
86+
87+
88+
class BitfieldRoundTripTest(unittest.TestCase):
89+
"""Bitfield serialization."""
90+
91+
def test_bitfield_round_trip(self):
92+
class test_t(struct):
93+
a: c_uint = bitfield_of(c_uint, 3)
94+
b: c_uint = bitfield_of(c_uint, 5)
95+
c: c_int
96+
97+
# a=3, b=10, c=42
98+
# a=0b011, b=0b01010 -> byte 0-3: 0b01010_011 = 0x53
99+
val = 0b01010_011
100+
memory = val.to_bytes(4, "little") + (42).to_bytes(4, "little")
101+
102+
test = test_t.from_bytes(memory)
103+
self.assertEqual(test.a.value, 3)
104+
self.assertEqual(test.b.value, 10)
105+
self.assertEqual(test.c.value, 42)
106+
107+
self.assertEqual(test.to_bytes(), memory)
108+
109+
110+
class BitfieldBackingTypeTest(unittest.TestCase):
111+
"""Bitfield backing type transitions."""
112+
113+
def test_bitfield_backing_type_change(self):
114+
class test_t(struct):
115+
a: c_uint = bitfield_of(c_uint, 3)
116+
b: c_uint = bitfield_of(c_uint, 5)
117+
# Different backing type -> new group
118+
c: c_long = bitfield_of(c_long, 16)
119+
120+
# a+b share 4 bytes (c_uint), c uses 8 bytes (c_long)
121+
# Total = 12 bytes
122+
memory = b"\x00" * 12
123+
test = test_t.from_bytes(memory)
124+
self.assertEqual(len(test.to_bytes()), 12)
125+
126+
127+
class BitfieldCParserTest(unittest.TestCase):
128+
"""C parser bitfield support."""
129+
130+
def test_bitfield_c_parser(self):
131+
t = definition_to_type("struct test { unsigned int flags:3; unsigned int reserved:5; };")
132+
self.assertIn("flags", t.__annotations__)
133+
self.assertIn("reserved", t.__annotations__)
134+
135+
# Inflate and verify
136+
memory = (0b01010_101).to_bytes(4, "little")
137+
test = t.from_bytes(memory)
138+
self.assertEqual(test.flags.value, 5)
139+
self.assertEqual(test.reserved.value, 10)
140+
141+
142+
if __name__ == "__main__":
143+
unittest.main()

0 commit comments

Comments
 (0)