common: Rename TimeAndPos::latitude to latitude_deg.
[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::fixed15_49;
28 use common::gps;
29 use common::storage::{Storage, Error};
30 use common::logger::{MEMORY_SIZE, Logger, Error as LoggerError};
31
32 type Fixed = fixed15_49::Fixed15_49;
33
34 struct FakeStorage {
35     expected: Box<[u8]>,
36     actual: Box<[u8]>,
37 }
38
39 impl FakeStorage {
40     fn new() -> FakeStorage {
41         FakeStorage {
42             expected: vec![0xff; MEMORY_SIZE].into_boxed_slice(),
43             actual: vec![0xff; MEMORY_SIZE].into_boxed_slice(),
44         }
45     }
46 }
47
48 impl Storage for FakeStorage {
49     fn size(&self) -> usize {
50         MEMORY_SIZE
51     }
52
53     fn read(&self, address: usize, buffer: &mut [u8]) {
54         for i in 0..buffer.len() {
55             buffer[i] = self.actual[address + i];
56         }
57     }
58
59     fn write(&mut self, address: usize, buffer: &[u8; 4096]) -> Result<(), Error> {
60         if (address & 4095) != 0 {
61             return Err(Error::UnalignedAddress);
62         }
63
64         for i in 0..buffer.len() {
65             self.actual[address + i] = buffer[i];
66         }
67
68         Ok(())
69     }
70
71     fn clear(&mut self) {
72         self.actual = vec![0xff; MEMORY_SIZE].into_boxed_slice();
73     }
74 }
75
76 struct Pipe {
77     pipe_fd: [i32; 2],
78 }
79
80 impl Pipe {
81     fn new() -> Pipe {
82         let mut pipe_fd = [-1i32; 2];
83
84         unsafe {
85             extern {
86                 fn pipe2(pipefd: *mut i32, flags: i32) -> i32;
87             }
88
89             const O_NONBLOCK : i32 = 0x800;
90
91             pipe2(pipe_fd.as_mut_ptr(), O_NONBLOCK);
92         }
93
94         Pipe {
95             pipe_fd: pipe_fd,
96         }
97     }
98
99     fn read_fd(&self) -> i32 {
100         self.pipe_fd[0]
101     }
102
103     fn write_fd(&self) -> i32 {
104         self.pipe_fd[1]
105     }
106 }
107
108 // Runs a couple of recordings on fully erased flash memory.
109 #[test]
110 fn first_recording() {
111     let mut fake_storage = FakeStorage::new();
112
113     let mut logger = Logger::new(&mut fake_storage);
114     logger.init();
115
116     let tap = gps::TimeAndPos {
117         system_time: 0,
118         unix_time: 1478026311,
119         latitude_deg: 0x73234e,
120         longitude_deg: 0x73234f,
121         latitude_rad: Fixed::from_f32(12.57613).to_radians(),
122         longitude_rad: Fixed::from_f32(12.576131666666667).to_radians(),
123     };
124
125     let recording_id = logger.start_recording(&tap);
126     assert_eq!(1, recording_id);
127
128     let sectors_written = logger.stop_recording(&tap);
129     assert_eq!(1, sectors_written);
130
131     let expected = [
132         // Header:
133         0x01, 0x00, 0x01, 0x00,
134         0x47, 0xe4, 0x18, 0x58,
135
136         // First point:
137         0x03,
138         0x00,
139         0x9c, 0x8d, 0x99, 0x07,
140         0x9e, 0x8d, 0x99, 0x07,
141
142         // Sentinel:
143         0xff, 0xff, 0xff, 0xff, 0x0f,
144
145         // Footer:
146         0x00, 0x00
147     ];
148
149     let start = 0;
150     let end = start + expected.len();
151     fake_storage.expected[start..end].copy_from_slice(&expected);
152
153     assert_eq!(fake_storage.expected, fake_storage.actual);
154 }
155
156 #[test]
157 fn second_recording() {
158     let mut fake_storage = FakeStorage::new();
159
160     // Mark first sector as in use.
161     let recording0 = [
162         // Header:
163         0x01, 0x00, 0x01, 0x00,
164     ];
165
166     fake_storage.expected[0..recording0.len()].copy_from_slice(&recording0);
167     fake_storage.actual[0..recording0.len()].copy_from_slice(&recording0);
168
169     let mut logger = Logger::new(&mut fake_storage);
170     logger.init();
171
172     let tap = gps::TimeAndPos {
173         system_time: 0,
174         unix_time: 1478026312,
175         latitude_deg: 0x73234e,
176         longitude_deg: 0x73234f,
177         latitude_rad: Fixed::from_f32(12.57613).to_radians(),
178         longitude_rad: Fixed::from_f32(12.576131666666667).to_radians(),
179     };
180
181     let recording_id = logger.start_recording(&tap);
182     assert_eq!(2, recording_id);
183
184     let sectors_written = logger.stop_recording(&tap);
185     assert_eq!(1, sectors_written);
186
187     let expected = [
188         // Header:
189         0x01, 0x00, 0x02, 0x00,
190         0x48, 0xe4, 0x18, 0x58,
191
192         // First point:
193         0x03,
194         0x00,
195         0x9c, 0x8d, 0x99, 0x07,
196         0x9e, 0x8d, 0x99, 0x07,
197
198         // Sentinel:
199         0xff, 0xff, 0xff, 0xff, 0x0f,
200
201         // Footer:
202         0x00, 0x00
203     ];
204
205     let start = 4096;
206     let end = start + expected.len();
207     fake_storage.expected[start..end].copy_from_slice(&expected);
208
209     assert_eq!(fake_storage.expected, fake_storage.actual);
210 }
211
212 #[test]
213 fn multi_sector_recording() {
214     let mut fake_storage = FakeStorage::new();
215
216     let mut logger = Logger::new(&mut fake_storage);
217     logger.init();
218
219     let tap = gps::TimeAndPos {
220         system_time: 0,
221         unix_time: 1578425250,
222         latitude_deg: 0x73234e,
223         longitude_deg: 0x73234f,
224         latitude_rad: Fixed::from_f32(12.57613).to_radians(),
225         longitude_rad: Fixed::from_f32(12.576131666666667).to_radians(),
226     };
227
228     let recording_id = logger.start_recording(&tap);
229     assert_eq!(1, recording_id);
230
231     let mut prev_tap = tap;
232
233     for _ in 0..2048 {
234         let tap = gps::TimeAndPos {
235             system_time: 0,
236             unix_time: prev_tap.unix_time + 1,
237             latitude_deg: prev_tap.latitude_deg + 1,
238             longitude_deg: prev_tap.longitude_deg + 1,
239             latitude_rad: Fixed::from_f32(
240                 (prev_tap.latitude_deg + 1) as f32 / 600000.0).to_radians(),
241             longitude_rad: Fixed::from_f32(
242                 (prev_tap.longitude_deg + 1) as f32 / 600000.0).to_radians(),
243         };
244
245         logger.log(&prev_tap, &tap);
246
247         prev_tap = tap;
248     }
249
250     let sectors_written = logger.stop_recording(&tap);
251     assert_eq!(2, sectors_written);
252
253     let header0 = [
254         0x01, 0x00, 0x01, 0x00,
255     ];
256
257     let header1 = [
258         0x03, 0x00, 0x01, 0x00,
259     ];
260
261     assert_eq!(header0, fake_storage.actual[0..(0 + header0.len())]);
262     assert_eq!(header1, fake_storage.actual[4096..(4096 + header1.len())]);
263 }
264
265 extern "C" fn flush_write_buffer(user_data: *mut BufferUserData,
266                                  buf: *const u8,
267                                  _bufsiz: usize,
268                                  count: usize) -> isize {
269     let _file = user_data as *mut std::fs::File;
270
271     unsafe {
272         let file = &mut * _file;
273
274         file.write_all(std::slice::from_raw_parts(buf, count)).unwrap();
275     }
276
277     count as isize
278 }
279
280 // Verifies that Logger::get_recording() detects unknown recording IDs.
281 #[test]
282 fn get_recording_invalid() {
283     let pipe = Pipe::new();
284
285     let mut file = unsafe { std::fs::File::from_raw_fd(pipe.write_fd()) };
286
287     let mut yenc_buffer_space = [0u8; 8192];
288     let mut yenc_buffer = Buffer::alloc();
289
290     let user_data = (&mut file as *mut std::fs::File) as *mut BufferUserData;
291
292     yenc_buffer.init(yenc_buffer_space.as_mut_ptr(),
293                      yenc_buffer_space.len(),
294                      flush_write_buffer,
295                      user_data);
296
297     let mut fake_storage = FakeStorage::new();
298
299     let mut logger = Logger::new(&mut fake_storage);
300     logger.init();
301
302     // Zero is never a valid recording id.
303     let result = logger.get_recording(0, &mut yenc_buffer);
304     assert_eq!(LoggerError::NoSuchRecording, result.unwrap_err());
305
306     let result = logger.get_recording(1, &mut yenc_buffer);
307     assert_eq!(LoggerError::NoSuchRecording, result.unwrap_err());
308 }
309
310 // Verifies that Logger::get_recording() can retrieve finished recordings.
311 #[test]
312 fn get_recording_valid() {
313     let pipe = Pipe::new();
314
315     let mut file = unsafe { std::fs::File::from_raw_fd(pipe.write_fd()) };
316
317     let mut yenc_buffer_space = [0u8; 8192];
318     let mut yenc_buffer = Buffer::alloc();
319
320     let user_data = (&mut file as *mut std::fs::File) as *mut BufferUserData;
321
322     yenc_buffer.init(yenc_buffer_space.as_mut_ptr(),
323                      yenc_buffer_space.len(),
324                      flush_write_buffer,
325                      user_data);
326
327     let mut fake_storage = FakeStorage::new();
328
329     let mut logger = Logger::new(&mut fake_storage);
330     logger.init();
331
332     let result = logger.get_recording(1, &mut yenc_buffer);
333     assert_eq!(LoggerError::NoSuchRecording, result.unwrap_err());
334
335     let tap0 = gps::TimeAndPos {
336         system_time: 0,
337         unix_time: 1478026311,
338         latitude_deg: 0x73234e,
339         longitude_deg: 0x73234f,
340         latitude_rad: Fixed::from_f32(12.57613).to_radians(),
341         longitude_rad: Fixed::from_f32(12.576131666666667).to_radians(),
342     };
343
344     logger.start_recording(&tap0);
345
346     let tap1 = gps::TimeAndPos {
347         system_time: 0,
348         unix_time: 1478026311 + 1,
349         latitude_deg: 0x73234e + 5,
350         longitude_deg: 0x73234f + 5,
351         latitude_rad: Fixed::from_f32(12.576138333333333).to_radians(),
352         longitude_rad: Fixed::from_f32(12.57614).to_radians(),
353     };
354
355     logger.log(&tap0, &tap1);
356
357     let tap2 = gps::TimeAndPos {
358         system_time: 0,
359         unix_time: 1478026311 + 2,
360         latitude_deg: 0x73234e + 10,
361         longitude_deg: 0x73234f + 10,
362         latitude_rad: Fixed::from_f32(12.576146666666666).to_radians(),
363         longitude_rad: Fixed::from_f32(12.576148333333334).to_radians(),
364     };
365
366     logger.log(&tap1, &tap2);
367
368     logger.stop_recording(&tap2);
369
370     assert!(logger.get_recording(1, &mut yenc_buffer).is_ok());
371 }
372
373 // Verifies that Logger::list_recordings() shows an empty listing
374 // if there are no recordings.
375 #[test]
376 fn list_recordings0() {
377     let pipe = Pipe::new();
378
379     let mut file = unsafe { std::fs::File::from_raw_fd(pipe.write_fd()) };
380
381     let mut ls_buffer_space = [0u8; 4096];
382     let mut ls_buffer = Buffer::alloc();
383
384     let user_data = (&mut file as *mut std::fs::File) as *mut BufferUserData;
385
386     ls_buffer.init(ls_buffer_space.as_mut_ptr(),
387                    ls_buffer_space.len(),
388                    flush_write_buffer,
389                    user_data);
390
391     let mut fake_storage = FakeStorage::new();
392
393     let mut logger = Logger::new(&mut fake_storage);
394     logger.init();
395
396     logger.list_recordings(&mut ls_buffer);
397
398     let mut file = unsafe { std::fs::File::from_raw_fd(pipe.read_fd()) };
399
400     let mut listing_raw = [0u8; 256];
401
402     // There are no recordings, so there's no data in the pipe either.
403     assert!(file.read(&mut listing_raw).is_err());
404 }
405
406 // Verifies that Logger::list_recordings() shows a listing
407 // of a single recording in the very first sector.
408 #[test]
409 fn list_recordings1() {
410     let pipe = Pipe::new();
411
412     let mut file = unsafe { std::fs::File::from_raw_fd(pipe.write_fd()) };
413
414     let mut ls_buffer_space = [0u8; 4096];
415     let mut ls_buffer = Buffer::alloc();
416
417     let user_data = (&mut file as *mut std::fs::File) as *mut BufferUserData;
418
419     ls_buffer.init(ls_buffer_space.as_mut_ptr(),
420                    ls_buffer_space.len(),
421                    flush_write_buffer,
422                    user_data);
423
424     let mut fake_storage = FakeStorage::new();
425
426     // Mark first sector as in use.
427     let recording0 = [
428         // Header:
429         0x01, 0x00, 0x01, 0x00,
430         0xa2, 0xdb, 0x14, 0x5e,
431     ];
432
433     fake_storage.expected[0..recording0.len()].copy_from_slice(&recording0);
434     fake_storage.actual[0..recording0.len()].copy_from_slice(&recording0);
435
436     let mut logger = Logger::new(&mut fake_storage);
437     logger.init();
438
439     logger.list_recordings(&mut ls_buffer);
440
441     let mut file = unsafe { std::fs::File::from_raw_fd(pipe.read_fd()) };
442
443     let mut listing_raw = [0u8; 256];
444     let num_bytes_read = file.read(&mut listing_raw).unwrap();
445
446     let expected_bytes = b"\
447 2020-01-07 19:27:30       4K       1\n";
448
449     assert_eq!(String::from_utf8(expected_bytes.to_vec()).unwrap(),
450                String::from_utf8(listing_raw[0..num_bytes_read].to_vec()).unwrap());
451 }
452
453 // Verifies that Logger::list_recordings() handles sectors holding
454 // additional recording data.
455 #[test]
456 fn list_recording1_multi_sector() {
457     let pipe = Pipe::new();
458
459     let mut file = unsafe { std::fs::File::from_raw_fd(pipe.write_fd()) };
460
461     let mut ls_buffer_space = [0u8; 4096];
462     let mut ls_buffer = Buffer::alloc();
463
464     let user_data = (&mut file as *mut std::fs::File) as *mut BufferUserData;
465
466     ls_buffer.init(ls_buffer_space.as_mut_ptr(),
467                    ls_buffer_space.len(),
468                    flush_write_buffer,
469                    user_data);
470
471     let mut fake_storage = FakeStorage::new();
472
473     // The first sector starts recording 1.
474     let header0 = [
475         // Header:
476         0x01, 0x00, 0x01, 0x00,
477         0x75, 0x18, 0x17, 0x5e,
478     ];
479
480     // The second sector has additional data for recording 1.
481     let header1 = [
482         0x03, 0x00, 0x01, 0x00,
483     ];
484
485     fake_storage.actual[0..header0.len()].copy_from_slice(&header0);
486     fake_storage.actual[4096..4096 + header1.len()].copy_from_slice(&header1);
487
488     let mut logger = Logger::new(&mut fake_storage);
489     logger.init();
490
491     logger.list_recordings(&mut ls_buffer);
492
493     let mut file = unsafe { std::fs::File::from_raw_fd(pipe.read_fd()) };
494
495     let mut listing_raw = [0u8; 4096];
496     let num_bytes_read = file.read(&mut listing_raw).unwrap();
497
498     let expected_bytes = b"\
499 2020-01-09 12:11:33       8K       1\n";
500
501     assert_eq!(String::from_utf8(expected_bytes.to_vec()).unwrap(),
502                String::from_utf8(listing_raw[0..num_bytes_read].to_vec()).unwrap());
503 }