Move freshness test to bootstrap
This commit is contained in:
parent
432c4a80e9
commit
f414afbf9f
9 changed files with 396 additions and 316 deletions
|
|
@ -56,6 +56,7 @@ dependencies = [
|
|||
"sha2",
|
||||
"sysinfo",
|
||||
"tar",
|
||||
"tempfile",
|
||||
"termcolor",
|
||||
"toml",
|
||||
"tracing",
|
||||
|
|
@ -233,6 +234,12 @@ dependencies = [
|
|||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "fd-lock"
|
||||
version = "4.0.2"
|
||||
|
|
@ -240,7 +247,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"rustix",
|
||||
"rustix 0.38.40",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
|
|
@ -266,6 +273,18 @@ dependencies = [
|
|||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "globset"
|
||||
version = "0.4.15"
|
||||
|
|
@ -355,6 +374,12 @@ version = "0.4.14"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.22"
|
||||
|
|
@ -486,6 +511,12 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "5.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.7"
|
||||
|
|
@ -548,10 +579,23 @@ dependencies = [
|
|||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"linux-raw-sys 0.4.14",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7178faa4b75a30e269c71e61c353ce2748cf3d76f0c44c393f4e60abf49b825"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys 0.9.3",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.18"
|
||||
|
|
@ -678,6 +722,19 @@ dependencies = [
|
|||
"xattr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "488960f40a3fd53d72c2a29a58722561dee8afdd175bd88e3db4677d7b2ba600"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"rustix 1.0.2",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.4.1"
|
||||
|
|
@ -824,6 +881,15 @@ dependencies = [
|
|||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.14.2+wasi-0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
|
||||
dependencies = [
|
||||
"wit-bindgen-rt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
|
|
@ -990,6 +1056,15 @@ version = "0.52.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.39.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xattr"
|
||||
version = "1.3.1"
|
||||
|
|
@ -997,8 +1072,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"rustix",
|
||||
"linux-raw-sys 0.4.14",
|
||||
"rustix 0.38.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ features = [
|
|||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "1.4"
|
||||
tempfile = "3.15.0"
|
||||
|
||||
# We care a lot about bootstrap's compile times, so don't include debuginfo for
|
||||
# dependencies, only bootstrap itself.
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use std::path::{Path, PathBuf};
|
|||
use std::{env, fs};
|
||||
|
||||
use build_helper::ci::CiEnv;
|
||||
use build_helper::git::PathFreshness;
|
||||
use clap::CommandFactory;
|
||||
use serde::Deserialize;
|
||||
|
||||
|
|
@ -15,6 +16,7 @@ use crate::core::build_steps::clippy::{LintConfig, get_clippy_rules_in_order};
|
|||
use crate::core::build_steps::llvm;
|
||||
use crate::core::build_steps::llvm::LLVM_INVALIDATION_PATHS;
|
||||
use crate::core::config::{LldMode, Target, TargetSelection, TomlConfig};
|
||||
use crate::utils::tests::git::git_test;
|
||||
|
||||
pub(crate) fn parse(config: &str) -> Config {
|
||||
Config::parse_inner(
|
||||
|
|
@ -744,3 +746,171 @@ fn test_include_precedence_over_profile() {
|
|||
// override profile settings, so we expect this to be "dev" here.
|
||||
assert_eq!(config.channel, "dev");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pr_ci_unchanged_anywhere() {
|
||||
git_test(|ctx| {
|
||||
let sha = ctx.create_upstream_merge(&["a"]);
|
||||
ctx.create_nonupstream_merge(&["b"]);
|
||||
let src = ctx.check_modifications(&["c"], CiEnv::GitHubActions);
|
||||
assert_eq!(src, PathFreshness::LastModifiedUpstream { upstream: sha });
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pr_ci_changed_in_pr() {
|
||||
git_test(|ctx| {
|
||||
let sha = ctx.create_upstream_merge(&["a"]);
|
||||
ctx.create_nonupstream_merge(&["b"]);
|
||||
let src = ctx.check_modifications(&["b"], CiEnv::GitHubActions);
|
||||
assert_eq!(src, PathFreshness::HasLocalModifications { upstream: sha });
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_auto_ci_unchanged_anywhere_select_parent() {
|
||||
git_test(|ctx| {
|
||||
let sha = ctx.create_upstream_merge(&["a"]);
|
||||
ctx.create_upstream_merge(&["b"]);
|
||||
let src = ctx.check_modifications(&["c"], CiEnv::GitHubActions);
|
||||
assert_eq!(src, PathFreshness::LastModifiedUpstream { upstream: sha });
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_auto_ci_changed_in_pr() {
|
||||
git_test(|ctx| {
|
||||
let sha = ctx.create_upstream_merge(&["a"]);
|
||||
ctx.create_upstream_merge(&["b", "c"]);
|
||||
let src = ctx.check_modifications(&["c", "d"], CiEnv::GitHubActions);
|
||||
assert_eq!(src, PathFreshness::HasLocalModifications { upstream: sha });
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_local_uncommitted_modifications() {
|
||||
git_test(|ctx| {
|
||||
let sha = ctx.create_upstream_merge(&["a"]);
|
||||
ctx.create_branch("feature");
|
||||
ctx.modify("a");
|
||||
|
||||
assert_eq!(
|
||||
ctx.check_modifications(&["a", "d"], CiEnv::None),
|
||||
PathFreshness::HasLocalModifications { upstream: sha }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_local_committed_modifications() {
|
||||
git_test(|ctx| {
|
||||
let sha = ctx.create_upstream_merge(&["a"]);
|
||||
ctx.create_upstream_merge(&["b", "c"]);
|
||||
ctx.create_branch("feature");
|
||||
ctx.modify("x");
|
||||
ctx.commit();
|
||||
ctx.modify("a");
|
||||
ctx.commit();
|
||||
|
||||
assert_eq!(
|
||||
ctx.check_modifications(&["a", "d"], CiEnv::None),
|
||||
PathFreshness::HasLocalModifications { upstream: sha }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_local_committed_modifications_subdirectory() {
|
||||
git_test(|ctx| {
|
||||
let sha = ctx.create_upstream_merge(&["a/b/c"]);
|
||||
ctx.create_upstream_merge(&["b", "c"]);
|
||||
ctx.create_branch("feature");
|
||||
ctx.modify("a/b/d");
|
||||
ctx.commit();
|
||||
|
||||
assert_eq!(
|
||||
ctx.check_modifications(&["a/b"], CiEnv::None),
|
||||
PathFreshness::HasLocalModifications { upstream: sha }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_local_changes_in_head_upstream() {
|
||||
git_test(|ctx| {
|
||||
// We want to resolve to the upstream commit that made modifications to a,
|
||||
// even if it is currently HEAD
|
||||
let sha = ctx.create_upstream_merge(&["a"]);
|
||||
assert_eq!(
|
||||
ctx.check_modifications(&["a", "d"], CiEnv::None),
|
||||
PathFreshness::LastModifiedUpstream { upstream: sha }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_local_changes_in_previous_upstream() {
|
||||
git_test(|ctx| {
|
||||
// We want to resolve to this commit, which modified a
|
||||
let sha = ctx.create_upstream_merge(&["a", "e"]);
|
||||
// Not to this commit, which is the latest upstream commit
|
||||
ctx.create_upstream_merge(&["b", "c"]);
|
||||
ctx.create_branch("feature");
|
||||
ctx.modify("d");
|
||||
ctx.commit();
|
||||
assert_eq!(
|
||||
ctx.check_modifications(&["a"], CiEnv::None),
|
||||
PathFreshness::LastModifiedUpstream { upstream: sha }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_local_no_upstream_commit_with_changes() {
|
||||
git_test(|ctx| {
|
||||
ctx.create_upstream_merge(&["a", "e"]);
|
||||
ctx.create_upstream_merge(&["a", "e"]);
|
||||
// We want to fall back to this commit, because there are no commits
|
||||
// that modified `x`.
|
||||
let sha = ctx.create_upstream_merge(&["a", "e"]);
|
||||
ctx.create_branch("feature");
|
||||
ctx.modify("d");
|
||||
ctx.commit();
|
||||
assert_eq!(
|
||||
ctx.check_modifications(&["x"], CiEnv::None),
|
||||
PathFreshness::LastModifiedUpstream { upstream: sha }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_local_no_upstream_commit() {
|
||||
git_test(|ctx| {
|
||||
let src = ctx.check_modifications(&["c", "d"], CiEnv::None);
|
||||
assert_eq!(src, PathFreshness::MissingUpstream);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_local_changes_negative_path() {
|
||||
git_test(|ctx| {
|
||||
let upstream = ctx.create_upstream_merge(&["a"]);
|
||||
ctx.create_branch("feature");
|
||||
ctx.modify("b");
|
||||
ctx.modify("d");
|
||||
ctx.commit();
|
||||
|
||||
assert_eq!(
|
||||
ctx.check_modifications(&[":!b", ":!d"], CiEnv::None),
|
||||
PathFreshness::LastModifiedUpstream { upstream: upstream.clone() }
|
||||
);
|
||||
assert_eq!(
|
||||
ctx.check_modifications(&[":!c"], CiEnv::None),
|
||||
PathFreshness::HasLocalModifications { upstream: upstream.clone() }
|
||||
);
|
||||
assert_eq!(
|
||||
ctx.check_modifications(&[":!d", ":!x"], CiEnv::None),
|
||||
PathFreshness::HasLocalModifications { upstream }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,4 +20,4 @@ pub(crate) mod tracing;
|
|||
pub(crate) mod metrics;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
pub(crate) mod tests;
|
||||
|
|
|
|||
143
src/bootstrap/src/utils/tests/git.rs
Normal file
143
src/bootstrap/src/utils/tests/git.rs
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
use std::ffi::OsStr;
|
||||
use std::fs::OpenOptions;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
|
||||
use build_helper::ci::CiEnv;
|
||||
use build_helper::git::{GitConfig, PathFreshness, check_path_modifications};
|
||||
|
||||
pub struct GitCtx {
|
||||
dir: tempfile::TempDir,
|
||||
pub git_repo: String,
|
||||
pub nightly_branch: String,
|
||||
pub merge_bot_email: String,
|
||||
}
|
||||
|
||||
impl GitCtx {
|
||||
fn new() -> Self {
|
||||
let dir = tempfile::TempDir::new().unwrap();
|
||||
let ctx = Self {
|
||||
dir,
|
||||
git_repo: "rust-lang/rust".to_string(),
|
||||
nightly_branch: "nightly".to_string(),
|
||||
merge_bot_email: "Merge bot <merge-bot@rust-lang.org>".to_string(),
|
||||
};
|
||||
ctx.run_git(&["init"]);
|
||||
ctx.run_git(&["config", "user.name", "Tester"]);
|
||||
ctx.run_git(&["config", "user.email", "tester@rust-lang.org"]);
|
||||
ctx.modify("README.md");
|
||||
ctx.commit();
|
||||
ctx.run_git(&["branch", "-m", "main"]);
|
||||
ctx
|
||||
}
|
||||
|
||||
pub fn get_path(&self) -> &Path {
|
||||
self.dir.path()
|
||||
}
|
||||
|
||||
pub fn check_modifications(&self, target_paths: &[&str], ci_env: CiEnv) -> PathFreshness {
|
||||
check_path_modifications(Some(self.dir.path()), &self.git_config(), target_paths, ci_env)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn create_upstream_merge(&self, modified_files: &[&str]) -> String {
|
||||
self.create_branch_and_merge("previous-pr", modified_files, &self.merge_bot_email)
|
||||
}
|
||||
|
||||
pub fn create_nonupstream_merge(&self, modified_files: &[&str]) -> String {
|
||||
self.create_branch_and_merge("pr", modified_files, "Tester <tester@rust-lang.org>")
|
||||
}
|
||||
|
||||
pub fn create_branch_and_merge(
|
||||
&self,
|
||||
branch: &str,
|
||||
modified_files: &[&str],
|
||||
author: &str,
|
||||
) -> String {
|
||||
self.create_branch(branch);
|
||||
for file in modified_files {
|
||||
self.modify(file);
|
||||
}
|
||||
self.commit();
|
||||
self.switch_to_branch("main");
|
||||
self.merge(branch, author);
|
||||
self.run_git(&["branch", "-d", branch]);
|
||||
self.get_current_commit()
|
||||
}
|
||||
|
||||
pub fn get_current_commit(&self) -> String {
|
||||
self.run_git(&["rev-parse", "HEAD"])
|
||||
}
|
||||
|
||||
pub fn merge(&self, branch: &str, author: &str) {
|
||||
self.run_git(&["merge", "--no-commit", "--no-ff", branch]);
|
||||
self.run_git(&[
|
||||
"commit".to_string(),
|
||||
"-m".to_string(),
|
||||
"Merge of {branch}".to_string(),
|
||||
"--author".to_string(),
|
||||
author.to_string(),
|
||||
]);
|
||||
}
|
||||
|
||||
pub fn modify(&self, path: &str) {
|
||||
use std::io::Write;
|
||||
|
||||
let path = self.dir.path().join(path);
|
||||
std::fs::create_dir_all(&path.parent().unwrap()).unwrap();
|
||||
|
||||
let mut file = OpenOptions::new().create(true).append(true).open(path).unwrap();
|
||||
writeln!(file, "line").unwrap();
|
||||
}
|
||||
|
||||
pub fn commit(&self) -> String {
|
||||
self.run_git(&["add", "."]);
|
||||
self.run_git(&["commit", "-m", "commit message"]);
|
||||
self.get_current_commit()
|
||||
}
|
||||
|
||||
pub fn switch_to_branch(&self, name: &str) {
|
||||
self.run_git(&["switch", name]);
|
||||
}
|
||||
|
||||
/// Creates a branch and switches to it.
|
||||
pub fn create_branch(&self, name: &str) {
|
||||
self.run_git(&["checkout", "-b", name]);
|
||||
}
|
||||
|
||||
pub fn run_git<S: AsRef<OsStr>>(&self, args: &[S]) -> String {
|
||||
let mut cmd = self.git_cmd();
|
||||
cmd.args(args);
|
||||
eprintln!("Running {cmd:?}");
|
||||
let output = cmd.output().unwrap();
|
||||
let stdout = String::from_utf8(output.stdout).unwrap().trim().to_string();
|
||||
let stderr = String::from_utf8(output.stderr).unwrap().trim().to_string();
|
||||
if !output.status.success() {
|
||||
panic!("Git command `{cmd:?}` failed\nStdout\n{stdout}\nStderr\n{stderr}");
|
||||
}
|
||||
stdout
|
||||
}
|
||||
|
||||
fn git_cmd(&self) -> Command {
|
||||
let mut cmd = Command::new("git");
|
||||
cmd.current_dir(&self.dir);
|
||||
cmd
|
||||
}
|
||||
|
||||
fn git_config(&self) -> GitConfig<'_> {
|
||||
GitConfig {
|
||||
git_repository: &self.git_repo,
|
||||
nightly_branch: &self.nightly_branch,
|
||||
git_merge_commit_email: &self.merge_bot_email,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Run an end-to-end test that allows testing git logic.
|
||||
pub fn git_test<F>(test_fn: F)
|
||||
where
|
||||
F: FnOnce(&mut GitCtx),
|
||||
{
|
||||
let mut ctx = GitCtx::new();
|
||||
test_fn(&mut ctx);
|
||||
}
|
||||
|
|
@ -1 +1,2 @@
|
|||
pub mod git;
|
||||
mod shared_helpers_tests;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,3 @@ edition = "2021"
|
|||
[dependencies]
|
||||
serde = "1"
|
||||
serde_derive = "1"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.19"
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use std::path::Path;
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
use crate::ci::CiEnv;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GitConfig<'a> {
|
||||
pub git_repository: &'a str,
|
||||
pub nightly_branch: &'a str,
|
||||
|
|
|
|||
|
|
@ -1,305 +0,0 @@
|
|||
use std::ffi::OsStr;
|
||||
use std::fs::OpenOptions;
|
||||
use std::process::Command;
|
||||
|
||||
use crate::ci::CiEnv;
|
||||
use crate::git::{GitConfig, PathFreshness, check_path_modifications};
|
||||
|
||||
#[test]
|
||||
fn test_pr_ci_unchanged_anywhere() {
|
||||
git_test(|ctx| {
|
||||
let sha = ctx.create_upstream_merge(&["a"]);
|
||||
ctx.create_nonupstream_merge(&["b"]);
|
||||
let src = ctx.check_modifications(&["c"], CiEnv::GitHubActions);
|
||||
assert_eq!(src, PathFreshness::LastModifiedUpstream { upstream: sha });
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pr_ci_changed_in_pr() {
|
||||
git_test(|ctx| {
|
||||
let sha = ctx.create_upstream_merge(&["a"]);
|
||||
ctx.create_nonupstream_merge(&["b"]);
|
||||
let src = ctx.check_modifications(&["b"], CiEnv::GitHubActions);
|
||||
assert_eq!(src, PathFreshness::HasLocalModifications { upstream: sha });
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_auto_ci_unchanged_anywhere_select_parent() {
|
||||
git_test(|ctx| {
|
||||
let sha = ctx.create_upstream_merge(&["a"]);
|
||||
ctx.create_upstream_merge(&["b"]);
|
||||
let src = ctx.check_modifications(&["c"], CiEnv::GitHubActions);
|
||||
assert_eq!(src, PathFreshness::LastModifiedUpstream { upstream: sha });
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_auto_ci_changed_in_pr() {
|
||||
git_test(|ctx| {
|
||||
let sha = ctx.create_upstream_merge(&["a"]);
|
||||
ctx.create_upstream_merge(&["b", "c"]);
|
||||
let src = ctx.check_modifications(&["c", "d"], CiEnv::GitHubActions);
|
||||
assert_eq!(src, PathFreshness::HasLocalModifications { upstream: sha });
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_local_uncommitted_modifications() {
|
||||
git_test(|ctx| {
|
||||
let sha = ctx.create_upstream_merge(&["a"]);
|
||||
ctx.create_branch("feature");
|
||||
ctx.modify("a");
|
||||
|
||||
assert_eq!(
|
||||
ctx.check_modifications(&["a", "d"], CiEnv::None),
|
||||
PathFreshness::HasLocalModifications { upstream: sha }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_local_committed_modifications() {
|
||||
git_test(|ctx| {
|
||||
let sha = ctx.create_upstream_merge(&["a"]);
|
||||
ctx.create_upstream_merge(&["b", "c"]);
|
||||
ctx.create_branch("feature");
|
||||
ctx.modify("x");
|
||||
ctx.commit();
|
||||
ctx.modify("a");
|
||||
ctx.commit();
|
||||
|
||||
assert_eq!(
|
||||
ctx.check_modifications(&["a", "d"], CiEnv::None),
|
||||
PathFreshness::HasLocalModifications { upstream: sha }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_local_committed_modifications_subdirectory() {
|
||||
git_test(|ctx| {
|
||||
let sha = ctx.create_upstream_merge(&["a/b/c"]);
|
||||
ctx.create_upstream_merge(&["b", "c"]);
|
||||
ctx.create_branch("feature");
|
||||
ctx.modify("a/b/d");
|
||||
ctx.commit();
|
||||
|
||||
assert_eq!(
|
||||
ctx.check_modifications(&["a/b"], CiEnv::None),
|
||||
PathFreshness::HasLocalModifications { upstream: sha }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_local_changes_in_head_upstream() {
|
||||
git_test(|ctx| {
|
||||
// We want to resolve to the upstream commit that made modifications to a,
|
||||
// even if it is currently HEAD
|
||||
let sha = ctx.create_upstream_merge(&["a"]);
|
||||
assert_eq!(
|
||||
ctx.check_modifications(&["a", "d"], CiEnv::None),
|
||||
PathFreshness::LastModifiedUpstream { upstream: sha }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_local_changes_in_previous_upstream() {
|
||||
git_test(|ctx| {
|
||||
// We want to resolve to this commit, which modified a
|
||||
let sha = ctx.create_upstream_merge(&["a", "e"]);
|
||||
// Not to this commit, which is the latest upstream commit
|
||||
ctx.create_upstream_merge(&["b", "c"]);
|
||||
ctx.create_branch("feature");
|
||||
ctx.modify("d");
|
||||
ctx.commit();
|
||||
assert_eq!(
|
||||
ctx.check_modifications(&["a"], CiEnv::None),
|
||||
PathFreshness::LastModifiedUpstream { upstream: sha }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_local_no_upstream_commit_with_changes() {
|
||||
git_test(|ctx| {
|
||||
ctx.create_upstream_merge(&["a", "e"]);
|
||||
ctx.create_upstream_merge(&["a", "e"]);
|
||||
// We want to fall back to this commit, because there are no commits
|
||||
// that modified `x`.
|
||||
let sha = ctx.create_upstream_merge(&["a", "e"]);
|
||||
ctx.create_branch("feature");
|
||||
ctx.modify("d");
|
||||
ctx.commit();
|
||||
assert_eq!(
|
||||
ctx.check_modifications(&["x"], CiEnv::None),
|
||||
PathFreshness::LastModifiedUpstream { upstream: sha }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_local_no_upstream_commit() {
|
||||
git_test(|ctx| {
|
||||
let src = ctx.check_modifications(&["c", "d"], CiEnv::None);
|
||||
assert_eq!(src, PathFreshness::MissingUpstream);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_local_changes_negative_path() {
|
||||
git_test(|ctx| {
|
||||
let upstream = ctx.create_upstream_merge(&["a"]);
|
||||
ctx.create_branch("feature");
|
||||
ctx.modify("b");
|
||||
ctx.modify("d");
|
||||
ctx.commit();
|
||||
|
||||
assert_eq!(
|
||||
ctx.check_modifications(&[":!b", ":!d"], CiEnv::None),
|
||||
PathFreshness::LastModifiedUpstream { upstream: upstream.clone() }
|
||||
);
|
||||
assert_eq!(
|
||||
ctx.check_modifications(&[":!c"], CiEnv::None),
|
||||
PathFreshness::HasLocalModifications { upstream: upstream.clone() }
|
||||
);
|
||||
assert_eq!(
|
||||
ctx.check_modifications(&[":!d", ":!x"], CiEnv::None),
|
||||
PathFreshness::HasLocalModifications { upstream }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
struct GitCtx {
|
||||
dir: tempfile::TempDir,
|
||||
git_repo: String,
|
||||
nightly_branch: String,
|
||||
merge_bot_email: String,
|
||||
}
|
||||
|
||||
impl GitCtx {
|
||||
fn new() -> Self {
|
||||
let dir = tempfile::TempDir::new().unwrap();
|
||||
let ctx = Self {
|
||||
dir,
|
||||
git_repo: "rust-lang/rust".to_string(),
|
||||
nightly_branch: "nightly".to_string(),
|
||||
merge_bot_email: "Merge bot <merge-bot@rust-lang.org>".to_string(),
|
||||
};
|
||||
ctx.run_git(&["init"]);
|
||||
ctx.run_git(&["config", "user.name", "Tester"]);
|
||||
ctx.run_git(&["config", "user.email", "tester@rust-lang.org"]);
|
||||
ctx.modify("README.md");
|
||||
ctx.commit();
|
||||
ctx.run_git(&["branch", "-m", "main"]);
|
||||
ctx
|
||||
}
|
||||
|
||||
fn check_modifications(&self, target_paths: &[&str], ci_env: CiEnv) -> PathFreshness {
|
||||
check_path_modifications(Some(self.dir.path()), &self.git_config(), target_paths, ci_env)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn create_upstream_merge(&self, modified_files: &[&str]) -> String {
|
||||
self.create_branch_and_merge("previous-pr", modified_files, &self.merge_bot_email)
|
||||
}
|
||||
|
||||
fn create_nonupstream_merge(&self, modified_files: &[&str]) -> String {
|
||||
self.create_branch_and_merge("pr", modified_files, "Tester <tester@rust-lang.org>")
|
||||
}
|
||||
|
||||
fn create_branch_and_merge(
|
||||
&self,
|
||||
branch: &str,
|
||||
modified_files: &[&str],
|
||||
author: &str,
|
||||
) -> String {
|
||||
self.create_branch(branch);
|
||||
for file in modified_files {
|
||||
self.modify(file);
|
||||
}
|
||||
self.commit();
|
||||
self.switch_to_branch("main");
|
||||
self.merge(branch, author);
|
||||
self.run_git(&["branch", "-d", branch]);
|
||||
self.get_current_commit()
|
||||
}
|
||||
|
||||
fn get_current_commit(&self) -> String {
|
||||
self.run_git(&["rev-parse", "HEAD"])
|
||||
}
|
||||
|
||||
fn merge(&self, branch: &str, author: &str) {
|
||||
self.run_git(&["merge", "--no-commit", "--no-ff", branch]);
|
||||
self.run_git(&[
|
||||
"commit".to_string(),
|
||||
"-m".to_string(),
|
||||
"Merge of {branch}".to_string(),
|
||||
"--author".to_string(),
|
||||
author.to_string(),
|
||||
]);
|
||||
}
|
||||
|
||||
fn modify(&self, path: &str) {
|
||||
use std::io::Write;
|
||||
|
||||
let path = self.dir.path().join(path);
|
||||
std::fs::create_dir_all(&path.parent().unwrap()).unwrap();
|
||||
|
||||
let mut file = OpenOptions::new().create(true).append(true).open(path).unwrap();
|
||||
writeln!(file, "line").unwrap();
|
||||
}
|
||||
|
||||
fn commit(&self) -> String {
|
||||
self.run_git(&["add", "."]);
|
||||
self.run_git(&["commit", "-m", "commit message"]);
|
||||
self.get_current_commit()
|
||||
}
|
||||
|
||||
fn switch_to_branch(&self, name: &str) {
|
||||
self.run_git(&["switch", name]);
|
||||
}
|
||||
|
||||
/// Creates a branch and switches to it.
|
||||
fn create_branch(&self, name: &str) {
|
||||
self.run_git(&["checkout", "-b", name]);
|
||||
}
|
||||
|
||||
fn run_git<S: AsRef<OsStr>>(&self, args: &[S]) -> String {
|
||||
let mut cmd = self.git_cmd();
|
||||
cmd.args(args);
|
||||
eprintln!("Running {cmd:?}");
|
||||
let output = cmd.output().unwrap();
|
||||
let stdout = String::from_utf8(output.stdout).unwrap().trim().to_string();
|
||||
let stderr = String::from_utf8(output.stderr).unwrap().trim().to_string();
|
||||
if !output.status.success() {
|
||||
panic!("Git command `{cmd:?}` failed\nStdout\n{stdout}\nStderr\n{stderr}");
|
||||
}
|
||||
stdout
|
||||
}
|
||||
|
||||
fn git_cmd(&self) -> Command {
|
||||
let mut cmd = Command::new("git");
|
||||
cmd.current_dir(&self.dir);
|
||||
cmd
|
||||
}
|
||||
|
||||
fn git_config(&self) -> GitConfig<'_> {
|
||||
GitConfig {
|
||||
git_repository: &self.git_repo,
|
||||
nightly_branch: &self.nightly_branch,
|
||||
git_merge_commit_email: &self.merge_bot_email,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn git_test<F>(test_fn: F)
|
||||
where
|
||||
F: FnOnce(&GitCtx),
|
||||
{
|
||||
let ctx = GitCtx::new();
|
||||
test_fn(&ctx);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue