• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2025 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #![no_std]
16 
17 use core::ptr::addr_of_mut;
18 use intrusive_collections::{intrusive_adapter, LinkedList, LinkedListLink};
19 
20 pub use pw_bytes;
21 
22 intrusive_adapter!(pub TestDescAndFnAdapter<'a> = &'a TestDescAndFn: TestDescAndFn { link: LinkedListLink });
23 
24 static mut TEST_LIST: Option<LinkedList<TestDescAndFnAdapter>> = None;
25 
26 // All accesses to test list go through this function.  This gives us a
27 // single point of ownership of TEST_LIST and keeps us from leaking references
28 // to it.
access_test_list<F>(callback: F) where F: FnOnce(&mut LinkedList<TestDescAndFnAdapter>),29 fn access_test_list<F>(callback: F)
30 where
31     F: FnOnce(&mut LinkedList<TestDescAndFnAdapter>),
32 {
33     // Safety: Tests are single threaded for now.  This assumption needs to be
34     // revisited.
35     let test_list: &mut Option<LinkedList<TestDescAndFnAdapter>> =
36         unsafe { addr_of_mut!(TEST_LIST).as_mut().unwrap_unchecked() };
37     let list = test_list.get_or_insert_with(|| LinkedList::new(TestDescAndFnAdapter::new()));
38     callback(list)
39 }
40 
add_test(test: &'static mut TestDescAndFn)41 pub fn add_test(test: &'static mut TestDescAndFn) {
42     access_test_list(|test_list| test_list.push_back(test))
43 }
44 
for_each_test<F>(mut callback: F) where F: FnMut(&TestDescAndFn),45 pub fn for_each_test<F>(mut callback: F)
46 where
47     F: FnMut(&TestDescAndFn),
48 {
49     access_test_list(|test_list| {
50         for test in test_list.iter() {
51             callback(test);
52         }
53     });
54 }
55 
56 pub struct TestError {
57     pub file: &'static str,
58     pub line: u32,
59     pub message: &'static str,
60 }
61 
62 pub type Result<T> = core::result::Result<T, TestError>;
63 
64 pub enum TestFn {
65     StaticTestFn(fn() -> Result<()>),
66 }
67 
68 pub struct TestDesc {
69     pub name: &'static str,
70 }
71 
72 pub struct TestDescAndFn {
73     pub desc: TestDesc,
74     pub test_fn: TestFn,
75     pub link: LinkedListLink,
76 }
77 
78 impl TestDescAndFn {
new(desc: TestDesc, test_fn: TestFn) -> Self79     pub const fn new(desc: TestDesc, test_fn: TestFn) -> Self {
80         Self {
81             desc,
82             test_fn,
83             link: LinkedListLink::new(),
84         }
85     }
86 }
87 
88 // We're marking these as send and sync so that we can declare statics with.
89 // them.  They're not actually Send and Sync because they contain linked list
90 // pointers but in practice tests are single threaded and these are never sent
91 // between threads.
92 //
93 // A better pattern here must be worked out with intrusive lists of static data
94 // (for statically declared threads for instance) so we'll revisit this later.
95 unsafe impl Send for TestDescAndFn {}
96 unsafe impl Sync for TestDescAndFn {}
97 
98 #[macro_export]
99 macro_rules! assert_eq {
100     ($a:expr, $b:expr) => {
101         if $a != $b {
102             return Err(unittest::TestError {
103                 file: file!(),
104                 line: line!(),
105                 message: unittest::pw_bytes::concat_static_strs!(
106                     "assert_eq!(",
107                     stringify!($a),
108                     ", ",
109                     stringify!($b),
110                     ") failed"
111                 ),
112             });
113         }
114     };
115 }
116 
117 #[macro_export]
118 macro_rules! assert_ne {
119     ($a:expr, $b:expr) => {
120         if $a == $b {
121             return Err(unittest::TestError {
122                 file: file!(),
123                 line: line!(),
124                 message: unittest::pw_bytes::concat_static_strs!(
125                     "assert_ne!(",
126                     stringify!($a),
127                     ", ",
128                     stringify!($b),
129                     ") failed"
130                 ),
131             });
132         }
133     };
134 }
135 
136 #[macro_export]
137 macro_rules! assert_true {
138     ($a:expr) => {
139         if !$a {
140             return Err(unittest::TestError {
141                 file: file!(),
142                 line: line!(),
143                 message: unittest::pw_bytes::concat_static_strs!(
144                     "assert_true!(",
145                     stringify!($a),
146                     ") failed"
147                 ),
148             });
149         }
150     };
151 }
152 
153 #[macro_export]
154 macro_rules! assert_false {
155     ($a:expr) => {
156         if $a {
157             return Err(unittest::TestError {
158                 file: file!(),
159                 line: line!(),
160                 message: unittest::pw_bytes::concat_static_strs!(
161                     "assert_false!(",
162                     stringify!($a),
163                     ") failed"
164                 ),
165             });
166         }
167     };
168 }
169