blob: 82531f328da667b46feb1340ad99ad86a87ab101 [file] [edit]
use std::path::{Path, PathBuf};
use std::process::Command;
use std::{env, fs};
struct Test {
repo: &'static str,
name: &'static str,
sha: &'static str,
lock: Option<&'static str>,
packages: &'static [&'static str],
features: Option<&'static [&'static str]>,
manifest_path: Option<&'static str>,
/// `filters` are passed to libtest (i.e., after a `--` in the `cargo test` invocation).
filters: &'static [&'static str],
}
const TEST_REPOS: &[Test] = &[
Test {
name: "iron",
repo: "https://github.com/iron/iron",
sha: "cf056ea5e8052c1feea6141e40ab0306715a2c33",
lock: None,
packages: &[],
features: None,
manifest_path: None,
filters: &[],
},
Test {
name: "ripgrep",
repo: "https://github.com/BurntSushi/ripgrep",
sha: "ced5b92aa93eb47e892bd2fd26ab454008721730",
lock: None,
packages: &[],
features: None,
manifest_path: None,
filters: &[],
},
Test {
name: "tokei",
repo: "https://github.com/XAMPPRocky/tokei",
sha: "fdf3f8cb279a7aeac0696c87e5d8b0cd946e4f9e",
lock: None,
packages: &[],
features: None,
manifest_path: None,
filters: &[],
},
Test {
name: "xsv",
repo: "https://github.com/BurntSushi/xsv",
sha: "3de6c04269a7d315f7e9864b9013451cd9580a08",
lock: None,
packages: &[],
features: None,
manifest_path: None,
// Many tests here use quickcheck and some of them can fail randomly, so only run deterministic tests.
filters: &[
"test_flatten::",
"test_fmt::",
"test_headers::",
"test_index::",
"test_join::",
"test_partition::",
"test_search::",
"test_select::",
"test_slice::",
"test_split::",
"test_stats::",
"test_table::",
],
},
Test {
name: "stylo",
repo: "https://github.com/servo/stylo",
sha: "127b0b5cab6a6927552e889debb20beb031b79d1",
lock: None,
packages: &["selectors", "stylo"],
features: None,
manifest_path: None,
filters: &[],
},
Test {
name: "diesel",
repo: "https://github.com/diesel-rs/diesel",
sha: "3db7c17c5b069656ed22750e84d6498c8ab5b81d",
lock: None,
packages: &[],
// Test the embedded sqlite variant of diesel
// This does not require any dependency to be present,
// sqlite will be compiled as part of the build process
features: Some(&["sqlite", "libsqlite3-sys/bundled"]),
// We are only interested in testing diesel itself
// not any other crate present in the diesel workspace
// (This is required to set the feature flags above)
manifest_path: Some("diesel/Cargo.toml"),
filters: &[],
},
];
fn main() {
let args = env::args().collect::<Vec<_>>();
let cargo = &args[1];
let out_dir = Path::new(&args[2]);
let cargo = &Path::new(cargo);
for test in TEST_REPOS.iter().rev() {
if args[3..].is_empty() || args[3..].iter().any(|s| s.contains(test.name)) {
test_repo(cargo, out_dir, test);
}
}
}
fn test_repo(cargo: &Path, out_dir: &Path, test: &Test) {
println!("testing {}", test.repo);
let dir = clone_repo(test, out_dir);
if let Some(lockfile) = test.lock {
fs::write(&dir.join("Cargo.lock"), lockfile).unwrap();
}
if !run_cargo_test(cargo, &dir, test.packages, test.features, test.manifest_path, test.filters)
{
panic!("tests failed for {}", test.repo);
}
}
fn clone_repo(test: &Test, out_dir: &Path) -> PathBuf {
let out_dir = out_dir.join(test.name);
if !out_dir.join(".git").is_dir() {
let status = Command::new("git").arg("init").arg(&out_dir).status().unwrap();
assert!(status.success());
}
// Try progressively deeper fetch depths to find the commit
let mut found = false;
for depth in &[0, 1, 10, 100, 1000, 100000] {
if *depth > 0 {
let status = Command::new("git")
.arg("fetch")
.arg(test.repo)
.arg(test.sha)
.arg(&format!("--depth={}", depth))
.current_dir(&out_dir)
.status()
.unwrap();
assert!(status.success());
}
let status = Command::new("git")
.arg("reset")
.arg(test.sha)
.arg("--hard")
.current_dir(&out_dir)
.status()
.unwrap();
if status.success() {
found = true;
break;
}
}
if !found {
panic!("unable to find commit {}", test.sha)
}
let status =
Command::new("git").arg("clean").arg("-fdx").current_dir(&out_dir).status().unwrap();
assert!(status.success());
out_dir
}
fn run_cargo_test(
cargo_path: &Path,
crate_path: &Path,
packages: &[&str],
features: Option<&[&str]>,
manifest_path: Option<&str>,
filters: &[&str],
) -> bool {
let mut command = Command::new(cargo_path);
command.arg("test");
if let Some(path) = manifest_path {
command.arg(format!("--manifest-path={}", path));
}
if let Some(features) = features {
command.arg("--no-default-features");
for feature in features {
command.arg(format!("--features={}", feature));
}
}
for name in packages {
command.arg("-p").arg(name);
}
command.arg("--");
command.args(filters);
let status = command
// `xsv` locates binaries relative to `current_exe()`
// which assumes Cargo legacy build-dir layout.
//
// See failure logs:
// https://triage.rust-lang.org/gha-logs/rust-lang/rust/90658568103
//
// FIXME(weihanglo): replace xsv to something else with similar portfolio.
.env("__CARGO_TEMPORARY_BUILD_DIR_NEW_LAYOUT_OPT_OUT", "1")
// Disable rust-lang/cargo's cross-compile tests
.env("CFG_DISABLE_CROSS_TESTS", "1")
// Relax #![deny(warnings)] in some crates
.env("RUSTFLAGS", "--cap-lints warn")
.current_dir(crate_path)
.status()
.unwrap();
status.success()
}