Add KUNIT tests to make sure the macro is working correctly.
Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
---
rust/kernel/bitfield.rs | 321 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 321 insertions(+)
diff --git a/rust/kernel/bitfield.rs b/rust/kernel/bitfield.rs
index fed19918c3b9..9a20bcd2eb60 100644
--- a/rust/kernel/bitfield.rs
+++ b/rust/kernel/bitfield.rs
@@ -402,3 +402,324 @@ fn default() -> Self {
}
};
}
+
+#[::kernel::macros::kunit_tests(kernel_bitfield)]
+mod tests {
+ use core::convert::TryFrom;
+
+ // Enum types for testing => and ?=> conversions
+ #[derive(Debug, Clone, Copy, PartialEq)]
+ enum MemoryType {
+ Unmapped = 0,
+ Normal = 1,
+ Device = 2,
+ Reserved = 3,
+ }
+
+ impl Default for MemoryType {
+ fn default() -> Self {
+ MemoryType::Unmapped
+ }
+ }
+
+ impl TryFrom<u8> for MemoryType {
+ type Error = u8;
+ fn try_from(value: u8) -> Result<Self, Self::Error> {
+ match value {
+ 0 => Ok(MemoryType::Unmapped),
+ 1 => Ok(MemoryType::Normal),
+ 2 => Ok(MemoryType::Device),
+ 3 => Ok(MemoryType::Reserved),
+ _ => Err(value),
+ }
+ }
+ }
+
+ impl From<MemoryType> for u64 {
+ fn from(mt: MemoryType) -> u64 {
+ mt as u64
+ }
+ }
+
+ #[derive(Debug, Clone, Copy, PartialEq)]
+ enum Priority {
+ Low = 0,
+ Medium = 1,
+ High = 2,
+ Critical = 3,
+ }
+
+ impl Default for Priority {
+ fn default() -> Self {
+ Priority::Low
+ }
+ }
+
+ impl From<u8> for Priority {
+ fn from(value: u8) -> Self {
+ match value & 0x3 {
+ 0 => Priority::Low,
+ 1 => Priority::Medium,
+ 2 => Priority::High,
+ _ => Priority::Critical,
+ }
+ }
+ }
+
+ impl From<Priority> for u16 {
+ fn from(p: Priority) -> u16 {
+ p as u16
+ }
+ }
+
+ bitfield! {
+ struct TestPageTableEntry(u64) {
+ 0:0 present as bool;
+ 1:1 writable as bool;
+ 11:9 available as u8;
+ 13:12 mem_type as u8 ?=> MemoryType;
+ 17:14 extended_type as u8 ?=> MemoryType; // For testing failures
+ 51:12 pfn as u64;
+ 51:12 pfn_overlap as u64;
+ 61:52 available2 as u16;
+ }
+ }
+
+ bitfield! {
+ struct TestControlRegister(u16) {
+ 0:0 enable as bool;
+ 3:1 mode as u8;
+ 5:4 priority as u8 => Priority;
+ 7:4 priority_nibble as u8;
+ 15:8 channel as u8;
+ }
+ }
+
+ bitfield! {
+ struct TestStatusRegister(u8) {
+ 0:0 ready as bool;
+ 1:1 error as bool;
+ 3:2 state as u8;
+ 7:4 reserved as u8;
+ 7:0 full_byte as u8; // For entire register
+ }
+ }
+
+ #[test]
+ fn test_single_bits() {
+ let mut pte = TestPageTableEntry::default();
+
+ assert!(!pte.present());
+ assert!(!pte.writable());
+
+ pte = pte.set_present(true);
+ assert!(pte.present());
+
+ pte = pte.set_writable(true);
+ assert!(pte.writable());
+
+ pte = pte.set_writable(false);
+ assert!(!pte.writable());
+
+ assert_eq!(pte.available(), 0);
+ pte = pte.set_available(0x5);
+ assert_eq!(pte.available(), 0x5);
+ }
+
+ #[test]
+ fn test_range_fields() {
+ let mut pte = TestPageTableEntry::default();
+
+ pte = pte.set_pfn(0x123456);
+ assert_eq!(pte.pfn(), 0x123456);
+ // Test overlapping field reads same value
+ assert_eq!(pte.pfn_overlap(), 0x123456);
+
+ pte = pte.set_available(0x7);
+ assert_eq!(pte.available(), 0x7);
+
+ pte = pte.set_available2(0x3FF);
+ assert_eq!(pte.available2(), 0x3FF);
+
+ // Test TryFrom with ?=> for MemoryType
+ pte = pte.set_mem_type(MemoryType::Device);
+ assert_eq!(pte.mem_type(), Ok(MemoryType::Device));
+
+ pte = pte.set_mem_type(MemoryType::Normal);
+ assert_eq!(pte.mem_type(), Ok(MemoryType::Normal));
+
+ // Test all valid values for mem_type
+ pte = pte.set_mem_type(MemoryType::Reserved); // Valid value: 3
+ assert_eq!(pte.mem_type(), Ok(MemoryType::Reserved));
+
+ // Test failure case using extended_type field which has 4 bits (0-15)
+ // MemoryType only handles 0-3, so values 4-15 should return Err
+ let mut raw = pte.raw();
+ // Set bits 17:14 to 7 (invalid for MemoryType)
+ raw = (raw & !::kernel::bits::genmask_u64(14..=17)) | (0x7 << 14);
+ let invalid_pte = TestPageTableEntry::new(raw);
+ // Should return Err with the invalid value
+ assert_eq!(invalid_pte.extended_type(), Err(0x7));
+
+ // Test a valid value after testing invalid to ensure both cases work
+ // Set bits 17:14 to 2 (valid: Device)
+ raw = (raw & !::kernel::bits::genmask_u64(14..=17)) | (0x2 << 14);
+ let valid_pte = TestPageTableEntry::new(raw);
+ assert_eq!(valid_pte.extended_type(), Ok(MemoryType::Device));
+
+ let max_pfn = ::kernel::bits::genmask_u64(0..=39);
+ pte = pte.set_pfn(max_pfn);
+ assert_eq!(pte.pfn(), max_pfn);
+ assert_eq!(pte.pfn_overlap(), max_pfn);
+ }
+
+ #[test]
+ fn test_builder_pattern() {
+ let pte = TestPageTableEntry::default()
+ .set_present(true)
+ .set_writable(true)
+ .set_available(0x7)
+ .set_pfn(0xABCDEF)
+ .set_mem_type(MemoryType::Reserved)
+ .set_available2(0x3FF);
+
+ assert!(pte.present());
+ assert!(pte.writable());
+ assert_eq!(pte.available(), 0x7);
+ assert_eq!(pte.pfn(), 0xABCDEF);
+ assert_eq!(pte.pfn_overlap(), 0xABCDEF);
+ assert_eq!(pte.mem_type(), Ok(MemoryType::Reserved));
+ assert_eq!(pte.available2(), 0x3FF);
+ }
+
+ #[test]
+ fn test_raw_operations() {
+ let raw_value = 0x3FF0000003123E03u64;
+
+ let pte = TestPageTableEntry::new(raw_value);
+ assert_eq!(pte.raw(), raw_value);
+
+ assert!(pte.present());
+ assert!(pte.writable());
+ assert_eq!(pte.available(), 0x7);
+ assert_eq!(pte.pfn(), 0x3123);
+ assert_eq!(pte.pfn_overlap(), 0x3123);
+ assert_eq!(pte.mem_type(), Ok(MemoryType::Reserved));
+ assert_eq!(pte.available2(), 0x3FF);
+
+ // Test using direct constructor syntax TestStruct(value)
+ let pte2 = TestPageTableEntry::new(raw_value);
+ assert_eq!(pte2.raw(), raw_value);
+ }
+
+ #[test]
+ fn test_u16_bitfield() {
+ let mut ctrl = TestControlRegister::default();
+
+ assert!(!ctrl.enable());
+ assert_eq!(ctrl.mode(), 0);
+ assert_eq!(ctrl.priority(), Priority::Low);
+ assert_eq!(ctrl.priority_nibble(), 0);
+ assert_eq!(ctrl.channel(), 0);
+
+ ctrl = ctrl.set_enable(true);
+ assert!(ctrl.enable());
+
+ ctrl = ctrl.set_mode(0x5);
+ assert_eq!(ctrl.mode(), 0x5);
+
+ // Test From conversion with =>
+ ctrl = ctrl.set_priority(Priority::High);
+ assert_eq!(ctrl.priority(), Priority::High);
+ assert_eq!(ctrl.priority_nibble(), 0x2); // High = 2 in bits 5:4
+
+ ctrl = ctrl.set_channel(0xAB);
+ assert_eq!(ctrl.channel(), 0xAB);
+
+ // Test overlapping fields
+ ctrl = ctrl.set_priority_nibble(0xF);
+ assert_eq!(ctrl.priority_nibble(), 0xF);
+ assert_eq!(ctrl.priority(), Priority::Critical); // bits 5:4 = 0x3
+
+ let ctrl2 = TestControlRegister::default()
+ .set_enable(true)
+ .set_mode(0x3)
+ .set_priority(Priority::Medium)
+ .set_channel(0x42);
+
+ assert!(ctrl2.enable());
+ assert_eq!(ctrl2.mode(), 0x3);
+ assert_eq!(ctrl2.priority(), Priority::Medium);
+ assert_eq!(ctrl2.channel(), 0x42);
+
+ let raw_value: u16 = 0x4217;
+ let ctrl3 = TestControlRegister::new(raw_value);
+ assert_eq!(ctrl3.raw(), raw_value);
+ assert!(ctrl3.enable());
+ assert_eq!(ctrl3.priority(), Priority::Medium);
+ assert_eq!(ctrl3.priority_nibble(), 0x1);
+ assert_eq!(ctrl3.channel(), 0x42);
+ }
+
+ #[test]
+ fn test_u8_bitfield() {
+ let mut status = TestStatusRegister::default();
+
+ assert!(!status.ready());
+ assert!(!status.error());
+ assert_eq!(status.state(), 0);
+ assert_eq!(status.reserved(), 0);
+ assert_eq!(status.full_byte(), 0);
+
+ status = status.set_ready(true);
+ assert!(status.ready());
+ assert_eq!(status.full_byte(), 0x01);
+
+ status = status.set_error(true);
+ assert!(status.error());
+ assert_eq!(status.full_byte(), 0x03);
+
+ status = status.set_state(0x3);
+ assert_eq!(status.state(), 0x3);
+ assert_eq!(status.full_byte(), 0x0F);
+
+ status = status.set_reserved(0xA);
+ assert_eq!(status.reserved(), 0xA);
+ assert_eq!(status.full_byte(), 0xAF);
+
+ // Test overlapping field
+ status = status.set_full_byte(0x55);
+ assert_eq!(status.full_byte(), 0x55);
+ assert!(status.ready());
+ assert!(!status.error());
+ assert_eq!(status.state(), 0x1);
+ assert_eq!(status.reserved(), 0x5);
+
+ let status2 = TestStatusRegister::default()
+ .set_ready(true)
+ .set_state(0x2)
+ .set_reserved(0x5);
+
+ assert!(status2.ready());
+ assert!(!status2.error());
+ assert_eq!(status2.state(), 0x2);
+ assert_eq!(status2.reserved(), 0x5);
+ assert_eq!(status2.full_byte(), 0x59);
+
+ let raw_value: u8 = 0x59;
+ let status3 = TestStatusRegister::new(raw_value);
+ assert_eq!(status3.raw(), raw_value);
+ assert!(status3.ready());
+ assert!(!status3.error());
+ assert_eq!(status3.state(), 0x2);
+ assert_eq!(status3.reserved(), 0x5);
+ assert_eq!(status3.full_byte(), 0x59);
+
+ let status4 = TestStatusRegister::new(0xFF);
+ assert!(status4.ready());
+ assert!(status4.error());
+ assert_eq!(status4.state(), 0x3);
+ assert_eq!(status4.reserved(), 0xF);
+ assert_eq!(status4.full_byte(), 0xFF);
+ }
+}
--
2.34.1
On Tue Sep 30, 2025 at 11:45 PM JST, Joel Fernandes wrote: > Add KUNIT tests to make sure the macro is working correctly. > > Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com> > --- > rust/kernel/bitfield.rs | 321 ++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 321 insertions(+) > > diff --git a/rust/kernel/bitfield.rs b/rust/kernel/bitfield.rs > index fed19918c3b9..9a20bcd2eb60 100644 > --- a/rust/kernel/bitfield.rs > +++ b/rust/kernel/bitfield.rs > @@ -402,3 +402,324 @@ fn default() -> Self { > } > }; > } > + > +#[::kernel::macros::kunit_tests(kernel_bitfield)] > +mod tests { > + use core::convert::TryFrom; > + > + // Enum types for testing => and ?=> conversions > + #[derive(Debug, Clone, Copy, PartialEq)] > + enum MemoryType { > + Unmapped = 0, > + Normal = 1, > + Device = 2, > + Reserved = 3, > + } > + > + impl Default for MemoryType { > + fn default() -> Self { > + MemoryType::Unmapped > + } > + } Tip: you can add `Default` to the `#[derive]` marker of `MemoryType` and mark the variant you want as default with `#[default]` instead of providing a full impl block: #[derive(Debug, Default, Clone, Copy, PartialEq)] enum MemoryType { #[default] Unmapped = 0, Normal = 1, Device = 2, Reserved = 3, } > + > + impl TryFrom<u8> for MemoryType { > + type Error = u8; > + fn try_from(value: u8) -> Result<Self, Self::Error> { > + match value { > + 0 => Ok(MemoryType::Unmapped), > + 1 => Ok(MemoryType::Normal), > + 2 => Ok(MemoryType::Device), > + 3 => Ok(MemoryType::Reserved), > + _ => Err(value), > + } > + } > + } > + > + impl From<MemoryType> for u64 { > + fn from(mt: MemoryType) -> u64 { > + mt as u64 > + } > + } > + > + #[derive(Debug, Clone, Copy, PartialEq)] > + enum Priority { > + Low = 0, > + Medium = 1, > + High = 2, > + Critical = 3, > + } > + > + impl Default for Priority { > + fn default() -> Self { > + Priority::Low > + } > + } > + > + impl From<u8> for Priority { > + fn from(value: u8) -> Self { > + match value & 0x3 { > + 0 => Priority::Low, > + 1 => Priority::Medium, > + 2 => Priority::High, > + _ => Priority::Critical, > + } > + } > + } > + > + impl From<Priority> for u16 { > + fn from(p: Priority) -> u16 { > + p as u16 > + } > + } > + > + bitfield! { > + struct TestPageTableEntry(u64) { > + 0:0 present as bool; > + 1:1 writable as bool; > + 11:9 available as u8; > + 13:12 mem_type as u8 ?=> MemoryType; > + 17:14 extended_type as u8 ?=> MemoryType; // For testing failures > + 51:12 pfn as u64; > + 51:12 pfn_overlap as u64; > + 61:52 available2 as u16; > + } > + } > + > + bitfield! { > + struct TestControlRegister(u16) { > + 0:0 enable as bool; > + 3:1 mode as u8; > + 5:4 priority as u8 => Priority; > + 7:4 priority_nibble as u8; > + 15:8 channel as u8; > + } > + } > + > + bitfield! { > + struct TestStatusRegister(u8) { > + 0:0 ready as bool; > + 1:1 error as bool; > + 3:2 state as u8; > + 7:4 reserved as u8; > + 7:0 full_byte as u8; // For entire register > + } > + } > + > + #[test] > + fn test_single_bits() { > + let mut pte = TestPageTableEntry::default(); > + > + assert!(!pte.present()); > + assert!(!pte.writable()); > + > + pte = pte.set_present(true); > + assert!(pte.present()); > + > + pte = pte.set_writable(true); > + assert!(pte.writable()); > + > + pte = pte.set_writable(false); > + assert!(!pte.writable()); > + > + assert_eq!(pte.available(), 0); > + pte = pte.set_available(0x5); > + assert_eq!(pte.available(), 0x5); I'd suggest testing the actual raw value of the register on top of invoking the getter. That way you also test that: - The right field is actually written (i.e. if the offset is off by one, the getter will return the expected result even though the bitfield has the wrong value), - No other field has been affected. So something like: pte = pte.set_present(true); assert!(pte.present()); assert(pte.into(), 0x1u64); pte = pte.set_writable(true); assert!(pte.writable()); assert(pte.into(), 0x3u64); It might look a bit gross, but it is ok since these are not doctests that users are going to take as a reference, so we case improve test coverage at the detriment of readability.
On 10/2/25 1:41 AM, Alexandre Courbot wrote: > On Tue Sep 30, 2025 at 11:45 PM JST, Joel Fernandes wrote: >> Add KUNIT tests to make sure the macro is working correctly. >> >> Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com> >> --- >> rust/kernel/bitfield.rs | 321 ++++++++++++++++++++++++++++++++++++++++ >> 1 file changed, 321 insertions(+) >> >> diff --git a/rust/kernel/bitfield.rs b/rust/kernel/bitfield.rs >> index fed19918c3b9..9a20bcd2eb60 100644 >> --- a/rust/kernel/bitfield.rs >> +++ b/rust/kernel/bitfield.rs >> @@ -402,3 +402,324 @@ fn default() -> Self { >> } >> }; >> } >> + >> +#[::kernel::macros::kunit_tests(kernel_bitfield)] >> +mod tests { >> + use core::convert::TryFrom; >> + >> + // Enum types for testing => and ?=> conversions >> + #[derive(Debug, Clone, Copy, PartialEq)] >> + enum MemoryType { >> + Unmapped = 0, >> + Normal = 1, >> + Device = 2, >> + Reserved = 3, >> + } >> + >> + impl Default for MemoryType { >> + fn default() -> Self { >> + MemoryType::Unmapped >> + } >> + } > Tip: you can add `Default` to the `#[derive]` marker of `MemoryType` and > mark the variant you want as default with `#[default]` instead of > providing a full impl block: > > #[derive(Debug, Default, Clone, Copy, PartialEq)] > enum MemoryType { > #[default] > Unmapped = 0, > Normal = 1, > Device = 2, > Reserved = 3, > } I would alternatively recommend to provide a `MemoryType::new` impl with a `const` definition: ```rust impl MemoryType { pub const fn new() -> Self { Self::Unmapped } } impl Default for MemoryType { fn default() -> Self { Self::new() } } ``` This pattern allows using `MemoryType::new()` in `const` contexts, while also providing the `Default` impl using the same default. It's somewhat of a workaround until we get `const` traits. >> + >> + impl TryFrom<u8> for MemoryType { >> + type Error = u8; >> + fn try_from(value: u8) -> Result<Self, Self::Error> { >> + match value { >> + 0 => Ok(MemoryType::Unmapped), >> + 1 => Ok(MemoryType::Normal), >> + 2 => Ok(MemoryType::Device), >> + 3 => Ok(MemoryType::Reserved), >> + _ => Err(value), >> + } >> + } >> + } >> + >> + impl From<MemoryType> for u64 { >> + fn from(mt: MemoryType) -> u64 { >> + mt as u64 >> + } >> + } >> + >> + #[derive(Debug, Clone, Copy, PartialEq)] >> + enum Priority { >> + Low = 0, >> + Medium = 1, >> + High = 2, >> + Critical = 3, >> + } >> + >> + impl Default for Priority { >> + fn default() -> Self { >> + Priority::Low >> + } >> + } >> + >> + impl From<u8> for Priority { >> + fn from(value: u8) -> Self { >> + match value & 0x3 { >> + 0 => Priority::Low, >> + 1 => Priority::Medium, >> + 2 => Priority::High, >> + _ => Priority::Critical, >> + } >> + } >> + } >> + >> + impl From<Priority> for u16 { >> + fn from(p: Priority) -> u16 { >> + p as u16 >> + } >> + } >> + >> + bitfield! { >> + struct TestPageTableEntry(u64) { >> + 0:0 present as bool; >> + 1:1 writable as bool; >> + 11:9 available as u8; >> + 13:12 mem_type as u8 ?=> MemoryType; >> + 17:14 extended_type as u8 ?=> MemoryType; // For testing failures >> + 51:12 pfn as u64; >> + 51:12 pfn_overlap as u64; >> + 61:52 available2 as u16; >> + } >> + } >> + >> + bitfield! { >> + struct TestControlRegister(u16) { >> + 0:0 enable as bool; >> + 3:1 mode as u8; >> + 5:4 priority as u8 => Priority; >> + 7:4 priority_nibble as u8; >> + 15:8 channel as u8; >> + } >> + } >> + >> + bitfield! { >> + struct TestStatusRegister(u8) { >> + 0:0 ready as bool; >> + 1:1 error as bool; >> + 3:2 state as u8; >> + 7:4 reserved as u8; >> + 7:0 full_byte as u8; // For entire register >> + } >> + } >> + >> + #[test] >> + fn test_single_bits() { >> + let mut pte = TestPageTableEntry::default(); >> + >> + assert!(!pte.present()); >> + assert!(!pte.writable()); >> + >> + pte = pte.set_present(true); >> + assert!(pte.present()); >> + >> + pte = pte.set_writable(true); >> + assert!(pte.writable()); >> + >> + pte = pte.set_writable(false); >> + assert!(!pte.writable()); >> + >> + assert_eq!(pte.available(), 0); >> + pte = pte.set_available(0x5); >> + assert_eq!(pte.available(), 0x5); > I'd suggest testing the actual raw value of the register on top of > invoking the getter. That way you also test that: > > - The right field is actually written (i.e. if the offset is off by one, > the getter will return the expected result even though the bitfield > has the wrong value), > - No other field has been affected. > > So something like: > > pte = pte.set_present(true); > assert!(pte.present()); > assert(pte.into(), 0x1u64); > > pte = pte.set_writable(true); > assert!(pte.writable()); > assert(pte.into(), 0x3u64); > > It might look a bit gross, but it is ok since these are not doctests > that users are going to take as a reference, so we case improve test > coverage at the detriment of readability. >
On Thu Oct 2, 2025 at 11:16 AM JST, Elle Rhumsaa wrote: > On 10/2/25 1:41 AM, Alexandre Courbot wrote: > >> On Tue Sep 30, 2025 at 11:45 PM JST, Joel Fernandes wrote: >>> Add KUNIT tests to make sure the macro is working correctly. >>> >>> Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com> >>> --- >>> rust/kernel/bitfield.rs | 321 ++++++++++++++++++++++++++++++++++++++++ >>> 1 file changed, 321 insertions(+) >>> >>> diff --git a/rust/kernel/bitfield.rs b/rust/kernel/bitfield.rs >>> index fed19918c3b9..9a20bcd2eb60 100644 >>> --- a/rust/kernel/bitfield.rs >>> +++ b/rust/kernel/bitfield.rs >>> @@ -402,3 +402,324 @@ fn default() -> Self { >>> } >>> }; >>> } >>> + >>> +#[::kernel::macros::kunit_tests(kernel_bitfield)] >>> +mod tests { >>> + use core::convert::TryFrom; >>> + >>> + // Enum types for testing => and ?=> conversions >>> + #[derive(Debug, Clone, Copy, PartialEq)] >>> + enum MemoryType { >>> + Unmapped = 0, >>> + Normal = 1, >>> + Device = 2, >>> + Reserved = 3, >>> + } >>> + >>> + impl Default for MemoryType { >>> + fn default() -> Self { >>> + MemoryType::Unmapped >>> + } >>> + } >> Tip: you can add `Default` to the `#[derive]` marker of `MemoryType` and >> mark the variant you want as default with `#[default]` instead of >> providing a full impl block: >> >> #[derive(Debug, Default, Clone, Copy, PartialEq)] >> enum MemoryType { >> #[default] >> Unmapped = 0, >> Normal = 1, >> Device = 2, >> Reserved = 3, >> } > > I would alternatively recommend to provide a `MemoryType::new` impl with > a `const` definition: > > ```rust > impl MemoryType { > pub const fn new() -> Self { > > Self::Unmapped > > } > } > > impl Default for MemoryType { > fn default() -> Self { > Self::new() > } > } > ``` > > This pattern allows using `MemoryType::new()` in `const` contexts, while > also providing the `Default` impl using the same default. It's somewhat > of a workaround until we get `const` traits. That's an elegant pattern generally speaking, but I don't think we would benefit from using it in these unit tests.
On 10/2/25 2:51 AM, Alexandre Courbot wrote: > On Thu Oct 2, 2025 at 11:16 AM JST, Elle Rhumsaa wrote: >> On 10/2/25 1:41 AM, Alexandre Courbot wrote: >> >>> On Tue Sep 30, 2025 at 11:45 PM JST, Joel Fernandes wrote: >>>> Add KUNIT tests to make sure the macro is working correctly. >>>> >>>> Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com> >>>> --- >>>> rust/kernel/bitfield.rs | 321 ++++++++++++++++++++++++++++++++++++++++ >>>> 1 file changed, 321 insertions(+) >>>> >>>> diff --git a/rust/kernel/bitfield.rs b/rust/kernel/bitfield.rs >>>> index fed19918c3b9..9a20bcd2eb60 100644 >>>> --- a/rust/kernel/bitfield.rs >>>> +++ b/rust/kernel/bitfield.rs >>>> @@ -402,3 +402,324 @@ fn default() -> Self { >>>> } >>>> }; >>>> } >>>> + >>>> +#[::kernel::macros::kunit_tests(kernel_bitfield)] >>>> +mod tests { >>>> + use core::convert::TryFrom; >>>> + >>>> + // Enum types for testing => and ?=> conversions >>>> + #[derive(Debug, Clone, Copy, PartialEq)] >>>> + enum MemoryType { >>>> + Unmapped = 0, >>>> + Normal = 1, >>>> + Device = 2, >>>> + Reserved = 3, >>>> + } >>>> + >>>> + impl Default for MemoryType { >>>> + fn default() -> Self { >>>> + MemoryType::Unmapped >>>> + } >>>> + } >>> Tip: you can add `Default` to the `#[derive]` marker of `MemoryType` and >>> mark the variant you want as default with `#[default]` instead of >>> providing a full impl block: >>> >>> #[derive(Debug, Default, Clone, Copy, PartialEq)] >>> enum MemoryType { >>> #[default] >>> Unmapped = 0, >>> Normal = 1, >>> Device = 2, >>> Reserved = 3, >>> } >> I would alternatively recommend to provide a `MemoryType::new` impl with >> a `const` definition: >> >> ```rust >> impl MemoryType { >> pub const fn new() -> Self { >> >> Self::Unmapped >> >> } >> } >> >> impl Default for MemoryType { >> fn default() -> Self { >> Self::new() >> } >> } >> ``` >> >> This pattern allows using `MemoryType::new()` in `const` contexts, while >> also providing the `Default` impl using the same default. It's somewhat >> of a workaround until we get `const` traits. > That's an elegant pattern generally speaking, but I don't think we would > benefit from using it in these unit tests. *facepalm* right, I lost the context that these data structures were KUNIT-specific. Please disregard.
© 2016 - 2025 Red Hat, Inc.