use std::env; use std::fs; use std::path::{Path, PathBuf}; use std::process::Command; 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: "servo", repo: "https://github.com/servo/servo", sha: "caac107ae8145ef2fd20365e2b8fadaf09c2eb3b", lock: None, // Only test Stylo a.k.a. Quantum CSS, the parts of Servo going into Firefox. // This takes much less time to build than all of Servo and supports stable Rust. packages: &["selectors"], features: None, manifest_path: None, filters: &[], }, Test { name: "diesel", repo: "https://github.com/diesel-rs/diesel", sha: "91493fe47175076f330ce5fc518f0196c0476f56", 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::>(); 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("master") .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 // 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() }