Nothing to see here, move along
1use lancer_core::header::{KernelObjectHeader, NONE_SENTINEL};
2use lancer_core::object_layout::*;
3use lancer_core::object_tag::ObjectTag;
4use lancer_core::untyped::UntypedState;
5
6#[test]
7fn header_size_is_32() {
8 assert_eq!(core::mem::size_of::<KernelObjectHeader>(), 32);
9}
10
11#[test]
12fn all_fixed_objects_are_64_bytes() {
13 assert_eq!(core::mem::size_of::<EndpointObject>(), 64);
14 assert_eq!(core::mem::size_of::<NotificationObject>(), 64);
15 assert_eq!(core::mem::size_of::<SchedContextObject>(), 64);
16 assert_eq!(core::mem::size_of::<UntypedObject>(), 64);
17 assert_eq!(core::mem::size_of::<IrqHandlerObject>(), 64);
18 assert_eq!(core::mem::size_of::<FramebufferObject>(), 64);
19 assert_eq!(core::mem::size_of::<PciDeviceObject>(), 64);
20 assert_eq!(core::mem::size_of::<ProcessObject>(), 64);
21 assert_eq!(core::mem::size_of::<FrameObject>(), 64);
22 assert_eq!(core::mem::size_of::<CNodeObject>(), 64);
23}
24
25#[test]
26fn header_field_offsets() {
27 assert_eq!(core::mem::offset_of!(KernelObjectHeader, tag), 0);
28 assert_eq!(core::mem::offset_of!(KernelObjectHeader, generation), 4);
29 assert_eq!(core::mem::offset_of!(KernelObjectHeader, ref_count), 8);
30 assert_eq!(core::mem::offset_of!(KernelObjectHeader, parent_untyped), 12);
31 assert_eq!(core::mem::offset_of!(KernelObjectHeader, next_child), 14);
32 assert_eq!(core::mem::offset_of!(KernelObjectHeader, object_size), 16);
33}
34
35#[test]
36fn data_fields_start_at_32() {
37 assert_eq!(core::mem::offset_of!(EndpointObject, sender_head), 32);
38 assert_eq!(core::mem::offset_of!(NotificationObject, word), 32);
39 assert_eq!(core::mem::offset_of!(SchedContextObject, budget_us), 32);
40 assert_eq!(core::mem::offset_of!(UntypedObject, phys_base), 32);
41 assert_eq!(core::mem::offset_of!(IrqHandlerObject, vector), 32);
42 assert_eq!(core::mem::offset_of!(FramebufferObject, phys_addr), 32);
43 assert_eq!(core::mem::offset_of!(PciDeviceObject, device_table_idx), 32);
44 assert_eq!(core::mem::offset_of!(ProcessObject, pid), 32);
45 assert_eq!(core::mem::offset_of!(FrameObject, phys_addr), 32);
46 assert_eq!(core::mem::offset_of!(CNodeObject, slots_phys), 32);
47}
48
49#[test]
50fn init_default_sets_tag_and_refcount() {
51 let header = KernelObjectHeader::new(ObjectTag::Endpoint, 42, 64);
52 let obj = EndpointObject::init_default(header);
53 assert_eq!(obj.header.tag_byte(), ObjectTag::Endpoint as u8);
54 assert_eq!(obj.header.ref_count(), 1);
55 assert_eq!(obj.header.generation(), 42);
56 assert_eq!(obj.header.object_size(), 64);
57 assert_eq!(obj.sender_head, NONE_SENTINEL);
58 assert_eq!(obj.sender_tail, NONE_SENTINEL);
59 assert_eq!(obj.sender_len, 0);
60 assert_eq!(obj.receiver_head, NONE_SENTINEL);
61 assert_eq!(obj.receiver_tail, NONE_SENTINEL);
62 assert_eq!(obj.receiver_len, 0);
63 assert_eq!(obj.holder, NONE_SENTINEL);
64}
65
66#[test]
67fn init_default_notification() {
68 let header = KernelObjectHeader::new(ObjectTag::Notification, 7, 64);
69 let obj = NotificationObject::init_default(header);
70 assert_eq!(obj.header.tag_byte(), ObjectTag::Notification as u8);
71 assert_eq!(obj.word, 0);
72 assert_eq!(obj.waiter_count, 0);
73 obj.waiters.iter().for_each(|w| assert_eq!(*w, 0xFFFF));
74}
75
76#[test]
77fn init_default_sched_context() {
78 let header = KernelObjectHeader::new(ObjectTag::SchedContext, 0, 64);
79 let obj = SchedContextObject::init_default(header);
80 assert_eq!(obj.budget_us, 0);
81 assert_eq!(obj.period_us, 0);
82 assert_eq!(obj.remaining_us, 0);
83 assert_eq!(obj.replenish_remaining_us, 0);
84 assert_eq!(obj.attached_pid, 0xFFFF);
85 assert_eq!(obj.priority, 0);
86 assert_eq!(obj.flags, 0);
87}
88
89#[test]
90fn header_parent_and_child_default_to_none() {
91 let header = KernelObjectHeader::new(ObjectTag::Endpoint, 0, 64);
92 assert_eq!(header.parent_untyped(), NONE_SENTINEL);
93 assert_eq!(header.next_child(), NONE_SENTINEL);
94}
95
96#[test]
97fn inc_dec_ref_roundtrip() {
98 let mut header = KernelObjectHeader::new(ObjectTag::Endpoint, 0, 64);
99 assert_eq!(header.ref_count(), 1);
100 assert_eq!(header.inc_ref(), Some(2));
101 assert_eq!(header.ref_count(), 2);
102 assert_eq!(header.inc_ref(), Some(3));
103 assert_eq!(header.ref_count(), 3);
104 let remaining = header.dec_ref();
105 assert_eq!(remaining, 2);
106 let remaining = header.dec_ref();
107 assert_eq!(remaining, 1);
108 let remaining = header.dec_ref();
109 assert_eq!(remaining, 0);
110}
111
112#[test]
113fn ref_count_saturates() {
114 let mut header = KernelObjectHeader::new(ObjectTag::Endpoint, 0, 64);
115 let remaining = header.dec_ref();
116 assert_eq!(remaining, 0);
117 let remaining = header.dec_ref();
118 assert_eq!(remaining, 0);
119}
120
121#[test]
122fn inc_ref_returns_none_at_max() {
123 let mut header = KernelObjectHeader::new(ObjectTag::Endpoint, 0, 64);
124 header.ref_count = u32::MAX;
125 assert_eq!(header.inc_ref(), None);
126 assert_eq!(header.ref_count(), u32::MAX);
127}
128
129#[test]
130fn inc_ref_sequential() {
131 let mut header = KernelObjectHeader::new(ObjectTag::Endpoint, 0, 64);
132 (0..10u32).for_each(|_| { assert!(header.inc_ref().is_some()); });
133 assert_eq!(header.ref_count(), 11);
134}
135
136#[test]
137fn is_stale_correctness() {
138 let header = KernelObjectHeader::new(ObjectTag::Frame, 100, 64);
139 assert!(!header.is_stale(100));
140 assert!(header.is_stale(99));
141 assert!(header.is_stale(101));
142}
143
144#[test]
145fn set_parent_and_next_child() {
146 let mut header = KernelObjectHeader::new(ObjectTag::Endpoint, 0, 64);
147 header.set_parent_untyped(42);
148 header.set_next_child(99);
149 assert_eq!(header.parent_untyped(), 42);
150 assert_eq!(header.next_child(), 99);
151}
152
153#[test]
154fn untyped_state_roundtrip() {
155 let state = UntypedState::from_parts(1024, 20, true, 5);
156 let header = KernelObjectHeader::new(ObjectTag::Untyped, 0, 64);
157 let mut obj = UntypedObject::init_default(header);
158 obj.apply_state(&state);
159
160 let recovered = obj.to_state();
161 assert_eq!(recovered.watermark(), 1024);
162 assert_eq!(recovered.size_bits(), 20);
163 assert!(recovered.is_device());
164 assert_eq!(recovered.child_count(), 5);
165}
166
167#[test]
168fn untyped_state_roundtrip_non_device() {
169 let state = UntypedState::new(15, false);
170 let header = KernelObjectHeader::new(ObjectTag::Untyped, 0, 64);
171 let mut obj = UntypedObject::init_default(header);
172 obj.apply_state(&state);
173
174 let recovered = obj.to_state();
175 assert_eq!(recovered.watermark(), 0);
176 assert_eq!(recovered.size_bits(), 15);
177 assert!(!recovered.is_device());
178 assert_eq!(recovered.child_count(), 0);
179}
180
181#[test]
182fn kernel_object_trait_constants() {
183 assert_eq!(EndpointObject::METADATA_SIZE, 64);
184 assert_eq!(EndpointObject::METADATA_ALIGN, 64);
185 assert_eq!(EndpointObject::TAG, ObjectTag::Endpoint);
186
187 assert_eq!(CNodeObject::TAG, ObjectTag::CNode);
188 assert_eq!(FrameObject::TAG, ObjectTag::Frame);
189 assert_eq!(UntypedObject::TAG, ObjectTag::Untyped);
190 assert_eq!(SchedContextObject::TAG, ObjectTag::SchedContext);
191}
192
193#[test]
194fn frame_object_default_size_bits() {
195 let header = KernelObjectHeader::new(ObjectTag::Frame, 0, 64);
196 let obj = FrameObject::init_default(header);
197 assert_eq!(obj.size_bits, 12);
198 assert_eq!(obj.phys_addr, 0);
199}
200
201#[test]
202fn cnode_object_default() {
203 let header = KernelObjectHeader::new(ObjectTag::CNode, 0, 64);
204 let obj = CNodeObject::init_default(header);
205 assert_eq!(obj.slots_phys, 0);
206 assert_eq!(obj.size_bits, 0);
207 assert_eq!(obj.frame_count, 0);
208}
209
210#[test]
211fn init_default_tag_matches_trait_tag_all_types() {
212 fn check<T: KernelObject>() {
213 let header = KernelObjectHeader::new(T::TAG, 0, T::METADATA_SIZE as u16);
214 let obj = T::init_default(header);
215 let header_ref: &KernelObjectHeader =
216 unsafe { &*((&obj as *const T).cast::<KernelObjectHeader>()) };
217 assert_eq!(header_ref.tag_byte(), T::TAG as u8);
218 assert_eq!(header_ref.object_size(), T::METADATA_SIZE as u16);
219 }
220 check::<EndpointObject>();
221 check::<NotificationObject>();
222 check::<SchedContextObject>();
223 check::<UntypedObject>();
224 check::<IrqHandlerObject>();
225 check::<FramebufferObject>();
226 check::<PciDeviceObject>();
227 check::<ProcessObject>();
228 check::<FrameObject>();
229 check::<CNodeObject>();
230}
231
232use proptest::prelude::*;
233
234proptest! {
235 #[test]
236 fn arbitrary_generation_header(generation in proptest::num::u32::ANY) {
237 let header = KernelObjectHeader::new(ObjectTag::Notification, generation, 64);
238 prop_assert_eq!(header.generation(), generation);
239 prop_assert!(!header.is_stale(generation));
240 prop_assert!(header.is_stale(generation.wrapping_add(1)));
241 }
242
243 #[test]
244 fn ref_count_sequence(ops in proptest::collection::vec(proptest::bool::ANY, 0..50)) {
245 let mut header = KernelObjectHeader::new(ObjectTag::Endpoint, 0, 64);
246 let mut expected: u32 = 1;
247 ops.iter().for_each(|inc| {
248 match *inc {
249 true => match expected.checked_add(1) {
250 Some(next) => {
251 assert_eq!(header.inc_ref(), Some(next));
252 expected = next;
253 }
254 None => {
255 assert_eq!(header.inc_ref(), None);
256 }
257 },
258 false => {
259 header.dec_ref();
260 expected = expected.saturating_sub(1);
261 }
262 }
263 assert_eq!(header.ref_count(), expected);
264 });
265 }
266}