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