--- /dev/null
+/*
+ * Copyright (c) 2020 Tilman Sauerbeck (tilman at code-monkey de)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+use common::gps;
+use common::storage::{Storage, Error};
+use common::logger::*;
+
+struct FakeStorage {
+ expected: Box<[u8]>,
+ actual: Box<[u8]>,
+}
+
+impl FakeStorage {
+ fn new() -> FakeStorage {
+ FakeStorage {
+ expected: vec![0xff; MEMORY_SIZE].into_boxed_slice(),
+ actual: vec![0xff; MEMORY_SIZE].into_boxed_slice(),
+ }
+ }
+}
+
+impl Storage for FakeStorage {
+ fn size(&self) -> usize {
+ MEMORY_SIZE
+ }
+
+ fn read(&self, address: usize, buffer: &mut [u8]) {
+ for i in 0..buffer.len() {
+ buffer[i] = self.actual[address + i];
+ }
+ }
+
+ fn write(&mut self, address: usize, buffer: &[u8; 4096]) -> Result<(), Error> {
+ if (address & 4095) != 0 {
+ return Err(Error::UnalignedAddress);
+ }
+
+ for i in 0..buffer.len() {
+ self.actual[address + i] = buffer[i];
+ }
+
+ Ok(())
+ }
+
+ fn clear(&mut self) {
+ self.actual = vec![0xff; MEMORY_SIZE].into_boxed_slice();
+ }
+}
+
+// Runs a couple of recordings on fully erased flash memory.
+#[test]
+fn first_recording() {
+ let mut fake_storage = FakeStorage::new();
+
+ let mut logger = Logger::new(&mut fake_storage);
+ logger.init();
+
+ let tap = gps::TimeAndPos {
+ system_time: 0,
+ unix_time: 1478026311,
+ latitude: 0x73234e,
+ longitude: 0x73234f,
+ };
+
+ let recording_id = logger.start_recording(&tap);
+ assert_eq!(1, recording_id);
+
+ let sectors_written = logger.stop_recording(&tap);
+ assert_eq!(1, sectors_written);
+
+ let expected = [
+ // Header:
+ 0x01, 0x00, 0x01, 0x00,
+ 0x47, 0xe4, 0x18, 0x58,
+
+ // First point:
+ 0x03,
+ 0x00,
+ 0x9c, 0x8d, 0x99, 0x07,
+ 0x9e, 0x8d, 0x99, 0x07,
+
+ // Sentinel:
+ 0xff, 0xff, 0xff, 0xff, 0x0f,
+
+ // Footer:
+ 0x00, 0x00
+ ];
+
+ let start = 0;
+ let end = start + expected.len();
+ fake_storage.expected[start..end].copy_from_slice(&expected);
+
+ assert_eq!(fake_storage.expected, fake_storage.actual);
+}
+
+#[test]
+fn second_recording() {
+ let mut fake_storage = FakeStorage::new();
+
+ // Mark first sector as in use.
+ let recording0 = [
+ // Header:
+ 0x01, 0x00, 0x01, 0x00,
+ ];
+
+ fake_storage.expected[0..recording0.len()].copy_from_slice(&recording0);
+ fake_storage.actual[0..recording0.len()].copy_from_slice(&recording0);
+
+ let mut logger = Logger::new(&mut fake_storage);
+ logger.init();
+
+ let tap = gps::TimeAndPos {
+ system_time: 0,
+ unix_time: 1478026312,
+ latitude: 0x73234e,
+ longitude: 0x73234f,
+ };
+
+ let recording_id = logger.start_recording(&tap);
+ assert_eq!(2, recording_id);
+
+ let sectors_written = logger.stop_recording(&tap);
+ assert_eq!(1, sectors_written);
+
+ let expected = [
+ // Header:
+ 0x01, 0x00, 0x02, 0x00,
+ 0x48, 0xe4, 0x18, 0x58,
+
+ // First point:
+ 0x03,
+ 0x00,
+ 0x9c, 0x8d, 0x99, 0x07,
+ 0x9e, 0x8d, 0x99, 0x07,
+
+ // Sentinel:
+ 0xff, 0xff, 0xff, 0xff, 0x0f,
+
+ // Footer:
+ 0x00, 0x00
+ ];
+
+ let start = 4096;
+ let end = start + expected.len();
+ fake_storage.expected[start..end].copy_from_slice(&expected);
+
+ assert_eq!(fake_storage.expected, fake_storage.actual);
+}
+
+#[test]
+fn multi_sector_recording() {
+ let mut fake_storage = FakeStorage::new();
+
+ let mut logger = Logger::new(&mut fake_storage);
+ logger.init();
+
+ let tap = gps::TimeAndPos {
+ system_time: 0,
+ unix_time: 1578425250,
+ latitude: 0x73234e,
+ longitude: 0x73234f,
+ };
+
+ let recording_id = logger.start_recording(&tap);
+ assert_eq!(1, recording_id);
+
+ let mut prev_tap = tap;
+
+ for _ in 0..2048 {
+ let tap = gps::TimeAndPos {
+ system_time: 0,
+ unix_time: prev_tap.unix_time + 1,
+ latitude: prev_tap.latitude + 1,
+ longitude: prev_tap.longitude + 1,
+ };
+
+ logger.log(&prev_tap, &tap);
+
+ prev_tap = tap;
+ }
+
+ let sectors_written = logger.stop_recording(&tap);
+ assert_eq!(2, sectors_written);
+
+ let header0 = [
+ 0x01, 0x00, 0x01, 0x00,
+ ];
+
+ let header1 = [
+ 0x03, 0x00, 0x01, 0x00,
+ ];
+
+ assert_eq!(header0, fake_storage.actual[0..(0 + header0.len())]);
+ assert_eq!(header1, fake_storage.actual[4096..(4096 + header1.len())]);
+}