Skip to content

Commit 3289a95

Browse files
lauraltsandreim
authored andcommitted
Increase coverage for snapshot crates
Added some unit tests for the snapshotting functionality. Added a few more comments in tests and fixed some typos. Signed-off-by: Laura Loghin <[email protected]>
1 parent ee49525 commit 3289a95

File tree

4 files changed

+154
-35
lines changed

4 files changed

+154
-35
lines changed

src/snapshot/src/lib.rs

+104-22
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
//!
88
//! The `Snapshot` API manages serialization and deserialization of collections of objects
99
//! that implement the `Versionize` trait. Each object is stored in a separate section
10-
//! that can be save/loaded independently:
10+
//! that can be saved/loaded independently:
1111
//!
1212
//! |----------------------------|
1313
//! | 64 bit magic_id |
@@ -25,10 +25,10 @@
2525
//! | optional CRC64 |
2626
//! |----------------------------|
2727
//!
28-
//! Each structure, union or enum is versioned separately and only need to increment their version
28+
//! Each structure, union or enum is versioned separately and only needs to increment their version
2929
//! if a field is added or removed. For each state snapshot we define 2 versions:
3030
//! - **the format version** which refers to the SnapshotHdr, Section headers, CRC, or the
31-
//! representation of primitives types (currentl we use serde bincode as a backend). The current
31+
//! representation of primitives types (currently we use serde bincode as a backend). The current
3232
//! implementation does not have any logic dependent on it.
3333
//! - **the data version** which refers to the state stored in all of the snapshot sections.
3434
//!
@@ -340,6 +340,80 @@ mod tests {
340340
}
341341
}
342342

