use crate::fixture_index_path;use crate::index::file::read::loose_file_path;use filetime::FileTime;use git_index::verify::extensions::no_find;use git_index::write::Options;use git_index::{decode, entry, extension, write, State, Version}; /// Round-trips should eventually be possible for all files we have, as we write them back exactly as they were read.#[test]fn roundtrips() -> crate::Result { enum Kind { Generated(&'static str), Loose(&'static str), } use Kind::*; let input = [ (Loose("extended-flags"), all_ext_but_eoie()), (Loose("conflicting-file"), all_ext_but_eoie()), (Loose("very-long-path"), all_ext_but_eoie()), (Generated("v2"), Options::default()), (Generated("V2_empty"), Options::default()), (Generated("v2_more_files"), all_ext_but_eoie()), ]; for (fixture, options) in input { let (path, fixture) = match fixture { Generated(name) => (fixture_index_path(name), name), Loose(name) => (loose_file_path(name), name), }; let expected = git_index::File::at(&path, decode::Options::default())?; let expected_bytes = std::fs::read(&path)?; let mut out_bytes = Vec::new(); let actual_version = expected.write_to(&mut out_bytes, options)?; assert_eq!( actual_version, expected.version(), "{} didn't write the expected version", fixture ); let (actual, _) = State::from_bytes(&out_bytes, FileTime::now(), decode::Options::default())?; compare_states(&actual, actual_version, &expected, options, fixture); compare_raw_bytes(&out_bytes, &expected_bytes, fixture); } Ok(())} #[test]fn state_comparisons_with_various_extension_configurations() { fn options_with(extensions: write::Extensions) -> Options { Options { hash_kind: git_hash::Kind::Sha1, extensions, } } enum Kind { Generated(&'static str), Loose(&'static str), } use Kind::*; for fixture in [ Loose("extended-flags"), Loose("conflicting-file"), Loose("very-long-path"), Loose("FSMN"), Loose("REUC"), Loose("UNTR-with-oids"), Loose("UNTR"), Generated("V2_empty"), Generated("v2"), Generated("v2_more_files"), Generated("v2_split_index"), Generated("v4_more_files_IEOT"), ] { for options in [ options_with(write::Extensions::None), options_with(write::Extensions::All), options_with(write::Extensions::Given { tree_cache: true, end_of_index_entry: true, }), options_with(write::Extensions::Given { tree_cache: false, end_of_index_entry: true, }), ] { let (path, fixture) = match fixture { Generated(name) => (fixture_index_path(name), name), Loose(name) => (loose_file_path(name), name), }; let expected = git_index::File::at(&path, Default::default()).unwrap(); let mut out = Vec::<u8>::new(); let actual_version = expected.write_to(&mut out, options).unwrap(); let (actual, _) = State::from_bytes(&out, FileTime::now(), decode::Options::default()).unwrap(); compare_states(&actual, actual_version, &expected, options, fixture); } }} #[test]fn extended_flags_automatically_upgrade_the_version_to_avoid_data_loss() -> crate::Result { let mut expected = git_index::File::at(fixture_index_path("v2"), Default::default())?; assert_eq!(expected.version(), Version::V2); expected.entries_mut()[0].flags.insert(entry::Flags::EXTENDED); let mut buf = Vec::new(); let actual_version = expected.write_to(&mut buf, Default::default())?; assert_eq!(actual_version, Version::V3, "extended flags need V3"); Ok(())} fn compare_states(actual: &State, actual_version: Version, expected: &State, options: Options, fixture: &str) { actual.verify_entries().expect("valid"); actual.verify_extensions(false, no_find).expect("valid"); assert_eq!(actual.version(), actual_version, "version mismatch in {}", fixture); assert_eq!( actual.tree(), options .extensions .should_write(extension::tree::SIGNATURE) .and_then(|_| expected.tree()), "tree extension mismatch in {}", fixture ); assert_eq!( actual.entries().len(), expected.entries().len(), "entry count mismatch in {}", fixture ); assert_eq!(actual.entries(), expected.entries(), "entries mismatch in {}", fixture); assert_eq!( actual.path_backing(), expected.path_backing(), "path_backing mismatch in {}", fixture );} fn compare_raw_bytes(generated: &[u8], expected: &[u8], fixture: &str) { assert_eq!(generated.len(), expected.len(), "file length mismatch in {}", fixture); let print_range = 10; for (index, (a, b)) in generated.iter().zip(expected.iter()).enumerate() { if a != b { let range_left = index.saturating_sub(print_range); let range_right = (index + print_range).min(generated.len()); let generated = &generated[range_left..range_right]; let expected = &expected[range_left..range_right]; panic! {"\n\nRoundtrip failed for index in fixture {:?} at position {:?}\n\ \t Actual: ... {:?} ...\n\ \tExpected: ... {:?} ...\n\n\ ", &fixture, index, generated, expected} } }} fn all_ext_but_eoie() -> Options { Options { extensions: write::Extensions::Given { end_of_index_entry: false, tree_cache: true, }, ..Options::default() }}
use crate::fixture_index_path;
use crate::index::file::read::loose_file_path;
use filetime::FileTime;
use git_index::verify::extensions::no_find;
use git_index::write::Options;
use git_index::{decode, entry, extension, write, State, Version};
/// Round-trips should eventually be possible for all files we have, as we write them back exactly as they were read.
#[test]
fn roundtrips() -> crate::Result {
enum Kind {
Generated(&'static str),
Loose(&'static str),
}
use Kind::*;
let input = [
(Loose("extended-flags"), all_ext_but_eoie()),
(Loose("conflicting-file"), all_ext_but_eoie()),
(Loose("very-long-path"), all_ext_but_eoie()),
(Generated("v2"), Options::default()),
(Generated("V2_empty"), Options::default()),
(Generated("v2_more_files"), all_ext_but_eoie()),
];
for (fixture, options) in input {
let (path, fixture) = match fixture {
Generated(name) => (fixture_index_path(name), name),
Loose(name) => (loose_file_path(name), name),
};
let expected = git_index::File::at(&path, decode::Options::default())?;
let expected_bytes = std::fs::read(&path)?;
let mut out_bytes = Vec::new();
let actual_version = expected.write_to(&mut out_bytes, options)?;
assert_eq!(
actual_version,
expected.version(),
"{} didn't write the expected version",
fixture
);
let (actual, _) = State::from_bytes(&out_bytes, FileTime::now(), decode::Options::default())?;
compare_states(&actual, actual_version, &expected, options, fixture);
compare_raw_bytes(&out_bytes, &expected_bytes, fixture);
}
Ok(())
}
#[test]
fn state_comparisons_with_various_extension_configurations() {
fn options_with(extensions: write::Extensions) -> Options {
Options {
hash_kind: git_hash::Kind::Sha1,
extensions,
}
}
enum Kind {
Generated(&'static str),
Loose(&'static str),
}
use Kind::*;
for fixture in [
Loose("extended-flags"),
Loose("conflicting-file"),
Loose("very-long-path"),
Loose("FSMN"),
Loose("REUC"),
Loose("UNTR-with-oids"),
Loose("UNTR"),
Generated("V2_empty"),
Generated("v2"),
Generated("v2_more_files"),
Generated("v2_split_index"),
Generated("v4_more_files_IEOT"),
] {
for options in [
options_with(write::Extensions::None),
options_with(write::Extensions::All),
options_with(write::Extensions::Given {
tree_cache: true,
end_of_index_entry: true,
}),
options_with(write::Extensions::Given {
tree_cache: false,
end_of_index_entry: true,
}),
] {
let (path, fixture) = match fixture {
Generated(name) => (fixture_index_path(name), name),
Loose(name) => (loose_file_path(name), name),
};
let expected = git_index::File::at(&path, Default::default()).unwrap();
let mut out = Vec::<u8>::new();
let actual_version = expected.write_to(&mut out, options).unwrap();
let (actual, _) = State::from_bytes(&out, FileTime::now(), decode::Options::default()).unwrap();
compare_states(&actual, actual_version, &expected, options, fixture);
}
}
}
#[test]
fn extended_flags_automatically_upgrade_the_version_to_avoid_data_loss() -> crate::Result {
let mut expected = git_index::File::at(fixture_index_path("v2"), Default::default())?;
assert_eq!(expected.version(), Version::V2);
expected.entries_mut()[0].flags.insert(entry::Flags::EXTENDED);
let mut buf = Vec::new();
let actual_version = expected.write_to(&mut buf, Default::default())?;
assert_eq!(actual_version, Version::V3, "extended flags need V3");
Ok(())
}
fn compare_states(actual: &State, actual_version: Version, expected: &State, options: Options, fixture: &str) {
actual.verify_entries().expect("valid");
actual.verify_extensions(false, no_find).expect("valid");
assert_eq!(actual.version(), actual_version, "version mismatch in {}", fixture);
assert_eq!(
actual.tree(),
options
.extensions
.should_write(extension::tree::SIGNATURE)
.and_then(|_| expected.tree()),
"tree extension mismatch in {}",
fixture
);
assert_eq!(
actual.entries().len(),
expected.entries().len(),
"entry count mismatch in {}",
fixture
);
assert_eq!(actual.entries(), expected.entries(), "entries mismatch in {}", fixture);
assert_eq!(
actual.path_backing(),
expected.path_backing(),
"path_backing mismatch in {}",
fixture
);
}
fn compare_raw_bytes(generated: &[u8], expected: &[u8], fixture: &str) {
assert_eq!(generated.len(), expected.len(), "file length mismatch in {}", fixture);
let print_range = 10;
for (index, (a, b)) in generated.iter().zip(expected.iter()).enumerate() {
if a != b {
let range_left = index.saturating_sub(print_range);
let range_right = (index + print_range).min(generated.len());
let generated = &generated[range_left..range_right];
let expected = &expected[range_left..range_right];
panic! {"\n\nRoundtrip failed for index in fixture {:?} at position {:?}\n\
\t Actual: ... {:?} ...\n\
\tExpected: ... {:?} ...\n\n\
", &fixture, index, generated, expected}
}
}
}
fn all_ext_but_eoie() -> Options {
Options {
extensions: write::Extensions::Given {
end_of_index_entry: false,
tree_cache: true,
},
..Options::default()
}
}