zccache 1.12.16

Local-first compiler cache for C/C++/Rust/Emscripten
Documentation
//! Snapshot regression coverage for equivalent worktree variants.

use std::path::Path;

use tempfile::TempDir;
use crate::core::NormalizedPath;
use crate::hash::{hash_bytes, ContentHash};

use super::super::super::context::CompileContext;
use super::super::super::graph::{CacheVerdict, DepGraph};
use super::super::super::scanner::ScanResult;
use super::super::super::search_paths::IncludeSearchPaths;
use super::super::super::snapshot::{load_from_file, save_to_file};
use super::{always_fresh, test_path};

fn context(root: &str) -> CompileContext {
    CompileContext {
        source_file: NormalizedPath::from(format!("{root}/src/lib.rs").as_str()),
        include_search: IncludeSearchPaths::default(),
        defines: Vec::new(),
        flags: Vec::new(),
        force_includes: Vec::new(),
        unknown_flags: Vec::new(),
    }
}

fn scan(root: &str) -> ScanResult {
    ScanResult {
        resolved: vec![NormalizedPath::from(
            format!("{root}/include/shared.h").as_str(),
        )],
        unresolved: Vec::new(),
        has_computed: false,
    }
}

fn equal_hash(path: &Path) -> Option<ContentHash> {
    Some(hash_bytes(
        path.file_name()
            .unwrap_or_default()
            .to_string_lossy()
            .as_bytes(),
    ))
}

#[test]
fn equivalent_worktree_variants_survive_snapshot_roundtrip() {
    let dir = TempDir::new().unwrap();
    let path = test_path(&dir);
    let graph = DepGraph::new();
    let a = graph.register_with_root_result(
        context("/snapshot-a"),
        Some(NormalizedPath::from("/snapshot-a")),
    );
    let artifact_a = graph
        .update(&a.map_key, scan("/snapshot-a"), equal_hash)
        .unwrap();
    let b = graph.register_with_root_result(
        context("/snapshot-b"),
        Some(NormalizedPath::from("/snapshot-b")),
    );
    let changed_b = |path: &Path| {
        let bytes: &[u8] = if path.ends_with("lib.rs") {
            b"changed B"
        } else {
            b"header"
        };
        Some(hash_bytes(bytes))
    };
    let artifact_b = graph
        .update(&b.map_key, scan("/snapshot-b"), changed_b)
        .unwrap();
    assert_ne!(artifact_a, artifact_b);

    save_to_file(&graph, &path).unwrap();
    let loaded = load_from_file(&path).unwrap();
    assert_eq!(loaded.stats().context_count, 2);
    assert!(matches!(
        loaded.check(&a.map_key, always_fresh, equal_hash),
        CacheVerdict::Hit { artifact_key } if artifact_key == artifact_a
    ));
    assert!(matches!(
        loaded.check(&b.map_key, always_fresh, changed_b),
        CacheVerdict::Hit { artifact_key } if artifact_key == artifact_b
    ));
}