mod common;
fn dist_config() -> String {
common::without_comments("dist-workspace.toml")
}
fn rust_workflow() -> String {
common::without_comments(".github/workflows/rust.yml")
}
fn release_workflow() -> String {
common::without_comments(".github/workflows/release.yml")
}
fn workflow_job<'a>(workflow: &'a str, name: &str) -> &'a str {
let marker = format!("\n {name}:\n");
let start = workflow
.find(&marker)
.unwrap_or_else(|| panic!("workflow must declare a {name} job"))
+ marker.len();
let tail = &workflow[start..];
let end = tail
.match_indices('\n')
.find_map(|(offset, _)| {
let next_line = &tail[offset + 1..];
(next_line.starts_with(" ") && !next_line.starts_with(" ")).then_some(offset)
})
.unwrap_or(tail.len());
&tail[..end]
}
#[test]
fn family_ci_runs_linux_without_queueing_hosted_macos() {
let workflow = rust_workflow();
let linux = workflow_job(&workflow, "test-linux");
assert!(
linux.contains("runs-on: ubuntu-latest"),
"the family runner must retain the Linux test lane"
);
assert!(
linux.contains("cargo test --all-targets --all-features"),
"the Linux lane must run the complete test suite"
);
let macos = workflow_job(&workflow, "test-macos");
assert!(
macos.contains("if: ${{ github.server_url == 'https://github.com' }}"),
"the hosted macOS lane must not execute on Gitea"
);
assert!(
macos.contains(
"runs-on: ${{ github.server_url == 'https://github.com' && 'macos-latest' || 'ubuntu-latest' }}"
),
"GitHub must use native macOS while Gitea gets a claimable label for its guarded skip"
);
}
#[test]
fn mutation_ci_pins_a_compatible_container_and_tool() {
let workflow = rust_workflow();
let mutants = workflow_job(&workflow, "mutants");
assert!(
mutants.contains("container: node:22-trixie"),
"cargo-mutants needs the Debian 13 glibc supplied by node:22-trixie"
);
assert!(
mutants.contains("tool: cargo-mutants@27.1.0"),
"the mutation gate must pin the binary whose glibc contract was verified"
);
}
#[test]
fn remote_full_mutation_sweep_passes_no_phantom_argument() {
let script = common::without_comments("scripts/mutants-remote.sh");
assert!(
script.contains("if [ \"$#\" -gt 0 ]; then")
&& script.contains("printf -v REMOTE_ARGS ' %q' \"$@\"")
&& script.contains("./scripts/mutants-run.sh$REMOTE_ARGS"),
"the remote wrapper must append quoted arguments only when at least one exists"
);
assert!(
!script.contains("$(printf '%q ' \"$@\")"),
"empty positional parameters must not be formatted into a literal empty argument"
);
}
#[test]
fn every_supported_platform_is_built() {
let config = dist_config();
for target in [
"aarch64-apple-darwin",
"x86_64-apple-darwin",
"x86_64-unknown-linux-gnu",
"aarch64-unknown-linux-gnu",
] {
assert!(
config.contains(target),
"a release no longer builds for {target}"
);
}
}
#[test]
fn the_homebrew_formula_is_pushed_to_the_tap() {
let config = dist_config();
assert!(
config.contains(r#"installers = ["shell", "homebrew"]"#),
"a release must publish both the shell installer and the formula"
);
assert!(
config.contains(r#"tap = "slb350/homebrew-tap""#),
"the formula has no tap to be pushed to"
);
assert!(
config.contains(r#"publish-jobs = ["homebrew"]"#),
"without the homebrew publish job the formula is built and never pushed"
);
assert!(
config.contains(r#"formula = "drep""#),
"the formula must be named for the binary, not for the crate"
);
}
#[test]
fn the_pinned_dist_version_is_the_one_ci_installs() {
let config = dist_config();
let pinned = config
.lines()
.find_map(|line| line.strip_prefix("cargo-dist-version = "))
.expect("dist-workspace.toml must pin a dist version")
.trim()
.trim_matches('"');
let workflow = release_workflow();
assert!(
workflow.contains(&format!("cargo-dist/releases/download/v{pinned}/")),
"release.yml installs a different dist than the config pins ({pinned}) - run `dist init`"
);
}
#[test]
fn release_artifact_actions_use_the_compatible_v4_protocol() {
let config = dist_config();
let workflow = release_workflow();
let artifact_actions = workflow
.lines()
.map(str::trim)
.filter_map(|line| line.strip_prefix("uses: actions/"))
.filter(|action| {
action.starts_with("upload-artifact@") || action.starts_with("download-artifact@")
})
.collect::<Vec<_>>();
assert!(
artifact_actions
.iter()
.any(|action| action.starts_with("upload-artifact@")),
"release.yml must upload build artifacts"
);
assert!(
artifact_actions
.iter()
.any(|action| action.starts_with("download-artifact@")),
"release.yml must download artifacts between jobs"
);
assert!(
artifact_actions
.iter()
.all(|action| action.ends_with("@v4")),
"all release artifact actions must use v4, found: {artifact_actions:?}"
);
assert!(
config.contains(r#"allow-dirty = ["ci"]"#),
"cargo-dist must permit the tested release.yml compatibility override"
);
}
#[test]
fn released_binaries_inherit_the_tuned_release_profile() {
let path = concat!(env!("CARGO_MANIFEST_DIR"), "/Cargo.toml");
let raw = std::fs::read_to_string(path).expect("Cargo.toml must be readable");
let manifest: toml::Table = toml::from_str(&raw).expect("Cargo.toml must parse");
let profile = manifest["profile"]
.get("dist")
.and_then(toml::Value::as_table)
.expect("Cargo.toml must declare the profile dist builds with");
assert_eq!(
profile.keys().map(String::as_str).collect::<Vec<_>>(),
["inherits"],
"[profile.dist] must add nothing to [profile.release]"
);
assert_eq!(profile["inherits"].as_str(), Some("release"));
}