343+
#[test]
344+
fn test_get_format_version_x86() {
345+
// Check if `get_format_version()` returns indeed the format
346+
// version (the least significant 2 bytes) if the id is valid
347+
// (the other bytes == BASE_MAGIC_ID).
348+
#[cfg(target_arch = "x86_64")]
349+
let good_magic_id = 0x0710_1984_8664_0001u64;
350+
#[cfg(target_arch = "aarch64")]
351+
let good_magic_id = 0x0710_1984_AAAA_0001u64;
352+
353+
assert_eq!(get_format_version(good_magic_id).unwrap(), 1u16);
354+
355+
// Flip a bit to invalidate the arch id.
356+
let invalid_magic_id = good_magic_id | (1u64 << 63);
357+
assert_eq!(
358+
get_format_version(invalid_magic_id).unwrap_err(),
359+
Error::InvalidMagic(invalid_magic_id)
360+
);
361+
}
362+
363+
#[test]
364+
fn test_section_ops() {
365+
let vm = VersionMap::new();
366+
let state = Test {
367+
field0: 0,
368+
field1: 1,
369+
field2: 2,
370+
field3: "test".to_owned(),
371+
field4: vec![4, 3, 2, 1],
372+
field_x: 0,
373+
};
374+
375+
let mut snapshot_mem = vec![0u8; 1024];
376+
377+
// Serialize as v1.
378+
let mut snapshot = Snapshot::new(vm.clone(), 1);
379+
snapshot.write_section("test", &state).unwrap();
380+
snapshot.save(&mut snapshot_mem.as_mut_slice()).unwrap();
381+
382+
let mut sections = &mut snapshot.sections;
383+
assert_eq!(sections.len(), 1);
384+
385+
let section = sections.get_mut("test").unwrap();
386+
assert_eq!(section.name, "test");
387+
// Data should contain field_x (8 bytes), field0 (8 bytes) and field1 (4 bytes) in this order.
388+
// field_x == 1 because of the semantic serializer fn for field3, the semantic serializer fn
389+
// for field4 will set field0 to field4.iter().sum() = 10 and field1 == 1 (the original value).
390+
assert_eq!(
391+
section.data,
392+
[1, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0]
393+
);
394+
395+
let state_1 = Test1 {
396+
field_x: 0,
397+
field0: 0,
398+
field1: 1,
399+
};
400+
401+
snapshot.write_section("test1", &state_1).unwrap();
402+
snapshot.save(&mut snapshot_mem.as_mut_slice()).unwrap();
403+
sections = &mut snapshot.sections;
404+
assert_eq!(sections.len(), 2);
405+
406+
// Trying to read a section with an invalid name should fail.
407+
assert_eq!(
408+
snapshot.read_section::<Test>("test2").unwrap_err(),
409+
Error::SectionNotFound
410+
);
411+
412+
// Validate that the 2 inserted objects can be deserialized.
413+
assert!(snapshot.read_section::<Test>("test").is_ok());
414+
assert!(snapshot.read_section::<Test1>("test1").is_ok());
415+
}
416+
343417
#[test]
344418
fn test_struct_semantic_fn() {
345419
let mut vm = VersionMap::new();
@@ -370,8 +444,14 @@ mod tests {
370444

371445
// The semantic serializer fn for field4 will set field0 to field4.iter().sum() == 10.
372446
assert_eq!(restored_state.field0, state.field4.iter().sum::<u64>());
447+
// The semantic deserializer for field4 will change field's value to vec![field0; 4].
373448
assert_eq!(restored_state.field4, vec![restored_state.field0; 4]);
449+
// The semantic serializer and deserializer for field3 will both increment field_x value.
374450
assert_eq!(restored_state.field_x, 2);
451+
// field1 should have the original value.
452+
assert_eq!(restored_state.field1, 1);
453+
// field2 should have the default value as this field was added at version 2.
454+
assert_eq!(restored_state.field2, 20);
375455

376456
// Serialize as v3.
377457
let mut snapshot = Snapshot::new(vm.clone(), 3);
@@ -381,10 +461,13 @@ mod tests {
381461
snapshot = Snapshot::load(&mut snapshot_mem.as_slice(), vm.clone()).unwrap();
382462
restored_state = snapshot.read_section::<Test>("test").unwrap();
383463

384-
// The semantic fn for field4 will set field0 to field4.iter().sum() == 10.
464+
// We expect only the semantic serializer and deserializer for field4 to be called at version 3.
465+
// The semantic serializer will set field0 to field4.iter().sum() == 10.
385466
assert_eq!(restored_state.field0, state.field4.iter().sum::<u64>());
386-
// The semantic deserializer fn will create 4 element vec with all values == field0
467+
// The semantic deserializer will create a 4 elements vec with all values == field0.
387468
assert_eq!(restored_state.field4, vec![restored_state.field0; 4]);
469+
// The semantic fn for field3 must not be called at version 3.
470+
assert_eq!(restored_state.field_x, 0);
388471

389472
// Serialize as v4.
390473
let mut snapshot = Snapshot::new(vm.clone(), 4);
@@ -394,7 +477,7 @@ mod tests {
394477
snapshot = Snapshot::load(&mut snapshot_mem.as_slice(), vm.clone()).unwrap();
395478
restored_state = snapshot.read_section::<Test>("test").unwrap();
396479

397-
// The semantic fn must not be called.
480+
// The 4 semantic fns must not be called at version 4.
398481
assert_eq!(restored_state.field0, 0);
399482
assert_eq!(restored_state.field4, vec![4, 3, 2, 1]);
400483
}
@@ -419,7 +502,7 @@ mod tests {
419502
};
420503

421504
let mut snapshot = Snapshot::new(vm.clone(), 4);
422-
// The section will succesfully be serialized.
505+
// The section will successfully be serialized.
423506
assert!(snapshot.write_section("test", &state).is_ok());
424507

425508
snapshot = Snapshot::new(vm.clone(), 1);
@@ -452,7 +535,7 @@ mod tests {
452535
let mut snapshot_mem = vec![0u8; 1024];
453536

454537
let mut snapshot = Snapshot::new(vm.clone(), 2);
455-
// The section will succesfully be serialized.
538+
// The section will successfully be serialized.
456539
assert!(snapshot.write_section("test", &state).is_ok());
457540
assert_eq!(snapshot.save(&mut snapshot_mem.as_mut_slice()), Ok(()));
458541

@@ -478,8 +561,9 @@ mod tests {
478561

479562
// Serialize as v1.
480563
let mut snapshot = Snapshot::new(vm.clone(), 1);
481-
// The section will succesfully be serialized.
564+
// The section will successfully be serialized.
482565
assert!(snapshot.write_section("test", &state_1).is_ok());
566+
// Saving the snapshot will fail due to the small size of `snapshot_mem` vec.
483567
assert_eq!(
484568
snapshot.save(&mut snapshot_mem.as_mut_slice()).unwrap_err(),
485569
Error::Serialize(
@@ -502,7 +586,7 @@ mod tests {
502586

503587
// Serialize as v1.
504588
let mut snapshot = Snapshot::new(vm.clone(), 1);
505-
// The section will succesfully be serialized.
589+
// The section will successfully be serialized.
506590
snapshot.write_section("test", &state_1).unwrap();
507591
snapshot
508592
.save_with_crc64(&mut snapshot_mem.as_mut_slice())
@@ -523,7 +607,7 @@ mod tests {
523607

524608
// Serialize as v1.
525609
let mut snapshot = Snapshot::new(vm.clone(), 1);
526-
// The section will succesfully be serialized.
610+
// The section will successfully be serialized.
527611
snapshot.write_section("test", &state_1).unwrap();
528612
snapshot
529613
.save_with_crc64(&mut snapshot_mem.as_mut_slice())
@@ -562,10 +646,12 @@ mod tests {
562646
let mut snapshot_mem = vec![0u8; 1024];
563647

564648
let mut snapshot = Snapshot::new(vm.clone(), 4);
565-
// The section will succesfully be serialized.
649+
// The section will successfully be serialized.
566650
assert!(snapshot.write_section("test", &state).is_ok());
567651
assert_eq!(snapshot.save(&mut snapshot_mem.as_mut_slice()), Ok(()));
568652

653+
// Load operation should fail if we don't use the whole `snapshot_mem` resulted from
654+
// serialization.
569655
snapshot_mem.truncate(10);
570656
let snapshot_load_error =
571657
Snapshot::load(&mut snapshot_mem.as_slice(), vm.clone()).unwrap_err();
@@ -714,7 +800,6 @@ mod tests {
714800

715801
snapshot = Snapshot::load(&mut snapshot_mem.as_slice(), vm.clone()).unwrap();
716802
let restored_state = snapshot.read_section::<kvm_pit_config>("test").unwrap();
717-
println!("State: {:?}", restored_state);
718803
// Check if we serialized x correctly, that is if semantic_x() was called.
719804
assert_eq!(restored_state, state);
720805
}
@@ -723,7 +808,7 @@ mod tests {
723808
fn test_basic_add_remove_field() {
724809
#[rustfmt::skip]
725810
let basic_add_remove_field_v1: &[u8] = &[
726-
0x01, 0x00,
811+
0x01, 0x00,
727812
#[cfg(target_arch = "aarch64")]
728813
0xAA,
729814
#[cfg(target_arch = "aarch64")]
@@ -742,7 +827,7 @@ mod tests {
742827
let basic_add_remove_field_v2: &[u8] = &[
743828
0x01, 0x00,
744829
#[cfg(target_arch = "aarch64")]
745-
0xAA,
830+
0xAA,
746831
#[cfg(target_arch = "aarch64")]
747832
0xAA,
748833
#[cfg(target_arch = "x86_64")]
@@ -758,9 +843,9 @@ mod tests {
758843

759844
#[rustfmt::skip]
760845
let basic_add_remove_field_v3: &[u8] = &[
761-
0x01, 0x00,
846+
0x01, 0x00,
762847
#[cfg(target_arch = "aarch64")]
763-
0xAA,
848+
0xAA,
764849
#[cfg(target_arch = "aarch64")]
765850
0xAA,
766851
#[cfg(target_arch = "x86_64")]
@@ -823,27 +908,24 @@ mod tests {
823908

824909
let mut snapshot = Snapshot::load(&mut snapshot_blob, vm.clone()).unwrap();
825910
let mut restored_state = snapshot.read_section::<B>("test").unwrap();
826-
println!("State: {:?}", restored_state);
827911
// Check if we serialized x correctly, that is if semantic_x() was called.
828912
assert_eq!(restored_state.a.x, 1234);
829913
assert_eq!(restored_state.a.z, stringify!(whatever));
830914

831915
snapshot_blob = basic_add_remove_field_v2;
832916
snapshot = Snapshot::load(&mut snapshot_blob, vm.clone()).unwrap();
833917
restored_state = snapshot.read_section::<B>("test").unwrap();
834-
println!("State: {:?}", restored_state);
835918
// Check if x was not serialized, it should be 0.
836919
assert_eq!(restored_state.a.x, 0);
837-
// z field was added in version to, make sure it contains the original value
920+
// z field was added in version 2, make sure it contains the original value.
838921
assert_eq!(restored_state.a.z, stringify!(basic));
839922

840923
snapshot_blob = basic_add_remove_field_v3;
841924
snapshot = Snapshot::load(&mut snapshot_blob, vm.clone()).unwrap();
842925
restored_state = snapshot.read_section::<B>("test").unwrap();
843-
println!("State: {:?}", restored_state);
844926
// Check if x was not serialized, it should be 0.
845927
assert_eq!(restored_state.a.x, 0);
846-
// z field was added in version to, make sure it contains the original value
928+
// z field was added in version 2, make sure it contains the original value.
847929
assert_eq!(restored_state.a.z, stringify!(basic));
848930
assert_eq!(restored_state.a.q, 1234);
849931
}

src/versionize/src/crc.rs

+19-8
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,23 @@ where
7878
mod tests {
7979
use super::{CRC64Reader, CRC64Writer, Read, Write};
8080

81+
#[test]
82+
fn test_crc_new() {
83+
let buf = vec![1; 5];
84+
let mut slice = buf.as_slice();
85+
let crc_reader = CRC64Reader::new(&mut slice);
86+
assert_eq!(crc_reader.crc64, 0);
87+
assert_eq!(crc_reader.reader, &[1; 5]);
88+
assert_eq!(crc_reader.checksum(), 0);
89+
90+
let mut buf = vec![0; 5];
91+
let mut slice = buf.as_mut_slice();
92+
let crc_writer = CRC64Writer::new(&mut slice);
93+
assert_eq!(crc_writer.crc64, 0);
94+
assert_eq!(crc_writer.writer, &[0; 5]);
95+
assert_eq!(crc_writer.checksum(), 0);
96+
}
97+
8198
#[test]
8299
fn test_crc_read() {
83100
let buf = vec![1, 2, 3, 4, 5];
@@ -87,14 +104,7 @@ mod tests {
87104
let mut crc_reader = CRC64Reader::new(&mut slice);
88105
crc_reader.read_to_end(&mut read_buf).unwrap();
89106
assert_eq!(crc_reader.checksum(), 0xFB04_60DE_0638_3654);
90-
}
91-
92-
#[test]
93-
fn test_crc_init() {
94-
let buf = vec![1, 2, 3, 4, 5];
95-
let mut slice = buf.as_slice();
96-
let crc_reader = CRC64Reader::new(&mut slice);
97-
assert_eq!(crc_reader.checksum(), 0);
107+
assert_eq!(crc_reader.checksum(), crc_reader.crc64);
98108
}
99109

100110
#[test]
@@ -107,5 +117,6 @@ mod tests {
107117
crc_writer.write_all(&write_buf.as_slice()).unwrap();
108118
crc_writer.flush().unwrap();
109119
assert_eq!(crc_writer.checksum(), 0x29D5_3572_1632_6566);
120+
assert_eq!(crc_writer.checksum(), crc_writer.crc64);
110121
}
111122
}

src/versionize/src/primitives.rs

+30-4
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ where
156156
0u8 => Ok(None),
157157
1u8 => Ok(Some(T::deserialize(reader, version_map, app_version)?)),
158158
value => Err(Error::Deserialize(format!(
159-
"Invalind option value {}",
159+
"Invalid option value {}",
160160
value
161161
))),
162162
}
@@ -488,17 +488,43 @@ mod tests {
488488
fn test_ser_de_option() {
489489
let vm = VersionMap::new();
490490
let mut snapshot_mem = vec![0u8; 64];
491-
492-
let store = Some("test".to_owned());
491+
let mut store = Some("test".to_owned());
493492

494493
store
495494
.serialize(&mut snapshot_mem.as_mut_slice(), &vm, 1)
496495
.unwrap();
497-
let restore =
496+
let mut restore =
498497
<Option<String> as Versionize>::deserialize(&mut snapshot_mem.as_slice(), &vm, 1)
499498
.unwrap();
499+
assert_eq!(store, restore);
500500

501+
// Check that ser_de also works for `None` variant.
502+
store = None;
503+
store
504+
.serialize(&mut snapshot_mem.as_mut_slice(), &vm, 1)
505+
.unwrap();
506+
restore = <Option<String> as Versionize>::deserialize(&mut snapshot_mem.as_slice(), &vm, 1)
507+
.unwrap();
501508
assert_eq!(store, restore);
509+
510+
store = Some("test".to_owned());
511+
store
512+
.serialize(&mut snapshot_mem.as_mut_slice(), &vm, 1)
513+
.unwrap();
514+
// Corrupt `snapshot_mem` by changing the most significant bit to a value different than 0 or 1.
515+
snapshot_mem[0] = 2;
516+
let restore_err =
517+
<Option<String> as Versionize>::deserialize(&mut snapshot_mem.as_slice(), &vm, 1)
518+
.unwrap_err();
519+
assert_eq!(
520+
restore_err,
521+
Error::Deserialize("Invalid option value 2".to_string())
522+
);
523+
// Corrupt `snapshot_mem` by changing the most significant bit from 1 (`Some(type)`) to 0 (`None`).
524+
snapshot_mem[0] = 0;
525+
restore = <Option<String> as Versionize>::deserialize(&mut snapshot_mem.as_slice(), &vm, 1)
526+
.unwrap();
527+
assert_ne!(store, restore);
502528
}
503529

504530
#[test]

tests/integration_tests/build/test_coverage.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
import host_tools.cargo_build as host # pylint: disable=import-error
2121

22-
COVERAGE_TARGET_PCT = 82.56
22+
COVERAGE_TARGET_PCT = 82.60
2323
COVERAGE_MAX_DELTA = 0.05
2424

2525
CARGO_KCOV_REL_PATH = os.path.join(host.CARGO_BUILD_REL_PATH, 'kcov')

0 commit comments

Comments
 (0)