Nothing to see here, move along meow
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_24() {
8 assert_eq!(core::mem::size_of::<KernelObjectHeader>(), 24);
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 assert_eq!(core::mem::size_of::<VRegionObject>(), 64);
24}
25
26#[test]
27fn header_field_offsets() {
28 assert_eq!(core::mem::offset_of!(KernelObjectHeader, tag), 0);
29 assert_eq!(core::mem::offset_of!(KernelObjectHeader, generation), 4);
30 assert_eq!(core::mem::offset_of!(KernelObjectHeader, ref_count), 8);
31 assert_eq!(
32 core::mem::offset_of!(KernelObjectHeader, parent_untyped),
33 12
34 );
35 assert_eq!(core::mem::offset_of!(KernelObjectHeader, next_child), 16);
36 assert_eq!(core::mem::offset_of!(KernelObjectHeader, object_size), 20);
37}
38
39#[test]
40fn data_fields_start_after_header() {
41 assert_eq!(core::mem::offset_of!(EndpointObject, sender_head), 24);
42 assert_eq!(core::mem::offset_of!(NotificationObject, word), 24);
43 assert_eq!(core::mem::offset_of!(SchedContextObject, replenish_at), 24);
44 assert_eq!(core::mem::offset_of!(UntypedObject, phys_base), 24);
45 assert_eq!(core::mem::offset_of!(IrqHandlerObject, vector), 24);
46 assert_eq!(core::mem::offset_of!(FramebufferObject, phys_addr), 24);
47 assert_eq!(core::mem::offset_of!(PciDeviceObject, device_table_idx), 24);
48 assert_eq!(core::mem::offset_of!(ProcessObject, pid), 24);
49 assert_eq!(core::mem::offset_of!(FrameObject, phys_addr), 24);
50 assert_eq!(core::mem::offset_of!(CNodeObject, slots_phys), 24);
51 assert_eq!(core::mem::offset_of!(VRegionObject, phys_base), 24);
52}
53
54#[test]
55fn init_default_sets_tag_and_refcount() {
56 let header = KernelObjectHeader::new(ObjectTag::Endpoint, 42, 64);
57 let obj = EndpointObject::init_default(header);
58 assert_eq!(obj.header.tag_byte(), ObjectTag::Endpoint as u8);
59 assert_eq!(obj.header.ref_count(), 1);
60 assert_eq!(obj.header.generation(), 42);
61 assert_eq!(obj.header.object_size(), 64);
62 assert_eq!(obj.sender_head, NONE_SENTINEL);
63 assert_eq!(obj.sender_tail, NONE_SENTINEL);
64 assert_eq!(obj.sender_len, 0);
65 assert_eq!(obj.receiver_head, NONE_SENTINEL);
66 assert_eq!(obj.receiver_tail, NONE_SENTINEL);
67 assert_eq!(obj.receiver_len, 0);
68 assert_eq!(obj.holder, NONE_SENTINEL);
69}
70
71#[test]
72fn init_default_notification() {
73 let header = KernelObjectHeader::new(ObjectTag::Notification, 7, 64);
74 let obj = NotificationObject::init_default(header);
75 assert_eq!(obj.header.tag_byte(), ObjectTag::Notification as u8);
76 assert_eq!(obj.word, 0);
77 assert_eq!(obj.waiter_count, 0);
78 obj.waiters
79 .iter()
80 .for_each(|w| assert_eq!(*w, NONE_SENTINEL));
81}
82
83#[test]
84fn init_default_sched_context() {
85 let header = KernelObjectHeader::new(ObjectTag::SchedContext, 0, 64);
86 let obj = SchedContextObject::init_default(header);
87 assert_eq!(obj.budget_us, 0);
88 assert_eq!(obj.period_us, 0);
89 assert_eq!(obj.remaining_us, 0);
90 assert_eq!(obj.replenish_at, 0);
91 assert_eq!(obj.attached_pid, NONE_SENTINEL);
92 assert_eq!(obj.priority, 0);
93 assert_eq!(obj.flags, 0);
94}
95
96#[test]
97fn header_parent_and_child_default_to_none() {
98 let header = KernelObjectHeader::new(ObjectTag::Endpoint, 0, 64);
99 assert_eq!(header.parent_untyped(), NONE_SENTINEL);
100 assert_eq!(header.next_child(), NONE_SENTINEL);
101}
102
103#[test]
104fn inc_dec_ref_roundtrip() {
105 let mut header = KernelObjectHeader::new(ObjectTag::Endpoint, 0, 64);
106 assert_eq!(header.ref_count(), 1);
107 assert_eq!(header.inc_ref(), Some(2));
108 assert_eq!(header.ref_count(), 2);
109 assert_eq!(header.inc_ref(), Some(3));
110 assert_eq!(header.ref_count(), 3);
111 let remaining = header.dec_ref();
112 assert_eq!(remaining, 2);
113 let remaining = header.dec_ref();
114 assert_eq!(remaining, 1);
115 let remaining = header.dec_ref();
116 assert_eq!(remaining, 0);
117}
118
119#[test]
120fn ref_count_saturates() {
121 let mut header = KernelObjectHeader::new(ObjectTag::Endpoint, 0, 64);
122 let remaining = header.dec_ref();
123 assert_eq!(remaining, 0);
124 let remaining = header.dec_ref();
125 assert_eq!(remaining, 0);
126}
127
128#[test]
129fn inc_ref_returns_none_at_max() {
130 let mut header = KernelObjectHeader::new(ObjectTag::Endpoint, 0, 64);
131 header.ref_count = u32::MAX;
132 assert_eq!(header.inc_ref(), None);
133 assert_eq!(header.ref_count(), u32::MAX);
134}
135
136#[test]
137fn inc_ref_sequential() {
138 let mut header = KernelObjectHeader::new(ObjectTag::Endpoint, 0, 64);
139 (0..10u32).for_each(|_| {
140 assert!(header.inc_ref().is_some());
141 });
142 assert_eq!(header.ref_count(), 11);
143}
144
145#[test]
146fn is_stale_correctness() {
147 let header = KernelObjectHeader::new(ObjectTag::Frame, 100, 64);
148 assert!(!header.is_stale(100));
149 assert!(header.is_stale(99));
150 assert!(header.is_stale(101));
151}
152
153#[test]
154fn set_parent_and_next_child() {
155 let mut header = KernelObjectHeader::new(ObjectTag::Endpoint, 0, 64);
156 header.set_parent_untyped(42);
157 header.set_next_child(99);
158 assert_eq!(header.parent_untyped(), 42);
159 assert_eq!(header.next_child(), 99);
160}
161
162#[test]
163fn untyped_state_roundtrip() {
164 let state = UntypedState::from_parts(1024, 20, true, 5);
165 let header = KernelObjectHeader::new(ObjectTag::Untyped, 0, 64);
166 let mut obj = UntypedObject::init_default(header);
167 obj.apply_state(&state);
168
169 let recovered = obj.to_state();
170 assert_eq!(recovered.watermark(), 1024);
171 assert_eq!(recovered.size_bits(), 20);
172 assert!(recovered.is_device());
173 assert_eq!(recovered.child_count(), 5);
174}
175
176#[test]
177fn untyped_state_roundtrip_non_device() {
178 let state = UntypedState::new(15, false);
179 let header = KernelObjectHeader::new(ObjectTag::Untyped, 0, 64);
180 let mut obj = UntypedObject::init_default(header);
181 obj.apply_state(&state);
182
183 let recovered = obj.to_state();
184 assert_eq!(recovered.watermark(), 0);
185 assert_eq!(recovered.size_bits(), 15);
186 assert!(!recovered.is_device());
187 assert_eq!(recovered.child_count(), 0);
188}
189
190#[test]
191fn kernel_object_trait_constants() {
192 assert_eq!(EndpointObject::METADATA_SIZE, 64);
193 assert_eq!(EndpointObject::METADATA_ALIGN, 64);
194 assert_eq!(EndpointObject::TAG, ObjectTag::Endpoint);
195
196 assert_eq!(CNodeObject::TAG, ObjectTag::CNode);
197 assert_eq!(FrameObject::TAG, ObjectTag::Frame);
198 assert_eq!(UntypedObject::TAG, ObjectTag::Untyped);
199 assert_eq!(SchedContextObject::TAG, ObjectTag::SchedContext);
200 assert_eq!(VRegionObject::TAG, ObjectTag::VRegion);
201}
202
203#[test]
204fn frame_object_default_size_bits() {
205 let header = KernelObjectHeader::new(ObjectTag::Frame, 0, 64);
206 let obj = FrameObject::init_default(header);
207 assert_eq!(obj.size_bits, 12);
208 assert_eq!(obj.phys_addr, 0);
209}
210
211#[test]
212fn cnode_object_default() {
213 let header = KernelObjectHeader::new(ObjectTag::CNode, 0, 64);
214 let obj = CNodeObject::init_default(header);
215 assert_eq!(obj.slots_phys, 0);
216 assert_eq!(obj.size_bits, 0);
217 assert_eq!(obj.frame_count, 0);
218}
219
220#[test]
221fn init_default_tag_matches_trait_tag_all_types() {
222 fn check<T: KernelObject>() {
223 let header = KernelObjectHeader::new(T::TAG, 0, T::METADATA_SIZE as u16);
224 let obj = T::init_default(header);
225 let header_ref: &KernelObjectHeader =
226 unsafe { &*((&obj as *const T).cast::<KernelObjectHeader>()) };
227 assert_eq!(header_ref.tag_byte(), T::TAG as u8);
228 assert_eq!(header_ref.object_size(), T::METADATA_SIZE as u16);
229 }
230 check::<EndpointObject>();
231 check::<NotificationObject>();
232 check::<SchedContextObject>();
233 check::<UntypedObject>();
234 check::<IrqHandlerObject>();
235 check::<FramebufferObject>();
236 check::<PciDeviceObject>();
237 check::<ProcessObject>();
238 check::<FrameObject>();
239 check::<CNodeObject>();
240 check::<VRegionObject>();
241}
242
243#[test]
244fn vregion_object_field_offsets() {
245 assert_eq!(core::mem::offset_of!(VRegionObject, header), 0);
246 assert_eq!(core::mem::offset_of!(VRegionObject, phys_base), 24);
247 assert_eq!(core::mem::offset_of!(VRegionObject, owner_vaddr), 32);
248 assert_eq!(core::mem::offset_of!(VRegionObject, child_vaddr), 40);
249 assert_eq!(core::mem::offset_of!(VRegionObject, owner_pid), 48);
250 assert_eq!(core::mem::offset_of!(VRegionObject, child_pid), 52);
251 assert_eq!(core::mem::offset_of!(VRegionObject, page_count), 56);
252 assert_eq!(core::mem::offset_of!(VRegionObject, flags), 58);
253 assert_eq!(core::mem::offset_of!(VRegionObject, is_derived), 60);
254}
255
256#[test]
257fn vregion_object_init_default() {
258 let header = KernelObjectHeader::new(ObjectTag::VRegion, 0, 64);
259 let obj = VRegionObject::init_default(header);
260 assert_eq!(obj.header.tag_byte(), ObjectTag::VRegion as u8);
261 assert_eq!(obj.phys_base, 0);
262 assert_eq!(obj.owner_vaddr, 0);
263 assert_eq!(obj.child_vaddr, 0);
264 assert_eq!(obj.owner_pid, NONE_SENTINEL);
265 assert_eq!(obj.child_pid, NONE_SENTINEL);
266 assert_eq!(obj.page_count, 0);
267 assert_eq!(obj.flags.raw(), 0);
268 assert_eq!(obj.is_derived, 0);
269}
270
271#[test]
272fn vregion_flags_basic() {
273 use lancer_core::types::VRegionFlags;
274
275 let empty = VRegionFlags::try_new(0).unwrap();
276 assert!(!empty.writable());
277 assert!(!empty.executable());
278
279 let rw = VRegionFlags::try_new(VRegionFlags::WRITABLE).unwrap();
280 assert!(rw.writable());
281 assert!(!rw.executable());
282
283 let rx = VRegionFlags::try_new(VRegionFlags::EXECUTABLE).unwrap();
284 assert!(!rx.writable());
285 assert!(rx.executable());
286
287 let rwx = VRegionFlags::try_new(VRegionFlags::WRITABLE | VRegionFlags::EXECUTABLE).unwrap();
288 assert!(rwx.writable());
289 assert!(rwx.executable());
290
291 assert!(VRegionFlags::try_new(0b100).is_none());
292}
293
294#[test]
295fn vregion_flags_subset() {
296 use lancer_core::types::VRegionFlags;
297
298 let rw = VRegionFlags::try_new(VRegionFlags::WRITABLE).unwrap();
299 let rwx = VRegionFlags::try_new(VRegionFlags::WRITABLE | VRegionFlags::EXECUTABLE).unwrap();
300 let empty = VRegionFlags::try_new(0).unwrap();
301
302 assert!(rw.is_subset_of(rwx));
303 assert!(empty.is_subset_of(rw));
304 assert!(empty.is_subset_of(rwx));
305 assert!(!rwx.is_subset_of(rw));
306}
307
308#[test]
309fn untyped_vregion_allocation_math() {
310 let state = UntypedState::new(20, false);
311 let backing_size = 16u32 * 4096;
312 let result = state.try_allocate_raw(backing_size, 4096, 1).unwrap();
313 assert_eq!(result.start_offset(), 0);
314 assert_eq!(result.stride(), backing_size);
315 assert_eq!(result.object_count(), 1);
316}
317
318#[test]
319fn untyped_vregion_insufficient_space() {
320 let state = UntypedState::new(14, false);
321 let backing_size = 16u32 * 4096;
322 assert!(state.try_allocate_raw(backing_size, 4096, 1).is_err());
323}
324
325#[test]
326fn untyped_vregion_device_rejected() {
327 let state = UntypedState::new(20, true);
328 let result = state.try_retype(ObjectTag::VRegion, 0, 1);
329 assert!(result.is_err());
330}
331
332use proptest::prelude::*;
333
334proptest! {
335 #[test]
336 fn arbitrary_generation_header(generation in proptest::num::u32::ANY) {
337 let header = KernelObjectHeader::new(ObjectTag::Notification, generation, 64);
338 prop_assert_eq!(header.generation(), generation);
339 prop_assert!(!header.is_stale(generation));
340 prop_assert!(header.is_stale(generation.wrapping_add(1)));
341 }
342
343 #[test]
344 fn ref_count_sequence(ops in proptest::collection::vec(proptest::bool::ANY, 0..50)) {
345 let mut header = KernelObjectHeader::new(ObjectTag::Endpoint, 0, 64);
346 let mut expected: u32 = 1;
347 ops.iter().for_each(|inc| {
348 match *inc {
349 true => match expected.checked_add(1) {
350 Some(next) => {
351 assert_eq!(header.inc_ref(), Some(next));
352 expected = next;
353 }
354 None => {
355 assert_eq!(header.inc_ref(), None);
356 }
357 },
358 false => {
359 header.dec_ref();
360 expected = expected.saturating_sub(1);
361 }
362 }
363 assert_eq!(header.ref_count(), expected);
364 });
365 }
366}