ic-build 0.7.0

Cargo and Wasm builds, exact inputs and retained artifact caches
Documentation
use super::*;
use crate::artifacts::test_support::unique_temp_directory;

#[test]
fn discovery_and_environment_classification_keep_caller_selection() {
    let root = unique_temp_directory("cargo-environment-directories");
    let invocation = root.join("workspace/nested");
    let directories = configuration_directories(
        &invocation,
        Some(OsStr::new("relative-home")),
        Some(OsStr::new("ignored")),
    )
    .unwrap();
    assert_eq!(directories[0], invocation.join(".cargo"));
    assert_eq!(
        directories.last().unwrap(),
        &invocation.join("relative-home")
    );
    let default = configuration_directories(&invocation, None, Some(root.as_os_str())).unwrap();
    assert_eq!(default.last().unwrap(), &root.join(".cargo"));
    assert!(configuration_directories(Path::new("relative"), None, None).is_err());
    for name in [
        "RUSTC",
        "RUSTDOC",
        "CARGO_BUILD_RUSTC_WRAPPER",
        "RUSTC_WORKSPACE_WRAPPER",
    ] {
        assert!(is_compiler_override(name));
    }
    for name in [
        "RUSTDOCFLAGS",
        "CARGO_ENCODED_RUSTFLAGS",
        "CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_LINKER",
        "CARGO_PROFILE_RELEASE_LTO",
    ] {
        assert!(is_build_variable(name));
    }
    for name in ["HOME", "CARGO_HOME", "CARGO_TARGET_DIR", "GITHUB_TOKEN"] {
        assert!(!is_build_variable(name));
        assert!(!is_compiler_override(name));
    }
    fs::remove_dir_all(root).unwrap();
}

#[test]
fn observations_bind_raw_bytes_overrides_and_include_trees() {
    let root = unique_temp_directory("cargo-environment-observe");
    let cargo = root.join(".cargo");
    fs::create_dir_all(&cargo).unwrap();
    let config = cargo.join("config");
    let shadowed = cargo.join("config.toml");
    let included = cargo.join("included.toml");
    let source = "include = ['included.toml', {path = 'missing.toml', optional = true}]\n[build]\nrustc = 'caller-compiler'\nrustdoc = 'caller-doc'\n";
    fs::write(&config, source).unwrap();
    fs::write(&shadowed, "[build]\nrustc-wrapper = 'shadowed'\n").unwrap();
    fs::write(
        &included,
        "include = 'config'\n[build]\nrustc-workspace-wrapper = 'included'\n",
    )
    .unwrap();
    assert_eq!(preferred_configuration(&cargo), Some(config.clone()));
    let observed = observe_configurations(&[config.clone(), shadowed.clone()]).unwrap();
    assert_eq!(
        observed.iter().map(|file| &file.path).collect::<Vec<_>>(),
        vec![&config, &included, &shadowed]
    );
    assert_eq!(
        observed[0].sha256,
        <[u8; 32]>::from(Sha256::digest(source.as_bytes()))
    );
    assert_eq!(observed[0].compiler_overrides, ["rustc", "rustdoc"]);
    assert_eq!(observed[1].compiler_overrides, ["rustc-workspace-wrapper"]);
    assert_eq!(observed[2].compiler_overrides, ["rustc-wrapper"]);
    fs::write(&config, source.replace("caller-doc", "changed-doc")).unwrap();
    assert_ne!(
        observe_configurations(&[config]).unwrap()[0].sha256,
        observed[0].sha256
    );
    fs::remove_dir_all(root).unwrap();
}

#[test]
fn required_and_malformed_configuration_fail_without_source_text_in_diagnostics() {
    let root = unique_temp_directory("cargo-environment-refusal");
    fs::create_dir_all(&root).unwrap();
    let config = root.join("config.toml");
    for contents in [
        "include = 'missing.toml'\n",
        "include = 1\n",
        "include = {path = 1}\n",
        "include = {path = 'nested.toml', optional = 1}\n",
        "secret = 'private-value'\n[broken\n",
    ] {
        fs::write(&config, contents).unwrap();
        let Err(error) = observe_configurations(std::slice::from_ref(&config)) else {
            panic!("invalid configuration must fail");
        };
        assert!(!format!("{error:?}").contains("private-value"));
        assert!(!error.to_string().contains("private-value"));
    }
    assert!(observe_configurations(&[PathBuf::from("relative.toml")]).is_err());
    fs::remove_dir_all(root).unwrap();
}