* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
+use std::io::{Read, Write};
+use std::os::unix::io::FromRawFd;
use common::buffer::{Buffer, BufferUserData};
+use common::fixed15_49;
use common::gps;
use common::storage::{Storage, Error};
use common::logger::{MEMORY_SIZE, Logger, Error as LoggerError};
+type Fixed = fixed15_49::Fixed15_49;
+
struct FakeStorage {
expected: Box<[u8]>,
actual: Box<[u8]>,
Ok(())
}
+ fn erase(&mut self, address: usize) -> Result<(), Error> {
+ if (address & 4095) != 0 {
+ return Err(Error::UnalignedAddress);
+ }
+
+ for i in 0..4096 {
+ self.actual[address + i] = 0xff;
+ }
+
+ Ok(())
+
+ }
+
fn clear(&mut self) {
self.actual = vec![0xff; MEMORY_SIZE].into_boxed_slice();
}
}
+struct Pipe {
+ pipe_fd: [i32; 2],
+}
+
+impl Pipe {
+ fn new() -> Pipe {
+ let mut pipe_fd = [-1i32; 2];
+
+ unsafe {
+ extern {
+ fn pipe2(pipefd: *mut i32, flags: i32) -> i32;
+ }
+
+ const O_NONBLOCK : i32 = 0x800;
+
+ pipe2(pipe_fd.as_mut_ptr(), O_NONBLOCK);
+ }
+
+ Pipe {
+ pipe_fd: pipe_fd,
+ }
+ }
+
+ fn read_fd(&self) -> i32 {
+ self.pipe_fd[0]
+ }
+
+ fn write_fd(&self) -> i32 {
+ self.pipe_fd[1]
+ }
+}
+
// Runs a couple of recordings on fully erased flash memory.
#[test]
fn first_recording() {
let tap = gps::TimeAndPos {
system_time: 0,
unix_time: 1478026311,
- latitude: 0x73234e,
- longitude: 0x73234f,
+ latitude_deg: 0x73234e,
+ longitude_deg: 0x73234f,
+ latitude_rad: Fixed::from_f32(12.57613).to_radians(),
+ longitude_rad: Fixed::from_f32(12.576131666666667).to_radians(),
};
let recording_id = logger.start_recording(&tap);
let tap = gps::TimeAndPos {
system_time: 0,
unix_time: 1478026312,
- latitude: 0x73234e,
- longitude: 0x73234f,
+ latitude_deg: 0x73234e,
+ longitude_deg: 0x73234f,
+ latitude_rad: Fixed::from_f32(12.57613).to_radians(),
+ longitude_rad: Fixed::from_f32(12.576131666666667).to_radians(),
};
let recording_id = logger.start_recording(&tap);
let tap = gps::TimeAndPos {
system_time: 0,
unix_time: 1578425250,
- latitude: 0x73234e,
- longitude: 0x73234f,
+ latitude_deg: 0x73234e,
+ longitude_deg: 0x73234f,
+ latitude_rad: Fixed::from_f32(12.57613).to_radians(),
+ longitude_rad: Fixed::from_f32(12.576131666666667).to_radians(),
};
let recording_id = logger.start_recording(&tap);
let tap = gps::TimeAndPos {
system_time: 0,
unix_time: prev_tap.unix_time + 1,
- latitude: prev_tap.latitude + 1,
- longitude: prev_tap.longitude + 1,
+ latitude_deg: prev_tap.latitude_deg + 1,
+ longitude_deg: prev_tap.longitude_deg + 1,
+ latitude_rad: Fixed::from_f32(
+ (prev_tap.latitude_deg + 1) as f32 / 600000.0).to_radians(),
+ longitude_rad: Fixed::from_f32(
+ (prev_tap.longitude_deg + 1) as f32 / 600000.0).to_radians(),
};
logger.log(&prev_tap, &tap);
buf: *const u8,
_bufsiz: usize,
count: usize) -> isize {
- let _final_buffer = user_data as *mut [u8; 8192];
- let final_buffer = unsafe { &mut * _final_buffer };
+ let _file = user_data as *mut std::fs::File;
unsafe {
- core::ptr::copy_nonoverlapping(buf, final_buffer.as_mut_ptr(), count);
+ let file = &mut * _file;
+
+ file.write_all(std::slice::from_raw_parts(buf, count)).unwrap();
}
count as isize
// Verifies that Logger::get_recording() detects unknown recording IDs.
#[test]
fn get_recording_invalid() {
- let mut final_buffer = [0u8; 8192];
+ let pipe = Pipe::new();
+
+ let mut file = unsafe { std::fs::File::from_raw_fd(pipe.write_fd()) };
let mut yenc_buffer_space = [0u8; 8192];
let mut yenc_buffer = Buffer::alloc();
- let user_data = (&mut final_buffer as *mut [u8; 8192]) as *mut BufferUserData;
+ let user_data = (&mut file as *mut std::fs::File) as *mut BufferUserData;
yenc_buffer.init(yenc_buffer_space.as_mut_ptr(),
yenc_buffer_space.len(),
flush_write_buffer,
user_data);
+
let mut fake_storage = FakeStorage::new();
let mut logger = Logger::new(&mut fake_storage);
// Verifies that Logger::get_recording() can retrieve finished recordings.
#[test]
fn get_recording_valid() {
- let mut final_buffer = [0u8; 8192];
+ let pipe = Pipe::new();
+
+ let mut file = unsafe { std::fs::File::from_raw_fd(pipe.write_fd()) };
let mut yenc_buffer_space = [0u8; 8192];
let mut yenc_buffer = Buffer::alloc();
- let user_data = (&mut final_buffer as *mut [u8; 8192]) as *mut BufferUserData;
+ let user_data = (&mut file as *mut std::fs::File) as *mut BufferUserData;
yenc_buffer.init(yenc_buffer_space.as_mut_ptr(),
yenc_buffer_space.len(),
let tap0 = gps::TimeAndPos {
system_time: 0,
unix_time: 1478026311,
- latitude: 0x73234e,
- longitude: 0x73234f,
+ latitude_deg: 0x73234e,
+ longitude_deg: 0x73234f,
+ latitude_rad: Fixed::from_f32(12.57613).to_radians(),
+ longitude_rad: Fixed::from_f32(12.576131666666667).to_radians(),
};
logger.start_recording(&tap0);
let tap1 = gps::TimeAndPos {
system_time: 0,
unix_time: 1478026311 + 1,
- latitude: 0x73234e + 5,
- longitude: 0x73234f + 5,
+ latitude_deg: 0x73234e + 5,
+ longitude_deg: 0x73234f + 5,
+ latitude_rad: Fixed::from_f32(12.576138333333333).to_radians(),
+ longitude_rad: Fixed::from_f32(12.57614).to_radians(),
};
logger.log(&tap0, &tap1);
let tap2 = gps::TimeAndPos {
system_time: 0,
unix_time: 1478026311 + 2,
- latitude: 0x73234e + 10,
- longitude: 0x73234f + 10,
+ latitude_deg: 0x73234e + 10,
+ longitude_deg: 0x73234f + 10,
+ latitude_rad: Fixed::from_f32(12.576146666666666).to_radians(),
+ longitude_rad: Fixed::from_f32(12.576148333333334).to_radians(),
};
logger.log(&tap1, &tap2);
assert!(logger.get_recording(1, &mut yenc_buffer).is_ok());
}
+
+// Verifies that Logger::list_recordings() shows an empty listing
+// if there are no recordings.
+#[test]
+fn list_recordings0() {
+ let pipe = Pipe::new();
+
+ let mut file = unsafe { std::fs::File::from_raw_fd(pipe.write_fd()) };
+
+ let mut ls_buffer_space = [0u8; 4096];
+ let mut ls_buffer = Buffer::alloc();
+
+ let user_data = (&mut file as *mut std::fs::File) as *mut BufferUserData;
+
+ ls_buffer.init(ls_buffer_space.as_mut_ptr(),
+ ls_buffer_space.len(),
+ flush_write_buffer,
+ user_data);
+
+ let mut fake_storage = FakeStorage::new();
+
+ let mut logger = Logger::new(&mut fake_storage);
+ logger.init();
+
+ logger.list_recordings(&mut ls_buffer);
+
+ let mut file = unsafe { std::fs::File::from_raw_fd(pipe.read_fd()) };
+
+ let mut listing_raw = [0u8; 256];
+
+ // There are no recordings, so there's no data in the pipe either.
+ assert!(file.read(&mut listing_raw).is_err());
+}
+
+// Verifies that Logger::list_recordings() shows a listing
+// of a single recording in the very first sector.
+#[test]
+fn list_recordings1() {
+ let pipe = Pipe::new();
+
+ let mut file = unsafe { std::fs::File::from_raw_fd(pipe.write_fd()) };
+
+ let mut ls_buffer_space = [0u8; 4096];
+ let mut ls_buffer = Buffer::alloc();
+
+ let user_data = (&mut file as *mut std::fs::File) as *mut BufferUserData;
+
+ ls_buffer.init(ls_buffer_space.as_mut_ptr(),
+ ls_buffer_space.len(),
+ flush_write_buffer,
+ user_data);
+
+ let mut fake_storage = FakeStorage::new();
+
+ // Mark first sector as in use.
+ let recording0 = [
+ // Header:
+ 0x01, 0x00, 0x01, 0x00,
+ 0xa2, 0xdb, 0x14, 0x5e,
+ ];
+
+ 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();
+
+ logger.list_recordings(&mut ls_buffer);
+
+ let mut file = unsafe { std::fs::File::from_raw_fd(pipe.read_fd()) };
+
+ let mut listing_raw = [0u8; 256];
+ let num_bytes_read = file.read(&mut listing_raw).unwrap();
+
+ let expected_bytes = b"\
+2020-01-07 19:27:30 4K 1\n";
+
+ assert_eq!(String::from_utf8(expected_bytes.to_vec()).unwrap(),
+ String::from_utf8(listing_raw[0..num_bytes_read].to_vec()).unwrap());
+}
+
+// Verifies that Logger::list_recordings() handles sectors holding
+// additional recording data.
+#[test]
+fn list_recording1_multi_sector() {
+ let pipe = Pipe::new();
+
+ let mut file = unsafe { std::fs::File::from_raw_fd(pipe.write_fd()) };
+
+ let mut ls_buffer_space = [0u8; 4096];
+ let mut ls_buffer = Buffer::alloc();
+
+ let user_data = (&mut file as *mut std::fs::File) as *mut BufferUserData;
+
+ ls_buffer.init(ls_buffer_space.as_mut_ptr(),
+ ls_buffer_space.len(),
+ flush_write_buffer,
+ user_data);
+
+ let mut fake_storage = FakeStorage::new();
+
+ // The first sector starts recording 1.
+ let header0 = [
+ // Header:
+ 0x01, 0x00, 0x01, 0x00,
+ 0x75, 0x18, 0x17, 0x5e,
+ ];
+
+ // The second sector has additional data for recording 1.
+ let header1 = [
+ 0x03, 0x00, 0x01, 0x00,
+ ];
+
+ fake_storage.actual[0..header0.len()].copy_from_slice(&header0);
+ fake_storage.actual[4096..4096 + header1.len()].copy_from_slice(&header1);
+
+ let mut logger = Logger::new(&mut fake_storage);
+ logger.init();
+
+ logger.list_recordings(&mut ls_buffer);
+
+ let mut file = unsafe { std::fs::File::from_raw_fd(pipe.read_fd()) };
+
+ let mut listing_raw = [0u8; 4096];
+ let num_bytes_read = file.read(&mut listing_raw).unwrap();
+
+ let expected_bytes = b"\
+2020-01-09 12:11:33 8K 1\n";
+
+ assert_eq!(String::from_utf8(expected_bytes.to_vec()).unwrap(),
+ String::from_utf8(listing_raw[0..num_bytes_read].to_vec()).unwrap());
+}
+
+// Verifies that Logger::remove_recording() reports an error if there is no
+// recording with the given ID.
+#[test]
+fn remove_recording0() {
+ let mut fake_storage = FakeStorage::new();
+
+ let mut logger = Logger::new(&mut fake_storage);
+ logger.init();
+
+ let result = logger.remove_recording(42);
+ assert_eq!(LoggerError::NoSuchRecording, result.unwrap_err());
+}
+
+// Verifies that Logger::remove_recording() clears consecutive sectors
+// belonging to the recording identified by the given ID.
+#[test]
+fn remove_recording1() {
+ let mut fake_storage = FakeStorage::new();
+
+ // Mark first sector as in use.
+ let header0 = [
+ 0x01, 0x00, 0x01, 0x00,
+ 0xa2, 0xdb, 0x14, 0x5e,
+ ];
+
+ // Mark second sector as in use by the same recording.
+ let header1 = [
+ 0x03, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ ];
+
+ // Mark third sector as in use by another recording.
+ let header2 = [
+ 0x01, 0x00, 0x02, 0x00,
+ 0xa2, 0xdb, 0x14, 0x5f,
+ ];
+
+ fake_storage.actual[0..header0.len()].copy_from_slice(&header0);
+ fake_storage.actual[4096..4096 + header1.len()].copy_from_slice(&header1);
+ fake_storage.actual[8192..8192 + header2.len()].copy_from_slice(&header2);
+
+ let mut logger = Logger::new(&mut fake_storage);
+ logger.init();
+
+ assert!(logger.remove_recording(1).is_ok());
+
+ let cleared = [
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ ];
+
+ fake_storage.expected[0..cleared.len()].copy_from_slice(&cleared);
+ fake_storage.expected[4096..4096 + header1.len()].copy_from_slice(&cleared);
+ fake_storage.expected[8192..8192 + header2.len()].copy_from_slice(&header2);
+
+ assert_eq!(fake_storage.expected, fake_storage.actual);
+}
+
+#[test]
+fn pace0() {
+ let mut fake_storage = FakeStorage::new();
+
+ let mut logger = Logger::new(&mut fake_storage);
+ logger.init();
+
+ let lat_lon = [
+ (49.372117, 8.820219),
+ (49.372229, 8.821636),
+ ];
+
+ let template = gps::TimeAndPos {
+ system_time: 0,
+ unix_time: 0,
+ latitude_deg: 0,
+ longitude_deg: 0,
+ latitude_rad: Fixed::from_f32(0.0),
+ longitude_rad: Fixed::from_f32(0.0),
+ };
+
+ let mut taps = [template; 11];
+
+ for i in 0..taps.len() {
+ taps[i].system_time = (i as u32) * 100;
+ taps[i].unix_time = i as u32;
+ taps[i].latitude_rad = Fixed::from_f32(lat_lon[i % lat_lon.len()].0).to_radians();
+ taps[i].longitude_rad = Fixed::from_f32(lat_lon[i % lat_lon.len()].1).to_radians();
+ }
+
+ logger.start_recording(&taps[0]);
+
+ for i in 0..(taps.len() - 1) {
+ logger.log(&taps[i], &taps[i + 1]);
+ }
+
+ assert_eq!(logger.pace_s, 10);
+}