1044 lines · 36.6 KB · Rust
RawDownload
use gix_object::{    Tree,    tree::{Entry, EntryKind},}; fn hash_kind() -> gix_hash::Kind {    crate::fixture_hash_kind()} fn null_id() -> gix_hash::ObjectId {    hash_kind().null()} #[test]fn from_empty_cursor() -> crate::Result {    let (storage, mut write, num_writes_and_clear) = new_inmemory_writes();    let odb = StorageOdb::new(storage.clone());    let mut edit = gix_object::tree::Editor::new(Tree::default(), &odb, hash_kind());     edit.upsert(Some("root-file"), EntryKind::Blob, any_blob())?.upsert(        ["nested", "from", "root"],        EntryKind::BlobExecutable,        any_blob(),    )?;    let cursor_path = ["some", "deeply", "nested", "path"];    let mut cursor = edit.cursor_at(cursor_path)?;    let actual = cursor        .upsert(Some("file"), EntryKind::Blob, any_blob())?        .upsert(Some("empty-dir-via-cursor"), EntryKind::Tree, empty_tree())?        .upsert(["with-subdir", "dir", "file"], EntryKind::Blob, any_blob())?        .upsert(["with-subdir2", "dir", "file"], EntryKind::Blob, any_blob())?        .remove(Some("file"))?        .remove(["with-subdir", "dir", "file"])?        .remove(Some("with-subdir2"))?        .remove(Some("with-subdir2"))?        .write(&mut write)?;    insta::assert_snapshot!(        crate::normalize_tree_snapshot(&display_tree(actual, &storage)),        "only one item is left in the tree, which also keeps it alive",        @r#"        Oid(1)        └── empty-dir-via-cursor (empty)    "#    );    assert_eq!(num_writes_and_clear(), 1, "root tree");    assert_eq!(        cursor.get(None::<&str>),        None,        "the 'root' can't be obtained, no entry exists for it, ever"    );    assert_eq!(        cursor.get(Some("empty-dir-via-cursor")),        Some(&Entry {            mode: EntryKind::Tree.into(),            filename: "empty-dir-via-cursor".into(),            oid: empty_tree(),        }),    );     let actual = edit.write(&mut write)?;    insta::assert_snapshot!(crate::normalize_tree_snapshot(&display_tree(actual, &storage)), @r#"        Oid(1)        ├── nested        │   └── from        │       └── root Oid(2).100755        ├── root-file Oid(2).100644        └── some            └── deeply                └── nested                    └── path                        └── empty-dir-via-cursor (empty)    "#);     let mut cursor = edit.cursor_at(cursor_path)?;    let actual = cursor.remove(Some("empty-dir-via-cursor"))?.write(&mut write)?;    assert_eq!(actual, empty_tree(), "it keeps the empty tree like the editor would");    assert_eq!(        edit.get(["some", "deeply", "nested", "path"]),        Some(&Entry {            mode: EntryKind::Tree.into(),            filename: "path".into(),            oid: null_id(),        }),        "the directory leading to the removed one is still present"    );    assert_eq!(        edit.get(["some", "deeply", "nested", "path", "empty-dir-via-cursor"]),        None,        "but the removed entry is indee removed"    );     let actual = edit.write(&mut write)?;    insta::assert_snapshot!(        crate::normalize_tree_snapshot(&display_tree(actual, &storage)),        "now the editor naturally prunes all empty trees thus far, removing the cursor root",        @r#"        Oid(1)        ├── nested        │   └── from        │       └── root Oid(2).100755        └── root-file Oid(2).100644    "#    );     let mut cursor = edit.cursor_at(cursor_path)?;    let actual = cursor        .upsert(Some("root-file"), EntryKind::BlobExecutable, any_blob())?        .upsert(["nested", "from"], EntryKind::BlobExecutable, any_blob())?        .write(&mut write)?;     insta::assert_snapshot!(        crate::normalize_tree_snapshot(&display_tree(actual, &storage)),        "it is able to write the sub-tree, even though names from the top-level tree are re-used",        @r#"        Oid(1)        ├── nested        │   └── from Oid(2).100755        └── root-file Oid(2).100755    "#    );     let actual = edit.write(&mut write)?;    insta::assert_snapshot!(        crate::normalize_tree_snapshot(&display_tree(actual, &storage)),        "it places the subtree exactly where it's expected",        @r#"        Oid(1)        ├── nested        │   └── from        │       └── root Oid(2).100755        ├── root-file Oid(2).100644        └── some            └── deeply                └── nested                    └── path                        ├── nested                        │   └── from Oid(2).100755                        └── root-file Oid(2).100755    "#    );    Ok(())}#[test]fn from_existing_cursor() -> crate::Result {    let (storage, mut write, num_writes_and_clear) = new_inmemory_writes();    let odb = StorageOdb::new_with_odb(storage.clone(), tree_odb()?);    let root_tree_id = crate::generated_tree_root_id()?;    let root_tree = find_tree(&odb, root_tree_id)?;    odb.access_count_and_clear();    let mut edit = gix_object::tree::Editor::new(root_tree.clone(), &odb, hash_kind());     let mut cursor = edit.to_cursor();    let actual = cursor        .remove(Some("bin"))?        .remove(Some("bin.d"))?        .remove(Some("file.to"))?        .remove(Some("file.toml"))?        .remove(Some("file.toml.bin"))?        .upsert(["some", "nested", "file"], EntryKind::Blob, any_blob())?        .write(&mut write)?;    assert_eq!(        num_writes_and_clear(),        1 + 2,        "just the altered root tree, and two of trees towards `some/tested/file`"    );    insta::assert_snapshot!(        crate::normalize_tree_snapshot(&(display_tree_with_odb(actual, &storage, &odb))),        "a cursor at '' is equivalent to 'as_cursor()', or the editor itself",        @r#"        Oid(1)        ├── file        │   └── a Oid(2).100644        ├── file0 Oid(2).100644        └── some            └── nested                └── file Oid(3).100644    "#    );    let mut cursor = edit.cursor_at(["some", "nested"])?;    let actual = cursor        .upsert(Some("hello-from-cursor"), EntryKind::Blob, any_blob())?        .remove(Some("file"))?        .write(&mut write)?;    insta::assert_snapshot!(crate::normalize_tree_snapshot(&(display_tree_with_odb(actual, &storage, &odb))), @r#"        Oid(1)        └── hello-from-cursor Oid(2).100644    "#);     let mut cursor = edit.set_root(root_tree).to_cursor();    let actual = cursor        .remove(Some("bin"))?        .remove(Some("bin.d"))?        .remove(Some("file.to"))?        .remove(Some("file.toml"))?        .remove(Some("file.toml.bin"))?        .upsert(["some", "nested", "file"], EntryKind::Blob, any_blob())?        .write(&mut write)?;    insta::assert_snapshot!(        crate::normalize_tree_snapshot(&(display_tree_with_odb(actual, &storage, &odb))),        "this cursor is the same as the editor",        @r#"        Oid(1)        ├── file        │   └── a Oid(2).100644        ├── file0 Oid(2).100644        └── some            └── nested                └── file Oid(3).100644    "#    );    let actual = cursor.remove(["some", "nested", "file"])?.write(&mut write)?;    insta::assert_snapshot!(        crate::normalize_tree_snapshot(&(display_tree_with_odb(actual, &storage, &odb))),        "it's possible to delete a deeply nested file",        @r#"        Oid(1)        ├── file        │   └── a Oid(2).100644        └── file0 Oid(2).100644    "#    );    Ok(())}#[test]fn from_empty_removal() -> crate::Result {    let (storage, mut write, num_writes_and_clear) = new_inmemory_writes();    let odb = StorageOdb::new(storage.clone());    let mut edit = gix_object::tree::Editor::new(Tree::default(), &odb, hash_kind());     let actual = edit        .remove(Some("non-existing"))?        .remove(["still", "does", "not", "exist"])?        .write(&mut write)?;    assert_eq!(actual, empty_tree(), "nothing was actually done");    assert_eq!(num_writes_and_clear(), 1, "it has to write the empty tree though");    assert_eq!(storage.borrow().len(), 1, "the empty tree ends up in storage, too");    assert_eq!(odb.access_count_and_clear(), 0);     let actual = edit        .upsert(Some("file"), EntryKind::Blob, any_blob())?        .upsert(Some("empty-dir"), EntryKind::Tree, empty_tree())?        .upsert(["with-subdir", "dir", "file"], EntryKind::Blob, any_blob())?        .upsert(["with-subdir2", "dir", "file"], EntryKind::Blob, any_blob())?        .remove(Some("file"))?        .remove(Some("empty-dir"))?        .remove(Some("with-subdir"))?        .remove(["with-subdir2", "dir"])?        .remove(Some("with-subdir2"))?        .write(&mut write)?;    assert_eq!(actual, empty_tree(), "nothing was actually done");    assert_eq!(num_writes_and_clear(), 1, "still nothing to write but the empty tree");    assert_eq!(odb.access_count_and_clear(), 0);     let actual = edit        .upsert(Some("file"), EntryKind::Blob, any_blob())?        .upsert(Some("empty-dir"), EntryKind::Tree, empty_tree())?        .upsert(["with-subdir", "dir", "file"], EntryKind::Blob, any_blob())?        .upsert(["with-subdir2", "dir", "file"], EntryKind::Blob, any_blob())?        .write(&mut write)?;    insta::assert_snapshot!(crate::normalize_tree_snapshot(&display_tree(actual, &storage)), @r#"        Oid(1)        ├── empty-dir (empty)        ├── file Oid(2).100644        ├── with-subdir        │   └── dir        │       └── file Oid(2).100644        └── with-subdir2            └── dir                └── file Oid(2).100644    "#);    assert_eq!(num_writes_and_clear(), 5);    assert_eq!(odb.access_count_and_clear(), 0);     let actual = edit        .remove(Some("file"))?        .remove(Some("empty-dir"))?        .remove(Some("with-subdir"))?        .remove(["with-subdir2", "dir"])?        .remove(Some("with-subdir2"))?        .write(&mut write)?;    assert_eq!(actual, empty_tree(), "everything was removed, leaving nothing");    assert_eq!(num_writes_and_clear(), 1, "only the empty tree to write");    assert_eq!(        odb.access_count_and_clear(),        1,        "has to get `with-subdir2` to remove child-entry"    );     let actual = edit        .upsert(["with-subdir", "file"], EntryKind::Blob, any_blob())?        .upsert(["with-subdir", "dir", "file"], EntryKind::Blob, any_blob())?        .remove(["with-subdir", "dir"])?        .write(&mut write)?;    insta::assert_snapshot!(        crate::normalize_tree_snapshot(&display_tree(actual, &storage)),        "only one file remains, empty dirs are removed automatically",        @r#"        Oid(1)        └── with-subdir            └── file Oid(2).100644    "#    );    assert_eq!(num_writes_and_clear(), 1 + 1, "root and one subtree");    assert_eq!(storage.borrow().len(), 1 + 4, "empty tree and 4 unique trees");    assert_eq!(odb.access_count_and_clear(), 0);     Ok(())} #[test]fn from_empty_remove_accepts_empty_segments_after_unreachable_paths() -> crate::Result {    let (storage, mut write, _num_writes_and_clear) = new_inmemory_writes();    let odb = StorageOdb::new(storage.clone());    let mut edit = gix_object::tree::Editor::new(Tree::default(), &odb, hash_kind());     let actual = edit        .upsert(Some("file"), EntryKind::Blob, any_blob())?        .upsert(["dir", "file"], EntryKind::Blob, any_blob())?        .remove(["missing", ""])?        .remove(["file", ""])?        .remove(["dir", "missing", ""])?        .write(&mut write)?;     insta::assert_snapshot!(        crate::normalize_tree_snapshot(&display_tree(actual, &storage)),        "empty path components after an unreachable removal target are not reached or validated",        @r#"        Oid(1)        ├── dir        │   └── file Oid(2).100644        └── file Oid(2).100644    "#    );    Ok(())} #[test]fn from_empty_remove_leaf_rejects_tree_entries() -> crate::Result {    let (storage, mut write, _num_writes_and_clear) = new_inmemory_writes();    let odb = StorageOdb::new(storage.clone());    let mut edit = gix_object::tree::Editor::new(Tree::default(), &odb, hash_kind());     edit.upsert(["A", "one"], EntryKind::Blob, any_blob())?;     let err = edit.remove_leaf(Some("A")).unwrap_err();    assert_eq!(        err.to_string(),        "Cannot remove 'A' as leaf entry because it is a tree",        "leaf-only removal must reject non-leaf entries"    );     edit.remove_leaf(["A", "one"])?;    let actual = edit.write(&mut write)?;    assert_eq!(actual, empty_tree(), "the leaf itself can still be removed");     let actual = edit        .upsert(Some("empty-dir"), EntryKind::Tree, empty_tree())?        .remove_leaf(["empty-dir", "missing"])?        .write(&mut write)?;    insta::assert_snapshot!(        crate::normalize_tree_snapshot(&display_tree(actual, &storage)),        "an absent leaf below an empty tree is a no-op and must not prune the tree",        @r#"        Oid(1)        └── empty-dir (empty)    "#    );    edit.remove_leaf(["empty-dir", ""])?;    edit.remove_leaf(["empty-dir", "missing", ""])?;    edit.remove_leaf(["missing", ""])?;    edit.upsert(["non-empty", "file"], EntryKind::Blob, any_blob())?        .remove_leaf(["non-empty", "missing", ""])?        .remove_leaf(["non-empty", "file", ""])?;     let actual = edit        .set_root(Tree::default())        .upsert(Some("empty-dir"), EntryKind::Tree, empty_tree())?        .upsert(["empty-dir", "file"], EntryKind::Blob, any_blob())?        .remove_leaf(["empty-dir", "file"])?        .write(&mut write)?;    assert_eq!(        actual,        empty_tree(),        "leaf-only removal still descends into edited subtrees backed by an empty tree"    );    Ok(())} #[test]fn from_existing_remove() -> crate::Result {    let (storage, mut write, num_writes_and_clear) = new_inmemory_writes();    let odb = StorageOdb::new_with_odb(storage.clone(), tree_odb()?);    let root_tree_id = crate::generated_tree_root_id()?;    let root_tree = find_tree(&odb, root_tree_id)?;    odb.access_count_and_clear();    let mut edit = gix_object::tree::Editor::new(root_tree.clone(), &odb, hash_kind());     let actual = edit        .remove(["file"])?        .remove(Some("does not exist"))?        .remove(["also", "does", "not", "exist"])?        .remove(Some("bin.d"))?        .remove(Some("file.toml.bin"))?        .remove(Some("file.0"))?        .write(&mut write)?;    insta::assert_snapshot!(crate::normalize_tree_snapshot(&(display_tree_with_odb(actual, &storage, &odb))), @r#"        Oid(1)        ├── bin Oid(2).100644        ├── file.to Oid(2).100644        ├── file.toml Oid(2).100644        └── file0 Oid(2).100644    "#);    assert_eq!(num_writes_and_clear(), 1, "only the root tree is written");    assert_eq!(        odb.access_count_and_clear(),        0,        "no sub-tree has to be queried for removal"    );     let actual = edit        .remove(Some("bin"))?        .remove(Some("file.to"))?        .remove(Some("file.toml"))?        .remove(Some("file0"))?        .write(&mut write)?;    assert_eq!(actual, empty_tree(), "nothing is left");    assert_eq!(num_writes_and_clear(), 1, "only the empty tree is written");    assert_eq!(odb.access_count_and_clear(), 0);     let actual = edit.set_root(root_tree).remove(["file", "a"])?.write(&mut write)?;    assert_eq!(num_writes_and_clear(), 1, "it writes the changed root-tree");    assert_eq!(        odb.access_count_and_clear(),        1,        "lookup `file` to delete its (only) content"    );    insta::assert_snapshot!(        crate::normalize_tree_snapshot(&(display_tree_with_odb(actual, &storage, &odb))),        "`file` is removed as it remains empty",        @r#"        Oid(1)        ├── bin Oid(2).100644        ├── bin.d Oid(2).100644        ├── file.to Oid(2).100644        ├── file.toml Oid(2).100644        ├── file.toml.bin Oid(2).100644        └── file0 Oid(2).100644    "#    );     Ok(())}#[test]fn from_empty_invalid_write() -> crate::Result {    let (storage, mut write, _num_writes_and_clear) = new_inmemory_writes();    let odb = StorageOdb::new(storage.clone());    let mut edit = gix_object::tree::Editor::new(Tree::default(), &odb, hash_kind());     let actual = edit        .upsert(["a", "\n"], EntryKind::Blob, any_blob())?        .write(&mut write)        .expect("no validation is performed");    insta::assert_snapshot!(crate::normalize_tree_snapshot(&display_tree(actual, &storage)), @r#"        Oid(1)        └── a            └──          Oid(2).100644    "#);     let err = edit        .upsert(Some("with\0null"), EntryKind::Blob, any_blob())?        .write(&mut write)        .unwrap_err();    assert_eq!(        err.to_string(),        r#"Nullbytes are invalid in file paths as they are separators: "with\0null""#    );     let err = edit.upsert(Some(""), EntryKind::Blob, any_blob()).unwrap_err();    let expected_msg = "Empty path components are not allowed";    assert_eq!(err.to_string(), expected_msg);    let err = edit        .upsert(["fine", "", "previous is not fine"], EntryKind::Blob, any_blob())        .unwrap_err();    assert_eq!(err.to_string(), expected_msg);     let actual = edit        .remove(Some("a"))?        .remove(Some("with\0null"))?        .upsert(Some("works"), EntryKind::Blob, any_blob())?        .write(&mut write)?;    insta::assert_snapshot!(        crate::normalize_tree_snapshot(&display_tree(actual, &storage)),        "after removing invalid entries, it can write again",        @r#"        Oid(1)        └── works Oid(2).100644    "#    );    Ok(())

Lines 1–500 of 1044.