imix/test/
linear_log_test.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Tests the log storage interface in linear mode. For testing in circular mode, see
6//! log_test.rs.
7//!
8//! The testing framework creates a non-circular log storage interface in flash and performs a
9//! series of writes and syncs to ensure that the non-circular log properly denies overly-large
10//! writes once it is full. For testing all of the general capabilities of the log storage
11//! interface, see log_test.rs.
12//!
13//! To run the test, add the following line to the imix boot sequence:
14//! ```
15//!     test::linear_log_test::run(mux_alarm);
16//! ```
17//! and use the `USER` and `RESET` buttons to manually erase the log and reboot the imix,
18//! respectively.
19
20use capsules_core::virtualizers::virtual_alarm::{MuxAlarm, VirtualMuxAlarm};
21use capsules_extra::log;
22use core::cell::Cell;
23use core::ptr::addr_of_mut;
24use kernel::debug;
25use kernel::hil::flash;
26use kernel::hil::log::{LogRead, LogReadClient, LogWrite, LogWriteClient};
27use kernel::hil::time::{Alarm, AlarmClient, ConvertTicks};
28use kernel::static_init;
29use kernel::storage_volume;
30use kernel::utilities::cells::{NumericCellExt, TakeCell};
31use kernel::ErrorCode;
32use sam4l::ast::Ast;
33use sam4l::flashcalw;
34
35// Allocate 1kiB volume for log storage.
36storage_volume!(LINEAR_TEST_LOG, 1);
37
38pub unsafe fn run(
39    mux_alarm: &'static MuxAlarm<'static, Ast>,
40    flash_controller: &'static sam4l::flashcalw::FLASHCALW,
41) {
42    // Set up flash controller.
43    flash_controller.configure();
44    let pagebuffer = static_init!(flashcalw::Sam4lPage, flashcalw::Sam4lPage::default());
45
46    // Create actual log storage abstraction on top of flash.
47    let log = static_init!(
48        Log,
49        log::Log::new(&LINEAR_TEST_LOG, flash_controller, pagebuffer, false)
50    );
51    kernel::deferred_call::DeferredCallClient::register(log);
52    flash::HasClient::set_client(flash_controller, log);
53
54    let alarm = static_init!(
55        VirtualMuxAlarm<'static, Ast>,
56        VirtualMuxAlarm::new(mux_alarm)
57    );
58    alarm.setup();
59
60    // Create and run test for log storage.
61    let test = static_init!(
62        LogTest<VirtualMuxAlarm<'static, Ast>>,
63        LogTest::new(log, &mut *addr_of_mut!(BUFFER), alarm, &TEST_OPS)
64    );
65    log.set_read_client(test);
66    log.set_append_client(test);
67    test.alarm.set_alarm_client(test);
68
69    test.run();
70}
71
72static TEST_OPS: [TestOp; 9] = [
73    TestOp::Read,
74    // Write to first page.
75    TestOp::Write(8),
76    TestOp::Write(300),
77    // Write to next page, too large to fit on first.
78    TestOp::Write(304),
79    // Write should fail, not enough space remaining.
80    TestOp::Write(306),
81    // Write should succeed, enough space for a smaller entry.
82    TestOp::Write(9),
83    // Read back everything to verify and sync.
84    TestOp::Read,
85    TestOp::Sync,
86    // Write should still fail after sync.
87    TestOp::Write(308),
88];
89
90// Buffer for reading from and writing to in the log tests.
91static mut BUFFER: [u8; 310] = [0; 310];
92// Time to wait in between log operations.
93const WAIT_MS: u32 = 3;
94
95// A single operation within the test.
96#[derive(Clone, Copy, PartialEq)]
97enum TestOp {
98    Read,
99    Write(usize),
100    Sync,
101}
102
103type Log = log::Log<'static, flashcalw::FLASHCALW>;
104struct LogTest<A: 'static + Alarm<'static>> {
105    log: &'static Log,
106    buffer: TakeCell<'static, [u8]>,
107    alarm: &'static A,
108    ops: &'static [TestOp],
109    op_index: Cell<usize>,
110}
111
112impl<A: 'static + Alarm<'static>> LogTest<A> {
113    fn new(
114        log: &'static Log,
115        buffer: &'static mut [u8],
116        alarm: &'static A,
117        ops: &'static [TestOp],
118    ) -> LogTest<A> {
119        debug!(
120            "Log recovered from flash (Start and end entry IDs: {:?} to {:?})",
121            log.log_start(),
122            log.log_end()
123        );
124
125        LogTest {
126            log,
127            buffer: TakeCell::new(buffer),
128            alarm,
129            ops,
130            op_index: Cell::new(0),
131        }
132    }
133
134    fn run(&self) {
135        let op_index = self.op_index.get();
136        if op_index == self.ops.len() {
137            debug!("Linear Log Storage test succeeded!");
138            return;
139        }
140
141        match self.ops[op_index] {
142            TestOp::Read => self.read(),
143            TestOp::Write(len) => self.write(len),
144            TestOp::Sync => self.sync(),
145        }
146    }
147
148    fn read(&self) {
149        self.buffer.take().map_or_else(
150            || panic!("NO BUFFER"),
151            move |buffer| {
152                // Clear buffer first to make debugging more sane.
153                for e in buffer.iter_mut() {
154                    *e = 0;
155                }
156
157                if let Err((error, original_buffer)) = self.log.read(buffer, buffer.len()) {
158                    self.buffer.replace(original_buffer);
159                    match error {
160                        ErrorCode::FAIL => {
161                            // No more entries, start writing again.
162                            debug!(
163                                "READ DONE: READ OFFSET: {:?} / WRITE OFFSET: {:?}",
164                                self.log.next_read_entry_id(),
165                                self.log.log_end()
166                            );
167                            self.op_index.increment();
168                            self.run();
169                        }
170                        ErrorCode::BUSY => {
171                            debug!("Flash busy, waiting before reattempting read");
172                            self.wait();
173                        }
174                        _ => panic!("READ FAILED: {:?}", error),
175                    }
176                }
177            },
178        );
179    }
180
181    fn write(&self, len: usize) {
182        self.buffer
183            .take()
184            .map(move |buffer| {
185                let expect_write_fail = self.log.log_end() + len > LINEAR_TEST_LOG.len();
186
187                // Set buffer value.
188                for i in 0..buffer.len() {
189                    buffer[i] = if i < len {
190                        len as u8
191                    } else {
192                        0
193                    };
194                }
195
196                if let Err((error, original_buffer)) = self.log.append(buffer, len) {
197                    self.buffer.replace(original_buffer);
198
199                    match error {
200                        ErrorCode::FAIL =>
201                            if expect_write_fail {
202                                debug!(
203                                    "Write failed on {} byte write, as expected",
204                                    len
205                                );
206                                self.op_index.increment();
207                                self.run();
208                            } else {
209                                panic!(
210                                    "Write failed unexpectedly on {} byte write (read entry ID: {:?}, append entry ID: {:?})",
211                                    len,
212                                    self.log.next_read_entry_id(),
213                                    self.log.log_end()
214                                );
215                            }
216                        ErrorCode::BUSY => self.wait(),
217                        _ => panic!("WRITE FAILED: {:?}", error),
218                    }
219                } else if expect_write_fail {
220                    panic!(
221                        "Write succeeded unexpectedly on {} byte write (read entry ID: {:?}, append entry ID: {:?})",
222                        len,
223                        self.log.next_read_entry_id(),
224                        self.log.log_end()
225                    );
226                }
227            })
228            .unwrap();
229    }
230
231    fn sync(&self) {
232        match self.log.sync() {
233            Ok(()) => (),
234            error => panic!("Sync failed: {:?}", error),
235        }
236    }
237
238    fn wait(&self) {
239        let delay = self.alarm.ticks_from_ms(WAIT_MS);
240        let now = self.alarm.now();
241        self.alarm.set_alarm(now, delay);
242    }
243}
244
245impl<A: Alarm<'static>> LogReadClient for LogTest<A> {
246    fn read_done(&self, buffer: &'static mut [u8], length: usize, error: Result<(), ErrorCode>) {
247        match error {
248            Ok(()) => {
249                // Verify correct value was read.
250                assert!(length > 0);
251                for i in 0..length {
252                    if buffer[i] != length as u8 {
253                        panic!(
254                            "Read incorrect value {} at index {}, expected {}",
255                            buffer[i], i, length
256                        );
257                    }
258                }
259
260                debug!("Successful read of size {}", length);
261                self.buffer.replace(buffer);
262                self.wait();
263            }
264            _ => {
265                panic!("Read failed unexpectedly!");
266            }
267        }
268    }
269
270    fn seek_done(&self, _error: Result<(), ErrorCode>) {
271        unreachable!();
272    }
273}
274
275impl<A: Alarm<'static>> LogWriteClient for LogTest<A> {
276    fn append_done(
277        &self,
278        buffer: &'static mut [u8],
279        length: usize,
280        records_lost: bool,
281        error: Result<(), ErrorCode>,
282    ) {
283        assert!(!records_lost);
284        match error {
285            Ok(()) => {
286                debug!("Write succeeded on {} byte write, as expected", length);
287
288                self.buffer.replace(buffer);
289                self.op_index.increment();
290                self.wait();
291            }
292            error => panic!("WRITE FAILED IN CALLBACK: {:?}", error),
293        }
294    }
295
296    fn sync_done(&self, error: Result<(), ErrorCode>) {
297        if error == Ok(()) {
298            debug!(
299                "SYNC DONE: READ OFFSET: {:?} / WRITE OFFSET: {:?}",
300                self.log.next_read_entry_id(),
301                self.log.log_end()
302            );
303        } else {
304            panic!("Sync failed: {:?}", error);
305        }
306
307        self.op_index.increment();
308        self.run();
309    }
310
311    fn erase_done(&self, _error: Result<(), ErrorCode>) {
312        unreachable!();
313    }
314}
315
316impl<A: Alarm<'static>> AlarmClient for LogTest<A> {
317    fn alarm(&self) {
318        self.run();
319    }
320}