mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2026-10-27 02:27:18 +00:00
cleaning
This commit is contained in:
1 parent
840e943626
commit
b085a1d476
26 files changed
+1010
-196
No files matched your search
@@ -128,3 +128,159 @@ pub fn decode_waypoints(id: Uuid, bytes: &[u8]) -> Option<WaypointChunk> {
|
||||
wpt: decode::<Vec<Waypoint>>(bytes)?,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::Chunk;
|
||||
|
||||
use super::*;
|
||||
|
||||
fn trackpoints(n: usize) -> TrackpointChunk {
|
||||
TrackpointChunk::new(
|
||||
(0..n)
|
||||
.map(|i| Trackpoint {
|
||||
ele: i as f64,
|
||||
..Default::default()
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encode_decode_round_trip() {
|
||||
let value = vec![(1_u32, "a".to_string()), (2, "b".to_string())];
|
||||
let bytes = encode(&value);
|
||||
assert_eq!(bytes[0], VERSION);
|
||||
assert_eq!(decode::<Vec<(u32, String)>>(&bytes), Some(value));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_rejects_other_versions_and_garbage() {
|
||||
let mut bytes = encode(&42_u32);
|
||||
assert_eq!(decode::<u32>(&bytes), Some(42));
|
||||
bytes[0] = VERSION + 1;
|
||||
assert_eq!(decode::<u32>(&bytes), None);
|
||||
assert_eq!(decode::<u32>(&[]), None);
|
||||
// the right version, but not what was asked for
|
||||
assert_eq!(decode::<Vec<String>>(&[VERSION, 0xff, 0xff, 0xff]), None);
|
||||
assert_eq!(decode::<u32>(&[VERSION]), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chunks_keep_their_identity_and_items() {
|
||||
let chunk = trackpoints(3);
|
||||
let decoded = decode_trackpoints(chunk.id.0, &encode_trackpoints(&chunk)).unwrap();
|
||||
assert_eq!(decoded.id, chunk.id);
|
||||
let eles: Vec<f64> = decoded.trkpt.iter().map(|p| p.ele).collect();
|
||||
assert_eq!(eles, [0.0, 1.0, 2.0]);
|
||||
assert!(decode_trackpoints(chunk.id.0, &[VERSION, 9, 9]).is_none());
|
||||
|
||||
let chunk = WaypointChunk::new(vec![Waypoint {
|
||||
name: Some("summit".into()),
|
||||
..Default::default()
|
||||
}]);
|
||||
let decoded = decode_waypoints(chunk.id.0, &encode_waypoints(&chunk)).unwrap();
|
||||
assert_eq!(decoded.id, chunk.id);
|
||||
assert_eq!(decoded.wpt[0].name.as_deref(), Some("summit"));
|
||||
assert!(decode_waypoints(chunk.id.0, &[]).is_none());
|
||||
}
|
||||
|
||||
fn file() -> File {
|
||||
let mut file = File::default();
|
||||
file.info.name = "file".into();
|
||||
for name in ["a", "b"] {
|
||||
let mut track = Track::default();
|
||||
track.info.name = Some(name.into());
|
||||
let mut segment = TrackSegment::default();
|
||||
segment.push(trackpoints(2));
|
||||
segment.push(trackpoints(1));
|
||||
track.trkseg.push(segment);
|
||||
track.trkseg.push(TrackSegment::default());
|
||||
file.trk.push(track);
|
||||
}
|
||||
file.wpt.push(WaypointChunk::new(vec![Waypoint::default()]));
|
||||
file
|
||||
}
|
||||
|
||||
/// The chunks of a file, by identity.
|
||||
fn chunks_of(
|
||||
file: &File,
|
||||
) -> (
|
||||
HashMap<TrackpointChunkId, Rc<TrackpointChunk>>,
|
||||
HashMap<WaypointChunkId, Rc<WaypointChunk>>,
|
||||
) {
|
||||
let trackpoints = file
|
||||
.trk
|
||||
.iter()
|
||||
.flat_map(|track| &track.trkseg)
|
||||
.flat_map(|segment| segment.chunks().iter().map(|c| (c.id, c.clone())))
|
||||
.collect();
|
||||
let waypoints = file
|
||||
.wpt
|
||||
.chunks()
|
||||
.iter()
|
||||
.map(|c| (c.id, c.clone()))
|
||||
.collect();
|
||||
(trackpoints, waypoints)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_file_record_rebuilds_the_file_with_the_same_chunks() {
|
||||
let file = file();
|
||||
let (trackpoints, waypoints) = chunks_of(&file);
|
||||
let record: FileRecord = decode(&encode(&FileRecord::new(&file))).unwrap();
|
||||
let built = record
|
||||
.build(file.id, &|id| trackpoints.get(id).cloned(), &|id| {
|
||||
waypoints.get(id).cloned()
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(built.id, file.id);
|
||||
assert_eq!(built.info, file.info);
|
||||
assert_eq!(built.trk.len(), 2);
|
||||
for (built, original) in built.trk.iter().zip(&file.trk) {
|
||||
assert_eq!(built.id, original.id);
|
||||
assert_eq!(built.info, original.info);
|
||||
assert_eq!(built.trkseg.len(), 2);
|
||||
for (built, original) in built.trkseg.iter().zip(&original.trkseg) {
|
||||
assert_eq!(built.id, original.id);
|
||||
assert_eq!(built.len(), original.len());
|
||||
for (a, b) in built.chunks().iter().zip(original.chunks()) {
|
||||
// the chunks are the ones that were given, not copies
|
||||
assert!(Rc::ptr_eq(a, b));
|
||||
}
|
||||
}
|
||||
}
|
||||
assert_eq!(built.wpt.len(), 1);
|
||||
assert!(Rc::ptr_eq(&built.wpt.chunks()[0], &file.wpt.chunks()[0]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_file_record_of_a_missing_chunk_builds_nothing() {
|
||||
let file = file();
|
||||
let (trackpoints, waypoints) = chunks_of(&file);
|
||||
|
||||
let record = || FileRecord::new(&file);
|
||||
assert!(
|
||||
record()
|
||||
.build(file.id, &|_| None, &|id| waypoints.get(id).cloned())
|
||||
.is_none()
|
||||
);
|
||||
assert!(
|
||||
record()
|
||||
.build(file.id, &|id| trackpoints.get(id).cloned(), &|_| None)
|
||||
.is_none()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_file_record_without_data_builds_an_empty_file() {
|
||||
let file = File::default();
|
||||
let built = FileRecord::new(&file)
|
||||
.build(file.id, &|_| None, &|_| None)
|
||||
.unwrap();
|
||||
assert!(built.trk.is_empty() && built.wpt.is_empty());
|
||||
}
|
||||
}
|
||||
@@ -103,3 +103,110 @@ impl Storage for MemoryStorage {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::future::Future;
|
||||
use std::task::{Context, Poll, Waker};
|
||||
|
||||
use super::*;
|
||||
|
||||
fn block_on<F: Future>(future: F) -> F::Output {
|
||||
let mut future = std::pin::pin!(future);
|
||||
let mut context = Context::from_waker(Waker::noop());
|
||||
match future.as_mut().poll(&mut context) {
|
||||
Poll::Ready(value) => value,
|
||||
Poll::Pending => panic!("the memory storage does not wait"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_storage_has_nothing() {
|
||||
let storage = MemoryStorage::default();
|
||||
let data = block_on(storage.load()).unwrap();
|
||||
assert_eq!(data, StoredData::default());
|
||||
assert_eq!(storage.counts(), (0, 0, 0));
|
||||
assert!(block_on(storage.load_settings()).unwrap().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_commit_puts_and_deletes() {
|
||||
let storage = MemoryStorage::default();
|
||||
let (a, b) = (FileId::default(), FileId::default());
|
||||
let (t, w) = (Uuid::new_v4(), Uuid::new_v4());
|
||||
block_on(storage.commit(&Batch {
|
||||
put_files: vec![(a, vec![1]), (b, vec![2])],
|
||||
put_trackpoint_chunks: vec![(t, vec![3])],
|
||||
put_waypoint_chunks: vec![(w, vec![4])],
|
||||
order: Some(vec![5]),
|
||||
categories: Some(vec![6]),
|
||||
..Default::default()
|
||||
}))
|
||||
.unwrap();
|
||||
assert_eq!(storage.counts(), (2, 1, 1));
|
||||
|
||||
let mut data = block_on(storage.load()).unwrap();
|
||||
data.files.sort_by_key(|(_, bytes)| bytes.clone());
|
||||
assert_eq!(data.files, vec![(a, vec![1]), (b, vec![2])]);
|
||||
assert_eq!(data.trackpoint_chunks, vec![(t, vec![3])]);
|
||||
assert_eq!(data.waypoint_chunks, vec![(w, vec![4])]);
|
||||
assert_eq!(
|
||||
(data.order, data.categories),
|
||||
(Some(vec![5]), Some(vec![6]))
|
||||
);
|
||||
|
||||
// a put replaces, a delete removes, what is not mentioned stays
|
||||
block_on(storage.commit(&Batch {
|
||||
put_files: vec![(a, vec![10])],
|
||||
delete_files: vec![b],
|
||||
delete_trackpoint_chunks: vec![t],
|
||||
..Default::default()
|
||||
}))
|
||||
.unwrap();
|
||||
assert_eq!(storage.counts(), (1, 0, 1));
|
||||
let data = block_on(storage.load()).unwrap();
|
||||
assert_eq!(data.files, vec![(a, vec![10])]);
|
||||
assert_eq!(
|
||||
(data.order, data.categories),
|
||||
(Some(vec![5]), Some(vec![6]))
|
||||
);
|
||||
|
||||
block_on(storage.commit(&Batch {
|
||||
delete_waypoint_chunks: vec![w],
|
||||
order: Some(vec![]),
|
||||
..Default::default()
|
||||
}))
|
||||
.unwrap();
|
||||
assert_eq!(storage.counts(), (1, 0, 0));
|
||||
assert_eq!(block_on(storage.load()).unwrap().order, Some(vec![]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_settings() {
|
||||
let storage = MemoryStorage::default();
|
||||
block_on(storage.put_setting("a", "1")).unwrap();
|
||||
block_on(storage.put_setting("b", "2")).unwrap();
|
||||
block_on(storage.put_setting("a", "3")).unwrap();
|
||||
let mut settings = block_on(storage.load_settings()).unwrap();
|
||||
settings.sort();
|
||||
assert_eq!(
|
||||
settings,
|
||||
vec![("a".into(), "3".into()), ("b".into(), "2".into())]
|
||||
);
|
||||
|
||||
block_on(storage.delete_setting("a")).unwrap();
|
||||
// deleting what is not there is fine
|
||||
block_on(storage.delete_setting("missing")).unwrap();
|
||||
assert_eq!(
|
||||
block_on(storage.load_settings()).unwrap(),
|
||||
vec![("b".into(), "2".into())]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_settings_are_not_files() {
|
||||
let storage = MemoryStorage::default();
|
||||
block_on(storage.put_setting("a", "1")).unwrap();
|
||||
assert_eq!(block_on(storage.load()).unwrap(), StoredData::default());
|
||||
}
|
||||
}
|
||||
@@ -83,3 +83,47 @@ pub trait Storage {
|
||||
|
||||
async fn delete_setting(&self, key: &str) -> Result<(), StorageError>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_batch_is_empty_until_it_holds_something() {
|
||||
assert!(Batch::default().is_empty());
|
||||
let holds = [
|
||||
Batch {
|
||||
put_files: vec![(FileId::default(), vec![])],
|
||||
..Default::default()
|
||||
},
|
||||
Batch {
|
||||
delete_files: vec![FileId::default()],
|
||||
..Default::default()
|
||||
},
|
||||
Batch {
|
||||
delete_trackpoint_chunks: vec![Uuid::new_v4()],
|
||||
..Default::default()
|
||||
},
|
||||
Batch {
|
||||
delete_waypoint_chunks: vec![Uuid::new_v4()],
|
||||
..Default::default()
|
||||
},
|
||||
Batch {
|
||||
order: Some(vec![]),
|
||||
..Default::default()
|
||||
},
|
||||
Batch {
|
||||
categories: Some(vec![]),
|
||||
..Default::default()
|
||||
},
|
||||
];
|
||||
assert!(holds.iter().all(|batch| !batch.is_empty()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_storage_error_is_readable() {
|
||||
let error = StorageError("disk full".into());
|
||||
assert_eq!(error.to_string(), "storage error: disk full");
|
||||
let _: &dyn std::error::Error = &error;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user