use super::*;
fn identity() -> GithubPath {
GithubPath {
owner: "bobmatnyc".to_string(),
repo: "trusty-tools".to_string(),
}
}
#[test]
fn rust_build_env_defaults_target_dir_from_git_remote() {
let home = std::path::Path::new("/tmp/fake-home");
let resolved = resolve_build_env(None, home, Some(&identity()), 16)
.expect("a derivable identity must resolve");
assert_eq!(
resolved.cargo_target_dir,
home.join(".trusty-tools")
.join("cargo-target")
.join("bobmatnyc")
.join("trusty-tools"),
"the default must be ~/.trusty-tools/cargo-target/<owner>/<repo>"
);
assert!(
!resolved.target_dir_configured,
"a derived path must not be reported as configured"
);
let other = GithubPath {
owner: "bobmatnyc".to_string(),
repo: "other-repo".to_string(),
};
let other_resolved =
resolve_build_env(None, home, Some(&other), 16).expect("second identity resolves");
assert_ne!(
resolved.cargo_target_dir, other_resolved.cargo_target_dir,
"two repos must never share one target directory"
);
}
#[test]
fn rust_build_env_defaults_build_jobs_to_half_cores_min_two() {
assert_eq!(default_build_jobs(16), 8, "16 cores → 8 jobs");
assert_eq!(default_build_jobs(8), 4, "8 cores → 4 jobs");
assert_eq!(default_build_jobs(4), 2, "4 cores → 2 jobs");
assert_eq!(default_build_jobs(3), 2, "3 cores → the floor, not 1");
assert_eq!(default_build_jobs(1), 2, "1 core → the floor, not 0");
assert_eq!(
default_build_jobs(0),
2,
"an unknown core count → the floor"
);
let resolved = resolve_build_env(None, std::path::Path::new("/tmp/h"), Some(&identity()), 16)
.expect("resolves");
assert_eq!(resolved.build_jobs, 8, "resolution must use the same rule");
}
#[test]
fn rust_build_env_paste_line_matches_resolved_values() {
let env = ResolvedBuildEnv {
cargo_target_dir: std::path::PathBuf::from("/shared/target"),
build_jobs: 8,
sccache: true,
target_dir_configured: true,
};
assert_eq!(
paste_line(&env),
"CARGO_TARGET_DIR=/shared/target CARGO_BUILD_JOBS=8 RUSTC_WRAPPER=sccache SKIP_UI_BUILD=1"
);
let line = paste_line(&env);
assert!(line.contains(&env.cargo_target_dir.display().to_string()));
assert!(line.contains(&format!("CARGO_BUILD_JOBS={}", env.build_jobs)));
assert!(
line.ends_with("SKIP_UI_BUILD=1"),
"the UI-build skip must close the line: {line}"
);
}
#[test]
fn the_paste_line_omits_the_wrapper_when_sccache_is_off() {
let env = ResolvedBuildEnv {
cargo_target_dir: std::path::PathBuf::from("/shared/target"),
build_jobs: 4,
sccache: false,
target_dir_configured: false,
};
let line = paste_line(&env);
assert!(!line.contains("RUSTC_WRAPPER"), "{line}");
assert_eq!(
line,
"CARGO_TARGET_DIR=/shared/target CARGO_BUILD_JOBS=4 SKIP_UI_BUILD=1"
);
}
#[test]
fn an_absent_section_resolves_defaults() {
let resolved = resolve_build_env(None, std::path::Path::new("/h"), Some(&identity()), 12)
.expect("an absent section resolves");
assert_eq!(resolved.build_jobs, 6);
assert!(!resolved.sccache, "sccache is opt-in, never a default");
}
#[test]
fn a_configured_target_dir_wins_over_the_remote_default() {
let home = std::path::Path::new("/tmp/fake-home");
let config = BuildConfig {
cargo_target_dir: Some("~/elsewhere/target".to_string()),
build_jobs: Some(3),
sccache: Some(true),
};
let resolved = resolve_build_env(Some(&config), home, Some(&identity()), 16).expect("resolves");
assert_eq!(resolved.cargo_target_dir, home.join("elsewhere/target"));
assert!(resolved.target_dir_configured);
assert_eq!(resolved.build_jobs, 3, "a configured job count wins");
assert!(resolved.sccache);
}
#[test]
fn resolution_without_a_remote_or_a_config_is_an_error() {
let err = resolve_build_env(None, std::path::Path::new("/h"), None, 8)
.expect_err("no identity and no config must not resolve");
assert_eq!(err, BuildEnvError::NoRepoIdentity);
assert!(
err.to_string().contains("origin"),
"the error must name what could not be read: {err}"
);
let config = BuildConfig {
cargo_target_dir: Some("/explicit/target".to_string()),
..BuildConfig::default()
};
assert!(
resolve_build_env(Some(&config), std::path::Path::new("/h"), None, 8).is_ok(),
"an explicit directory must not need a remote"
);
}
#[test]
fn a_zeroed_build_jobs_falls_back_to_the_default() {
let config = BuildConfig {
build_jobs: Some(0),
..BuildConfig::default()
};
let resolved = resolve_build_env(
Some(&config),
std::path::Path::new("/h"),
Some(&identity()),
16,
)
.expect("resolves");
assert_eq!(resolved.build_jobs, 8);
}
#[test]
fn build_config_yaml_round_trips() {
let config = BuildConfig {
cargo_target_dir: Some("/shared/target".to_string()),
build_jobs: Some(8),
sccache: Some(false),
};
let yaml = serde_yaml::to_string(&config).expect("serialises");
let back: BuildConfig = serde_yaml::from_str(&yaml).expect("round-trips");
assert_eq!(back, config);
let empty = serde_yaml::to_string(&BuildConfig::default()).expect("serialises");
assert!(
!empty.contains("cargo_target_dir"),
"an absent key must not be emitted: {empty}"
);
}
#[test]
fn the_seeded_block_parses_back_to_the_resolved_values() {
let env = ResolvedBuildEnv {
cargo_target_dir: std::path::PathBuf::from("/shared/target: with colon"),
build_jobs: 8,
sccache: false,
target_dir_configured: false,
};
let block = default_build_section_yaml(&env);
#[derive(serde::Deserialize)]
struct Doc {
build: BuildConfig,
}
let doc: Doc = serde_yaml::from_str(&block).expect("the seeded block must be valid YAML");
assert_eq!(
doc.build.cargo_target_dir.as_deref(),
Some("/shared/target: with colon"),
"a path containing a colon must survive quoting"
);
assert_eq!(doc.build.build_jobs, Some(8));
assert_eq!(doc.build.sccache, Some(false));
}