test: Verify Logger::list_recordings()'s behavior with empty storage.
[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 pipe2(pipefd: *mut i32, flags: i32) -> i32;
84             }
85
86             const O_NONBLOCK : i32 = 0x800;
87
88             pipe2(pipe_fd.as_mut_ptr(), O_NONBLOCK);
89         }
90
91         Pipe {
92             pipe_fd: pipe_fd,
93         }
94     }
95
96     fn read_fd(&self) -> i32 {
97         self.pipe_fd[0]
98     }
99
100     fn write_fd(&self) -> i32 {
101         self.pipe_fd[1]
102     }
103 }
104
105 // Runs a couple of recordings on fully erased flash memory.
106 #[test]
107 fn first_recording() {
108     let mut fake_storage = FakeStorage::new();
109
110     let mut logger = Logger::new(&mut fake_storage);
111     logger.init();
112
113     let tap = gps::TimeAndPos {
114         system_time: 0,
115         unix_time: 1478026311,
116         latitude: 0x73234e,
117         longitude: 0x73234f,
118     };
119
120     let recording_id = logger.start_recording(&tap);
121     assert_eq!(1, recording_id);
122
123     let sectors_written = logger.stop_recording(&tap);
124     assert_eq!(1, sectors_written);
125
126     let expected = [
127         // Header:
128         0x01, 0x00, 0x01, 0x00,
129         0x47, 0xe4, 0x18, 0x58,
130
131         // First point:
132         0x03,
133         0x00,
134         0x9c, 0x8d, 0x99, 0x07,
135         0x9e, 0x8d, 0x99, 0x07,
136
137         // Sentinel:
138         0xff, 0xff, 0xff, 0xff, 0x0f,
139
140         // Footer:
141         0x00, 0x00
142     ];
143
144     let start = 0;
145     let end = start + expected.len();
146     fake_storage.expected[start..end].copy_from_slice(&expected);
147
148     assert_eq!(fake_storage.expected, fake_storage.actual);
149 }
150
151 #[test]
152 fn second_recording() {
153     let mut fake_storage = FakeStorage::new();
154
155     // Mark first sector as in use.
156     let recording0 = [
157         // Header:
158         0x01, 0x00, 0x01, 0x00,
159     ];
160
161     fake_storage.expected[0..recording0.len()].copy_from_slice(&recording0);
162     fake_storage.actual[0..recording0.len()].copy_from_slice(&recording0);
163
164     let mut logger = Logger::new(&mut fake_storage);
165     logger.init();
166
167     let tap = gps::TimeAndPos {
168         system_time: 0,
169         unix_time: 1478026312,
170         latitude: 0x73234e,
171         longitude: 0x73234f,
172     };
173
174     let recording_id = logger.start_recording(&tap);
175     assert_eq!(2, recording_id);
176
177     let sectors_written = logger.stop_recording(&tap);
178     assert_eq!(1, sectors_written);
179
180     let expected = [
181         // Header:
182         0x01, 0x00, 0x02, 0x00,
183         0x48, 0xe4, 0x18, 0x58,
184
185         // First point:
186         0x03,
187         0x00,
188         0x9c, 0x8d, 0x99, 0x07,
189         0x9e, 0x8d, 0x99, 0x07,
190
191         // Sentinel:
192         0xff, 0xff, 0xff, 0xff, 0x0f,
193
194         // Footer:
195         0x00, 0x00
196     ];
197
198     let start = 4096;
199     let end = start + expected.len();
200     fake_storage.expected[start..end].copy_from_slice(&expected);
201
202     assert_eq!(fake_storage.expected, fake_storage.actual);
203 }
204
205 #[test]
206 fn multi_sector_recording() {
207     let mut fake_storage = FakeStorage::new();
208
209     let mut logger = Logger::new(&mut fake_storage);
210     logger.init();
211
212     let tap = gps::TimeAndPos {
213         system_time: 0,
214         unix_time: 1578425250,
215         latitude: 0x73234e,
216         longitude: 0x73234f,
217     };
218
219     let recording_id = logger.start_recording(&tap);
220     assert_eq!(1, recording_id);
221
222     let mut prev_tap = tap;
223
224     for _ in 0..2048 {
225         let tap = gps::TimeAndPos {
226             system_time: 0,
227             unix_time: prev_tap.unix_time + 1,
228             latitude: prev_tap.latitude + 1,
229             longitude: prev_tap.longitude + 1,
230         };
231
232         logger.log(&prev_tap, &tap);
233
234         prev_tap = tap;
235     }
236
237     let sectors_written = logger.stop_recording(&tap);
238     assert_eq!(2, sectors_written);
239
240     let header0 = [
241         0x01, 0x00, 0x01, 0x00,
242     ];
243
244     let header1 = [
245         0x03, 0x00, 0x01, 0x00,
246     ];
247
248     assert_eq!(header0, fake_storage.actual[0..(0 + header0.len())]);
249     assert_eq!(header1, fake_storage.actual[4096..(4096 + header1.len())]);
250 }
251
252 extern "C" fn flush_write_buffer(user_data: *mut BufferUserData,
253                                  buf: *const u8,
254                                  _bufsiz: usize,
255                                  count: usize) -> isize {
256     let _file = user_data as *mut std::fs::File;
257
258     unsafe {
259         let file = &mut * _file;
260
261         file.write_all(std::slice::from_raw_parts(buf, count)).unwrap();
262     }
263
264     count as isize
265 }
266
267 // Verifies that Logger::get_recording() detects unknown recording IDs.
268 #[test]
269 fn get_recording_invalid() {
270     let pipe = Pipe::new();
271
272     let mut file = unsafe { std::fs::File::from_raw_fd(pipe.write_fd()) };
273
274     let mut yenc_buffer_space = [0u8; 8192];
275     let mut yenc_buffer = Buffer::alloc();
276
277     let user_data = (&mut file as *mut std::fs::File) as *mut BufferUserData;
278
279     yenc_buffer.init(yenc_buffer_space.as_mut_ptr(),
280                      yenc_buffer_space.len(),
281                      flush_write_buffer,
282                      user_data);
283
284     let mut fake_storage = FakeStorage::new();
285
286     let mut logger = Logger::new(&mut fake_storage);
287     logger.init();
288
289     // Zero is never a valid recording id.
290     let result = logger.get_recording(0, &mut yenc_buffer);
291     assert_eq!(LoggerError::NoSuchRecording, result.unwrap_err());
292
293     let result = logger.get_recording(1, &mut yenc_buffer);
294     assert_eq!(LoggerError::NoSuchRecording, result.unwrap_err());
295 }
296
297 // Verifies that Logger::get_recording() can retrieve finished recordings.
298 #[test]
299 fn get_recording_valid() {
300     let pipe = Pipe::new();
301
302     let mut file = unsafe { std::fs::File::from_raw_fd(pipe.write_fd()) };
303
304     let mut yenc_buffer_space = [0u8; 8192];
305     let mut yenc_buffer = Buffer::alloc();
306
307     let user_data = (&mut file as *mut std::fs::File) as *mut BufferUserData;
308
309     yenc_buffer.init(yenc_buffer_space.as_mut_ptr(),
310                      yenc_buffer_space.len(),
311                      flush_write_buffer,
312                      user_data);
313
314     let mut fake_storage = FakeStorage::new();
315
316     let mut logger = Logger::new(&mut fake_storage);
317     logger.init();
318
319     let result = logger.get_recording(1, &mut yenc_buffer);
320     assert_eq!(LoggerError::NoSuchRecording, result.unwrap_err());
321
322     let tap0 = gps::TimeAndPos {
323         system_time: 0,
324         unix_time: 1478026311,
325         latitude: 0x73234e,
326         longitude: 0x73234f,
327     };
328
329     logger.start_recording(&tap0);
330
331     let tap1 = gps::TimeAndPos {
332         system_time: 0,
333         unix_time: 1478026311 + 1,
334         latitude: 0x73234e + 5,
335         longitude: 0x73234f + 5,
336     };
337
338     logger.log(&tap0, &tap1);
339
340     let tap2 = gps::TimeAndPos {
341         system_time: 0,
342         unix_time: 1478026311 + 2,
343         latitude: 0x73234e + 10,
344         longitude: 0x73234f + 10,
345     };
346
347     logger.log(&tap1, &tap2);
348
349     logger.stop_recording(&tap2);
350
351     assert!(logger.get_recording(1, &mut yenc_buffer).is_ok());
352 }
353
354 // Verifies that Logger::list_recordings() shows an empty listing
355 // if there are no recordings.
356 #[test]
357 fn list_recordings0() {
358     let pipe = Pipe::new();
359
360     let mut file = unsafe { std::fs::File::from_raw_fd(pipe.write_fd()) };
361
362     let mut ls_buffer_space = [0u8; 4096];
363     let mut ls_buffer = Buffer::alloc();
364
365     let user_data = (&mut file as *mut std::fs::File) as *mut BufferUserData;
366
367     ls_buffer.init(ls_buffer_space.as_mut_ptr(),
368                    ls_buffer_space.len(),
369                    flush_write_buffer,
370                    user_data);
371
372     let mut fake_storage = FakeStorage::new();
373
374     let mut logger = Logger::new(&mut fake_storage);
375     logger.init();
376
377     logger.list_recordings(&mut ls_buffer);
378
379     let mut file = unsafe { std::fs::File::from_raw_fd(pipe.read_fd()) };
380
381     let mut listing_raw = [0u8; 256];
382
383     // There are no recordings, so there's no data in the pipe either.
384     assert!(file.read(&mut listing_raw).is_err());
385 }
386
387 // Verifies that Logger::list_recordings() shows a listing
388 // of a single recording in the very first sector.
389 #[test]
390 fn list_recordings1() {
391     let pipe = Pipe::new();
392
393     let mut file = unsafe { std::fs::File::from_raw_fd(pipe.write_fd()) };
394
395     let mut ls_buffer_space = [0u8; 4096];
396     let mut ls_buffer = Buffer::alloc();
397
398     let user_data = (&mut file as *mut std::fs::File) as *mut BufferUserData;
399
400     ls_buffer.init(ls_buffer_space.as_mut_ptr(),
401                    ls_buffer_space.len(),
402                    flush_write_buffer,
403                    user_data);
404
405     let mut fake_storage = FakeStorage::new();
406
407     // Mark first sector as in use.
408     let recording0 = [
409         // Header:
410         0x01, 0x00, 0x01, 0x00,
411         0xa2, 0xdb, 0x14, 0x5e,
412     ];
413
414     fake_storage.expected[0..recording0.len()].copy_from_slice(&recording0);
415     fake_storage.actual[0..recording0.len()].copy_from_slice(&recording0);
416
417     let mut logger = Logger::new(&mut fake_storage);
418     logger.init();
419
420     logger.list_recordings(&mut ls_buffer);
421
422     let mut file = unsafe { std::fs::File::from_raw_fd(pipe.read_fd()) };
423
424     let mut listing_raw = [0u8; 256];
425     let num_bytes_read = file.read(&mut listing_raw).unwrap();
426
427     let expected_bytes = b"\
428 2020-01-07 19:27:30              1\n";
429
430     assert_eq!(String::from_utf8(expected_bytes.to_vec()).unwrap(),
431                String::from_utf8(listing_raw[0..num_bytes_read].to_vec()).unwrap());
432 }