globally_ordered_mock_mmio/
mock_builder.rs1use mmio::region::{MmioRegion, UnsafeMmio};
6use std::sync::Arc;
7
8use crate::data_access::AccessSize;
9use crate::scoreboard::Scoreboard;
10use crate::trace_builder::ExpectedTraceBuilder;
11
12#[derive(Clone, Debug)]
16pub struct MockUnsafeMmio {
17 scoreboard: Arc<Scoreboard>,
18}
19
20impl UnsafeMmio for MockUnsafeMmio {
21 fn len(&self) -> usize {
22 self.scoreboard.region_size_bytes()
23 }
24
25 fn align_offset(&self, _align: usize) -> usize {
26 0
29 }
30
31 unsafe fn load8_unchecked(&self, offset: usize) -> u8 {
32 let value = self.scoreboard.load(offset, AccessSize::U8);
33 u8::try_from(value).expect("Scoreboard guarantees the value fits in the access size")
34 }
35
36 unsafe fn load16_unchecked(&self, offset: usize) -> u16 {
37 let value = self.scoreboard.load(offset, AccessSize::U16);
38 u16::try_from(value).expect("Scoreboard guarantees the value fits in the access size")
39 }
40
41 unsafe fn load32_unchecked(&self, offset: usize) -> u32 {
42 let value = self.scoreboard.load(offset, AccessSize::U32);
43 u32::try_from(value).expect("Scoreboard guarantees the value fits in the access size")
44 }
45
46 unsafe fn load64_unchecked(&self, offset: usize) -> u64 {
47 self.scoreboard.load(offset, AccessSize::U64)
48 }
49
50 unsafe fn store8_unchecked(&self, offset: usize, value: u8) {
51 self.scoreboard.store(offset, AccessSize::U8, u64::from(value));
52 }
53
54 unsafe fn store16_unchecked(&self, offset: usize, value: u16) {
55 self.scoreboard.store(offset, AccessSize::U16, u64::from(value));
56 }
57
58 unsafe fn store32_unchecked(&self, offset: usize, value: u32) {
59 self.scoreboard.store(offset, AccessSize::U32, u64::from(value));
60 }
61
62 unsafe fn store64_unchecked(&self, offset: usize, value: u64) {
63 self.scoreboard.store(offset, AccessSize::U64, value);
64 }
65
66 fn write_barrier(&self) {
67 self.scoreboard.write_barrier();
68 }
69}
70
71pub type MockMmioRegion = MmioRegion<MockUnsafeMmio, Arc<MockUnsafeMmio>>;
76
77#[derive(Debug)]
89pub struct MockMmioRegionBuilder {
90 scoreboard: Arc<Scoreboard>,
91}
92
93impl MockMmioRegionBuilder {
94 pub fn new(region_size_bytes: usize) -> Self {
96 Self { scoreboard: Arc::new(Scoreboard::new(region_size_bytes)) }
97 }
98
99 pub fn region_size_bytes(&self) -> usize {
101 self.scoreboard.region_size_bytes()
102 }
103
104 pub fn expect<F: FnOnce(&mut ExpectedTraceBuilder<'_>)>(&self, f: F) {
112 let mut builder = ExpectedTraceBuilder::new(&self.scoreboard);
113 f(&mut builder);
114 }
115
116 fn mock_unsafe_mmio(&self) -> MockUnsafeMmio {
118 MockUnsafeMmio { scoreboard: Arc::clone(&self.scoreboard) }
119 }
120
121 pub fn build(&self) -> MockMmioRegion {
127 MmioRegion::new(self.mock_unsafe_mmio()).into_split_send()
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134 use mmio::Mmio;
135
136 #[fuchsia::test]
137 fn test_mock_mmio_region_size_bytes() {
138 let mock_builder = MockMmioRegionBuilder::new(0x2000);
139 assert_eq!(mock_builder.region_size_bytes(), 0x2000);
140 }
141
142 #[fuchsia::test]
143 fn test_mock_mmio_built_region_len() {
144 let mock_builder = MockMmioRegionBuilder::new(0x2000);
145 let mmio = mock_builder.build();
146 assert_eq!(mmio.len(), 0x2000);
147 }
148
149 #[fuchsia::test]
150 fn test_mock_mmio_built_region_operations() {
151 let mock_builder = MockMmioRegionBuilder::new(0x1000);
152 mock_builder.expect(|t| {
153 t.at(0x00).write8(0x12);
154 t.at(0x02).write16(0x3456);
155 t.at(0x04).write32(0x789a_bcde);
156 t.at(0x08).write64(0x0123_4567_89ab_cdef);
157 t.at(0x10).read8(0xaa);
158 t.at(0x12).read16(0xbbcc);
159 t.at(0x14).read32(0xddee_ff00);
160 t.at(0x18).read64(0x1122_3344_5566_7788);
161 t.write_barrier();
162 });
163 let mut mmio = mock_builder.build();
164 mmio.store8(0x00, 0x12);
165 mmio.store16(0x02, 0x3456);
166 mmio.store32(0x04, 0x789a_bcde);
167 mmio.store64(0x08, 0x0123_4567_89ab_cdef);
168 assert_eq!(mmio.load8(0x10), 0xaa);
169 assert_eq!(mmio.load16(0x12), 0xbbcc);
170 assert_eq!(mmio.load32(0x14), 0xddee_ff00);
171 assert_eq!(mmio.load64(0x18), 0x1122_3344_5566_7788);
172 mmio.write_barrier();
173 }
174
175 #[fuchsia::test]
176 fn test_mock_mmio_built_region_split_off() {
177 use mmio::MmioSplit;
178
179 let mock_builder = MockMmioRegionBuilder::new(0x1000);
180 mock_builder.expect(|t| {
181 t.at(0x0).write32(0x1111);
182 t.at(0x100).write32(0x2222);
183 });
184 let mut mmio = mock_builder.build();
185 let mut sub1 = mmio.split_off(0x100);
186 assert_eq!(sub1.len(), 0x100);
187 assert_eq!(mmio.len(), 0xf00);
188
189 sub1.store32(0x0, 0x1111);
190 mmio.store32(0x0, 0x2222);
191 }
192
193 #[fuchsia::test]
194 #[should_panic(expected = "UNRETIRED MMIO EXPECTATIONS")]
195 fn test_dropping_builder_and_region_verifies_expectations() {
196 let mock_builder = MockMmioRegionBuilder::new(0x1000);
197 mock_builder.expect(|t| {
198 t.at(0x0).read8(0x42);
199 });
200 let mmio = mock_builder.build();
201 drop(mmio);
202 drop(mock_builder);
203 }
204
205 #[fuchsia::test]
206 fn test_dropping_builder_alone_does_not_verify_expectations() {
207 let mock_builder = MockMmioRegionBuilder::new(0x1000);
208 mock_builder.expect(|t| {
209 t.at(0x0).read8(0x42);
210 });
211 let mmio = mock_builder.build();
212
213 drop(mock_builder);
216
217 assert_eq!(mmio.load8(0x0), 0x42);
218 }
219
220 #[fuchsia::test]
221 fn test_mock_unsafe_mmio_align_offset() {
222 let mock_builder = MockMmioRegionBuilder::new(0x1000);
223 let mock_unsafe_mmio = mock_builder.mock_unsafe_mmio();
224 assert_eq!(mock_unsafe_mmio.align_offset(4), 0);
225 assert_eq!(mock_unsafe_mmio.align_offset(8), 0);
226 }
227
228 #[fuchsia::test]
229 fn test_mock_unsafe_mmio_load_unchecked() {
230 let mock_builder = MockMmioRegionBuilder::new(0x1000);
231 mock_builder.expect(|t| {
232 t.at(0x0).read8(0x12);
233 t.at(0x2).read16(0x3456);
234 t.at(0x4).read32(0x789a_bcde);
235 t.at(0x8).read64(0x0123_4567_89ab_cdef);
236 });
237 let mock_unsafe = mock_builder.mock_unsafe_mmio();
238 unsafe {
239 assert_eq!(mock_unsafe.load8_unchecked(0x0), 0x12);
240 assert_eq!(mock_unsafe.load16_unchecked(0x2), 0x3456);
241 assert_eq!(mock_unsafe.load32_unchecked(0x4), 0x789a_bcde);
242 assert_eq!(mock_unsafe.load64_unchecked(0x8), 0x0123_4567_89ab_cdef);
243 }
244 }
245
246 #[fuchsia::test]
247 fn test_mock_unsafe_mmio_store_unchecked() {
248 let mock_builder = MockMmioRegionBuilder::new(0x1000);
249 mock_builder.expect(|t| {
250 t.at(0x0).write8(0x12);
251 t.at(0x2).write16(0x3456);
252 t.at(0x4).write32(0x789a_bcde);
253 t.at(0x8).write64(0x0123_4567_89ab_cdef);
254 });
255 let mock_unsafe = mock_builder.mock_unsafe_mmio();
256 unsafe {
257 mock_unsafe.store8_unchecked(0x0, 0x12);
258 mock_unsafe.store16_unchecked(0x2, 0x3456);
259 mock_unsafe.store32_unchecked(0x4, 0x789a_bcde);
260 mock_unsafe.store64_unchecked(0x8, 0x0123_4567_89ab_cdef);
261 }
262 }
263
264 #[fuchsia::test]
265 fn test_mock_unsafe_mmio_write_barrier() {
266 let mock_builder = MockMmioRegionBuilder::new(0x1000);
267 mock_builder.expect(|t| {
268 t.write_barrier();
269 });
270 let mock_unsafe = mock_builder.mock_unsafe_mmio();
271 mock_unsafe.write_barrier();
272 }
273
274 #[fuchsia::test]
275 fn test_mock_unsafe_mmio_send() {
276 fn assert_send<T: Send>() {}
277 assert_send::<MockUnsafeMmio>();
278
279 let mock_builder = MockMmioRegionBuilder::new(0x1000);
280 let mock_unsafe_mmio = mock_builder.mock_unsafe_mmio();
281 let handle = std::thread::spawn(move || {
282 assert_eq!(mock_unsafe_mmio.len(), 0x1000);
283 });
284 handle.join().unwrap();
285 }
286
287 #[fuchsia::test]
288 fn test_mock_unsafe_mmio_sync() {
289 fn assert_sync<T: Sync>() {}
290 assert_sync::<MockUnsafeMmio>();
291
292 let mock_builder = MockMmioRegionBuilder::new(0x1000);
293 let mock_unsafe_mmio = mock_builder.mock_unsafe_mmio();
294 std::thread::scope(|s| {
295 s.spawn(|| {
296 assert_eq!(mock_unsafe_mmio.len(), 0x1000);
297 });
298 assert_eq!(mock_unsafe_mmio.len(), 0x1000);
299 });
300 }
301}