test: Add a first test for Logger::list_recordings().
[gps-watch.git] / test / logger_test.rs
1 /*
2  * Copyright (c) 2020 Tilman Sauerbeck (tilman at code-monkey de)
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files (the
6  * "Software"), to deal in the Software without restriction, including
7  * without limitation the rights to use, copy, modify, merge, publish,
8  * distribute, sublicense, and/or sell copies of the Software, and to
9  * permit persons to whom the Software is furnished to do so, subject to
10  * the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23
24 use std::io::{Read, Write};
25 use std::os::unix::io::FromRawFd;
26 use common::buffer::{Buffer, BufferUserData};
27 use common::gps;
28 use common::storage::{Storage, Error};
29 use common::logger::{MEMORY_SIZE, Logger, Error as LoggerError};
30
31 struct FakeStorage {
32     expected: Box<[u8]>,
33     actual: Box<[u8]>,
34 }
35
36 impl FakeStorage {
37     fn new() -> FakeStorage {
38         FakeStorage {
39             expected: vec![0xff; MEMORY_SIZE].into_boxed_slice(),
40             actual: vec![0xff; MEMORY_SIZE].into_boxed_slice(),
41         }
42     }
43 }
44
45 impl Storage for FakeStorage {
46     fn size(&self) -> usize {
47         MEMORY_SIZE
48     }
49
50     fn read(&self, address: usize, buffer: &mut [u8]) {
51         for i in 0..buffer.len() {
52             buffer[i] = self.actual[address + i];
53         }
54     }
55
56     fn write(&mut self, address: usize, buffer: &[u8; 4096]) -> Result<(), Error> {
57         if (address & 4095) != 0 {
58             return Err(Error::UnalignedAddress);
59         }
60
61         for i in 0..buffer.len() {
62             self.actual[address + i] = buffer[i];
63         }
64
65         Ok(())
66     }
67
68     fn clear(&mut self) {
69         self.actual = vec![0xff; MEMORY_SIZE].into_boxed_slice();
70     }
71 }
72
73 struct Pipe {
74     pipe_fd: [i32; 2],
75 }
76
77 impl Pipe {
78     fn new() -> Pipe {
79         let mut pipe_fd = [-1i32; 2];
80
81         unsafe {
82             extern {
83                 fn pipe(pipefd: *mut i32) -> i32;
84             }
85
86             pipe(pipe_fd.as_mut_ptr());
87         }
88
89         Pipe {
90             pipe_fd: pipe_fd,
91         }
92     }
93
94     fn read_fd(&self) -> i32 {
95         self.pipe_fd[0]
96     }
97
98     fn write_fd(&self) -> i32 {
99         self.pipe_fd[1]
100     }
101 }
102
103 // Runs a couple of recordings on fully erased flash memory.
104 #[test]
105 fn first_recording() {
106     let mut fake_storage = FakeStorage::new();
107
108     let mut logger = Logger::new(&mut fake_storage);
109     logger.init();
110
111     let tap = gps::TimeAndPos {
112         system_time: 0,
113         unix_time: 1478026311,
114         latitude: 0x73234e,
115         longitude: 0x73234f,
116     };
117
118     let recording_id = logger.start_recording(&tap);
119     assert_eq!(1, recording_id);
120
121     let sectors_written = logger.stop_recording(&tap);
122     assert_eq!(1, sectors_written);
123
124     let expected = [
125         // Header:
126         0x01, 0x00, 0x01, 0x00,
127         0x47, 0xe4, 0x18, 0x58,
128
129         // First point:
130         0x03,
131         0x00,
132         0x9c, 0x8d, 0x99, 0x07,
133         0x9e, 0x8d, 0x99, 0x07,
134
135         // Sentinel:
136         0xff, 0xff, 0xff, 0xff, 0x0f,
137
138         // Footer:
139         0x00, 0x00
140     ];
141
142     let start = 0;
143     let end = start + expected.len();
144     fake_storage.expected[start..end].copy_from_slice(&expected);
145
146     assert_eq!(fake_storage.expected, fake_storage.actual);
147 }
148
149 #[test]
150 fn second_recording() {
151     let mut fake_storage = FakeStorage::new();
152
153     // Mark first sector as in use.
154     let recording0 = [
155         // Header:
156         0x01, 0x00, 0x01, 0x00,
157     ];
158
159     fake_storage.expected[0..recording0.len()].copy_from_slice(&recording0);
160     fake_storage.actual[0..recording0.len()].copy_from_slice(&recording0);
161
162     let mut logger = Logger::new(&mut fake_storage);
163     logger.init();
164
165     let tap = gps::TimeAndPos {
166         system_time: 0,
167         unix_time: 1478026312,
168         latitude: 0x73234e,
169         longitude: 0x73234f,
170     };
171
172     let recording_id = logger.start_recording(&tap);
173     assert_eq!(2, recording_id);
174
175     let sectors_written = logger.stop_recording(&tap);
176     assert_eq!(1, sectors_written);
177
178     let expected = [
179         // Header:
180         0x01, 0x00, 0x02, 0x00,
181         0x48, 0xe4, 0x18, 0x58,
182
183         // First point:
184         0x03,
185         0x00,
186         0x9c, 0x8d, 0x99, 0x07,
187         0x9e, 0x8d, 0x99, 0x07,
188
189         // Sentinel:
190         0xff, 0xff, 0xff, 0xff, 0x0f,
191
192         // Footer:
193         0x00, 0x00
194     ];
195
196     let start = 4096;
197     let end = start + expected.len();
198     fake_storage.expected[start..end].copy_from_slice(&expected);
199
200     assert_eq!(fake_storage.expected, fake_storage.actual);
201 }
202
203 #[test]
204 fn multi_sector_recording() {
205     let mut fake_storage = FakeStorage::new();
206
207     let mut logger = Logger::new(&mut fake_storage);
208     logger.init();
209
210     let tap = gps::TimeAndPos {
211         system_time: 0,
212         unix_time: 1578425250,
213         latitude: 0x73234e,
214         longitude: 0x73234f,
215     };
216
217     let recording_id = logger.start_recording(&tap);
218     assert_eq!(1, recording_id);
219
220     let mut prev_tap = tap;
221
222     for _ in 0..2048 {
223         let tap = gps::TimeAndPos {
224             system_time: 0,
225             unix_time: prev_tap.unix_time + 1,
226             latitude: prev_tap.latitude + 1,
227             longitude: prev_tap.longitude + 1,
228         };
229
230         logger.log(&prev_tap, &tap);
231
232         prev_tap = tap;
233     }
234
235     let sectors_written = logger.stop_recording(&tap);
236     assert_eq!(2, sectors_written);
237
238     let header0 = [
239         0x01, 0x00, 0x01, 0x00,
240     ];
241
242     let header1 = [
243         0x03, 0x00, 0x01, 0x00,
244     ];
245
246     assert_eq!(header0, fake_storage.actual[0..(0 + header0.len())]);
247     assert_eq!(header1, fake_storage.actual[4096..(4096 + header1.len())]);
248 }
249
250 extern "C" fn flush_write_buffer(user_data: *mut BufferUserData,
251                                  buf: *const u8,
252                                  _bufsiz: usize,
253                                  count: usize) -> isize {
254     let _file = user_data as *mut std::fs::File;
255
256     unsafe {
257         let file = &mut * _file;
258
259         file.write_all(std::slice::from_raw_parts(buf, count)).unwrap();
260     }
261
262     count as isize
263 }
264
265 // Verifies that Logger::get_recording() detects unknown recording IDs.
266 #[test]
267 fn get_recording_invalid() {
268     let pipe = Pipe::new();
269
270     let mut file = unsafe { std::fs::File::from_raw_fd(pipe.write_fd()) };
271
272     let mut yenc_buffer_space = [0u8; 8192];
273     let mut yenc_buffer = Buffer::alloc();
274
275     let user_data = (&mut file as *mut std::fs::File) as *mut BufferUserData;
276
277     yenc_buffer.init(yenc_buffer_space.as_mut_ptr(),
278                      yenc_buffer_space.len(),
279                      flush_write_buffer,
280                      user_data);
281
282     let mut fake_storage = FakeStorage::new();
283
284     let mut logger = Logger::new(&mut fake_storage);
285     logger.init();
286
287     // Zero is never a valid recording id.
288     let result = logger.get_recording(0, &mut yenc_buffer);
289     assert_eq!(LoggerError::NoSuchRecording, result.unwrap_err());
290
291     let result = logger.get_recording(1, &mut yenc_buffer);
292     assert_eq!(LoggerError::NoSuchRecording, result.unwrap_err());
293 }
294
295 // Verifies that Logger::get_recording() can retrieve finished recordings.
296 #[test]
297 fn get_recording_valid() {
298     let pipe = Pipe::new();
299
300     let mut file = unsafe { std::fs::File::from_raw_fd(pipe.write_fd()) };
301
302     let mut yenc_buffer_space = [0u8; 8192];
303     let mut yenc_buffer = Buffer::alloc();
304
305     let user_data = (&mut file as *mut std::fs::File) as *mut BufferUserData;
306
307     yenc_buffer.init(yenc_buffer_space.as_mut_ptr(),
308                      yenc_buffer_space.len(),
309                      flush_write_buffer,
310                      user_data);
311
312     let mut fake_storage = FakeStorage::new();
313
314     let mut logger = Logger::new(&mut fake_storage);
315     logger.init();
316
317     let result = logger.get_recording(1, &mut yenc_buffer);
318     assert_eq!(LoggerError::NoSuchRecording, result.unwrap_err());
319
320     let tap0 = gps::TimeAndPos {
321         system_time: 0,
322         unix_time: 1478026311,
323         latitude: 0x73234e,
324         longitude: 0x73234f,
325     };
326
327     logger.start_recording(&tap0);
328
329     let tap1 = gps::TimeAndPos {
330         system_time: 0,
331         unix_time: 1478026311 + 1,
332         latitude: 0x73234e + 5,
333         longitude: 0x73234f + 5,
334     };
335
336     logger.log(&tap0, &tap1);
337
338     let tap2 = gps::TimeAndPos {
339         system_time: 0,
340         unix_time: 1478026311 + 2,
341         latitude: 0x73234e + 10,
342         longitude: 0x73234f + 10,
343     };
344
345     logger.log(&tap1, &tap2);
346
347     logger.stop_recording(&tap2);
348
349     assert!(logger.get_recording(1, &mut yenc_buffer).is_ok());
350 }
351
352 // Verifies that Logger::list_recordings() shows a listing
353 // of a single recording in the very first sector.
354 #[test]
355 fn list_recordings1() {
356     let pipe = Pipe::new();
357
358     let mut file = unsafe { std::fs::File::from_raw_fd(pipe.write_fd()) };
359
360     let mut ls_buffer_space = [0u8; 4096];
361     let mut ls_buffer = Buffer::alloc();
362
363     let user_data = (&mut file as *mut std::fs::File) as *mut BufferUserData;
364
365     ls_buffer.init(ls_buffer_space.as_mut_ptr(),
366                    ls_buffer_space.len(),
367                    flush_write_buffer,
368                    user_data);
369
370     let mut fake_storage = FakeStorage::new();
371
372     // Mark first sector as in use.
373     let recording0 = [
374         // Header:
375         0x01, 0x00, 0x01, 0x00,
376         0xa2, 0xdb, 0x14, 0x5e,
377     ];
378
379     fake_storage.expected[0..recording0.len()].copy_from_slice(&recording0);
380     fake_storage.actual[0..recording0.len()].copy_from_slice(&recording0);
381
382     let mut logger = Logger::new(&mut fake_storage);
383     logger.init();
384
385     logger.list_recordings(&mut ls_buffer);
386
387     let mut file = unsafe { std::fs::File::from_raw_fd(pipe.read_fd()) };
388
389     let mut listing_raw = [0u8; 256];
390     let num_bytes_read = file.read(&mut listing_raw).unwrap();
391
392     let expected_bytes = b"\
393 2020-01-07 19:27:30              1\n";
394
395     assert_eq!(String::from_utf8(expected_bytes.to_vec()).unwrap(),
396                String::from_utf8(listing_raw[0..num_bytes_read].to_vec()).unwrap());
397 }