614108bf5eb2cc5bd5c70a8e72a017cfe1a51145
[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 common::buffer::{Buffer, BufferUserData};
25 use common::gps;
26 use common::storage::{Storage, Error};
27 use common::logger::{MEMORY_SIZE, Logger, Error as LoggerError};
28
29 struct FakeStorage {
30     expected: Box<[u8]>,
31     actual: Box<[u8]>,
32 }
33
34 impl FakeStorage {
35     fn new() -> FakeStorage {
36         FakeStorage {
37             expected: vec![0xff; MEMORY_SIZE].into_boxed_slice(),
38             actual: vec![0xff; MEMORY_SIZE].into_boxed_slice(),
39         }
40     }
41 }
42
43 impl Storage for FakeStorage {
44     fn size(&self) -> usize {
45         MEMORY_SIZE
46     }
47
48     fn read(&self, address: usize, buffer: &mut [u8]) {
49         for i in 0..buffer.len() {
50             buffer[i] = self.actual[address + i];
51         }
52     }
53
54     fn write(&mut self, address: usize, buffer: &[u8; 4096]) -> Result<(), Error> {
55         if (address & 4095) != 0 {
56             return Err(Error::UnalignedAddress);
57         }
58
59         for i in 0..buffer.len() {
60             self.actual[address + i] = buffer[i];
61         }
62
63         Ok(())
64     }
65
66     fn clear(&mut self) {
67         self.actual = vec![0xff; MEMORY_SIZE].into_boxed_slice();
68     }
69 }
70
71 struct Pipe {
72     pipe_fd: [i32; 2],
73 }
74
75 impl Pipe {
76     fn new() -> Pipe {
77         let mut pipe_fd = [-1i32; 2];
78
79         unsafe {
80             extern {
81                 fn pipe(pipefd: *mut i32) -> i32;
82             }
83
84             pipe(pipe_fd.as_mut_ptr());
85         }
86
87         Pipe {
88             pipe_fd: pipe_fd,
89         }
90     }
91
92     fn read_fd(&self) -> i32 {
93         self.pipe_fd[0]
94     }
95
96     fn write_fd(&self) -> i32 {
97         self.pipe_fd[1]
98     }
99 }
100
101 // Runs a couple of recordings on fully erased flash memory.
102 #[test]
103 fn first_recording() {
104     let mut fake_storage = FakeStorage::new();
105
106     let mut logger = Logger::new(&mut fake_storage);
107     logger.init();
108
109     let tap = gps::TimeAndPos {
110         system_time: 0,
111         unix_time: 1478026311,
112         latitude: 0x73234e,
113         longitude: 0x73234f,
114     };
115
116     let recording_id = logger.start_recording(&tap);
117     assert_eq!(1, recording_id);
118
119     let sectors_written = logger.stop_recording(&tap);
120     assert_eq!(1, sectors_written);
121
122     let expected = [
123         // Header:
124         0x01, 0x00, 0x01, 0x00,
125         0x47, 0xe4, 0x18, 0x58,
126
127         // First point:
128         0x03,
129         0x00,
130         0x9c, 0x8d, 0x99, 0x07,
131         0x9e, 0x8d, 0x99, 0x07,
132
133         // Sentinel:
134         0xff, 0xff, 0xff, 0xff, 0x0f,
135
136         // Footer:
137         0x00, 0x00
138     ];
139
140     let start = 0;
141     let end = start + expected.len();
142     fake_storage.expected[start..end].copy_from_slice(&expected);
143
144     assert_eq!(fake_storage.expected, fake_storage.actual);
145 }
146
147 #[test]
148 fn second_recording() {
149     let mut fake_storage = FakeStorage::new();
150
151     // Mark first sector as in use.
152     let recording0 = [
153         // Header:
154         0x01, 0x00, 0x01, 0x00,
155     ];
156
157     fake_storage.expected[0..recording0.len()].copy_from_slice(&recording0);
158     fake_storage.actual[0..recording0.len()].copy_from_slice(&recording0);
159
160     let mut logger = Logger::new(&mut fake_storage);
161     logger.init();
162
163     let tap = gps::TimeAndPos {
164         system_time: 0,
165         unix_time: 1478026312,
166         latitude: 0x73234e,
167         longitude: 0x73234f,
168     };
169
170     let recording_id = logger.start_recording(&tap);
171     assert_eq!(2, recording_id);
172
173     let sectors_written = logger.stop_recording(&tap);
174     assert_eq!(1, sectors_written);
175
176     let expected = [
177         // Header:
178         0x01, 0x00, 0x02, 0x00,
179         0x48, 0xe4, 0x18, 0x58,
180
181         // First point:
182         0x03,
183         0x00,
184         0x9c, 0x8d, 0x99, 0x07,
185         0x9e, 0x8d, 0x99, 0x07,
186
187         // Sentinel:
188         0xff, 0xff, 0xff, 0xff, 0x0f,
189
190         // Footer:
191         0x00, 0x00
192     ];
193
194     let start = 4096;
195     let end = start + expected.len();
196     fake_storage.expected[start..end].copy_from_slice(&expected);
197
198     assert_eq!(fake_storage.expected, fake_storage.actual);
199 }
200
201 #[test]
202 fn multi_sector_recording() {
203     let mut fake_storage = FakeStorage::new();
204
205     let mut logger = Logger::new(&mut fake_storage);
206     logger.init();
207
208     let tap = gps::TimeAndPos {
209         system_time: 0,
210         unix_time: 1578425250,
211         latitude: 0x73234e,
212         longitude: 0x73234f,
213     };
214
215     let recording_id = logger.start_recording(&tap);
216     assert_eq!(1, recording_id);
217
218     let mut prev_tap = tap;
219
220     for _ in 0..2048 {
221         let tap = gps::TimeAndPos {
222             system_time: 0,
223             unix_time: prev_tap.unix_time + 1,
224             latitude: prev_tap.latitude + 1,
225             longitude: prev_tap.longitude + 1,
226         };
227
228         logger.log(&prev_tap, &tap);
229
230         prev_tap = tap;
231     }
232
233     let sectors_written = logger.stop_recording(&tap);
234     assert_eq!(2, sectors_written);
235
236     let header0 = [
237         0x01, 0x00, 0x01, 0x00,
238     ];
239
240     let header1 = [
241         0x03, 0x00, 0x01, 0x00,
242     ];
243
244     assert_eq!(header0, fake_storage.actual[0..(0 + header0.len())]);
245     assert_eq!(header1, fake_storage.actual[4096..(4096 + header1.len())]);
246 }
247
248 extern "C" fn flush_write_buffer(user_data: *mut BufferUserData,
249                                  buf: *const u8,
250                                  _bufsiz: usize,
251                                  count: usize) -> isize {
252     let _file = user_data as *mut std::fs::File;
253
254     unsafe {
255         let file = &mut * _file;
256
257         file.write_all(std::slice::from_raw_parts(buf, count)).unwrap();
258     }
259
260     count as isize
261 }
262
263 // Verifies that Logger::get_recording() detects unknown recording IDs.
264 #[test]
265 fn get_recording_invalid() {
266     let pipe = Pipe::new();
267
268     let mut file = unsafe { std::fs::File::from_raw_fd(pipe.write_fd()) };
269
270     let mut yenc_buffer_space = [0u8; 8192];
271     let mut yenc_buffer = Buffer::alloc();
272
273     let user_data = (&mut file as *mut std::fs::File) as *mut BufferUserData;
274
275     yenc_buffer.init(yenc_buffer_space.as_mut_ptr(),
276                      yenc_buffer_space.len(),
277                      flush_write_buffer,
278                      user_data);
279
280     let mut fake_storage = FakeStorage::new();
281
282     let mut logger = Logger::new(&mut fake_storage);
283     logger.init();
284
285     // Zero is never a valid recording id.
286     let result = logger.get_recording(0, &mut yenc_buffer);
287     assert_eq!(LoggerError::NoSuchRecording, result.unwrap_err());
288
289     let result = logger.get_recording(1, &mut yenc_buffer);
290     assert_eq!(LoggerError::NoSuchRecording, result.unwrap_err());
291 }
292
293 // Verifies that Logger::get_recording() can retrieve finished recordings.
294 #[test]
295 fn get_recording_valid() {
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     let result = logger.get_recording(1, &mut yenc_buffer);
316     assert_eq!(LoggerError::NoSuchRecording, result.unwrap_err());
317
318     let tap0 = gps::TimeAndPos {
319         system_time: 0,
320         unix_time: 1478026311,
321         latitude: 0x73234e,
322         longitude: 0x73234f,
323     };
324
325     logger.start_recording(&tap0);
326
327     let tap1 = gps::TimeAndPos {
328         system_time: 0,
329         unix_time: 1478026311 + 1,
330         latitude: 0x73234e + 5,
331         longitude: 0x73234f + 5,
332     };
333
334     logger.log(&tap0, &tap1);
335
336     let tap2 = gps::TimeAndPos {
337         system_time: 0,
338         unix_time: 1478026311 + 2,
339         latitude: 0x73234e + 10,
340         longitude: 0x73234f + 10,
341     };
342
343     logger.log(&tap1, &tap2);
344
345     logger.stop_recording(&tap2);
346
347     assert!(logger.get_recording(1, &mut yenc_buffer).is_ok());
348 }