vfstool_lib 0.9.0

A library for constructing and manipulating virtual file systems in Rust, based on OpenMW's VFS implementation.
Documentation
use super::*;

#[test]
fn provenance_chain_respects_load_order() {
    let low = TempDir::new("analysis_provenance_low");
    let high = TempDir::new("analysis_provenance_high");
    low.write("textures/a.dds", b"low");
    high.write("textures/a.dds", b"high");

    let (vfs, index) = VFS::from_directories_with_layer_index([low.path(), high.path()], None);
    let chain = index
        .provenance(&vfs, Path::new("textures/a.dds"), false)
        .expect("provenance should succeed")
        .expect("path should exist");

    assert_eq!(chain.providers.len(), 2);
    assert_eq!(chain.providers[0].source.path, low.path());
    assert_eq!(chain.providers[1].source.path, high.path());
    assert_eq!(chain.winner.path, high.path());
}

#[test]
fn layer_index_preserves_provider_occurrences_within_one_source() {
    let index = LayerIndex::from_file_lists(vec![(
        SourceMeta {
            path: PathBuf::from("/one"),
            kind: SourceKind::LooseDir,
        },
        vec![PathBuf::from("shared.txt"), PathBuf::from("SHARED.TXT")],
    )]);

    assert_eq!(index.sources_containing(Path::new("shared.txt")), &[0, 0]);
    let chain = index.provider_chain(Path::new("shared.txt"));
    assert_eq!(chain.len(), 2);
    assert_eq!(chain[0].provider_index, 0);
    assert_eq!(chain[0].original_path, PathBuf::from("shared.txt"));
    assert_eq!(chain[1].provider_index, 1);
    assert_eq!(chain[1].original_path, PathBuf::from("SHARED.TXT"));
}

#[test]
fn layer_index_includes_unique_provider_keys() {
    let data = TempDir::new("analysis_unique_provider");
    data.write("unique.txt", b"unique");
    let (_vfs, index) = VFS::from_directories_with_layer_index([data.path()], None);

    assert_eq!(index.sources_containing(Path::new("unique.txt")), &[0]);
}

#[test]
fn layer_index_skips_unsafe_provider_keys() {
    let index = LayerIndex::from_file_lists(vec![
        (
            SourceMeta {
                path: PathBuf::from("/one"),
                kind: SourceKind::LooseDir,
            },
            vec![PathBuf::from("../escape.txt"), PathBuf::from("safe.txt")],
        ),
        (
            SourceMeta {
                path: PathBuf::from("/two"),
                kind: SourceKind::LooseDir,
            },
            vec![PathBuf::from("/absolute.txt")],
        ),
    ]);

    assert_eq!(index.keys(), vec![crate::NormalizedPath::from("safe.txt")]);
}

#[test]
fn source_contributions_count_middle_sources_as_overriding_and_overridden() {
    let index = LayerIndex::from_file_lists(vec![
        (
            SourceMeta {
                path: PathBuf::from("/low"),
                kind: SourceKind::LooseDir,
            },
            vec![PathBuf::from("shared.txt")],
        ),
        (
            SourceMeta {
                path: PathBuf::from("/mid"),
                kind: SourceKind::LooseDir,
            },
            vec![PathBuf::from("shared.txt")],
        ),
        (
            SourceMeta {
                path: PathBuf::from("/high"),
                kind: SourceKind::LooseDir,
            },
            vec![PathBuf::from("shared.txt")],
        ),
    ]);

    let contributions = index.source_contributions();
    let low = &contributions.sources[0];
    let mid = &contributions.sources[1];
    let high = &contributions.sources[2];

    assert_eq!(low.overridden_files, 1);
    assert_eq!(low.overriding_files, 0);
    assert_eq!(mid.overridden_files, 1);
    assert_eq!(mid.overriding_files, 1);
    assert_eq!(high.winning_files, 1);
    assert_eq!(high.overriding_files, 1);
}

#[test]
fn same_source_duplicate_occurrences_do_not_count_as_cross_source_overrides() {
    let index = LayerIndex::from_file_lists(vec![(
        SourceMeta {
            path: PathBuf::from("/one"),
            kind: SourceKind::LooseDir,
        },
        vec![PathBuf::from("shared.txt"), PathBuf::from("SHARED.TXT")],
    )]);

    let contributions = index.source_contributions();
    let source = &contributions.sources[0];

    assert_eq!(source.duplicate_files, 2);
    assert_eq!(source.winning_files, 1);
    assert_eq!(source.overriding_files, 0);
    assert_eq!(source.overridden_files, 0);
}

#[test]
fn duplicate_provider_occurrences_have_matching_vfs_provider_stack() {
    let data = TempDir::new("analysis_layer_duplicate_provider_occurrences");
    data.write("upper/Foo.DDS", b"upper");
    data.write("lower/foo.dds", b"lower");

    let source = SourceMeta {
        path: data.path().to_path_buf(),
        kind: SourceKind::LooseDir,
    };
    let key = crate::NormalizedPath::new(b"textures/foo.dds");
    let mut vfs = VFS::new();
    assert_eq!(
        vfs.push_provider_batch(
            &source,
            [
                (
                    key.clone(),
                    crate::VfsFile::from(data.path().join("upper/Foo.DDS"))
                ),
                (key, crate::VfsFile::from(data.path().join("lower/foo.dds"))),
            ],
        ),
        2
    );
    let index = LayerIndex::from_file_lists([(
        source,
        vec![
            PathBuf::from("Textures/Foo.DDS"),
            PathBuf::from("textures/foo.dds"),
        ],
    )]);

    assert_eq!(vfs.provider_records_for("textures/foo.dds").len(), 2);
    let chain = index.provider_chain(Path::new("textures/foo.dds"));
    assert_eq!(chain.len(), 2);
    assert_eq!(chain[0].source_index, 0);
    assert_eq!(chain[1].source_index, 0);
}