diff options
| author | Jakub Beránek <berykubik@gmail.com> | 2025-03-19 10:36:48 +0100 |
|---|---|---|
| committer | Jakub Beránek <berykubik@gmail.com> | 2025-04-20 09:35:42 +0200 |
| commit | f414afbf9f6928038fbf00c8a08ded93269f12d3 (patch) | |
| tree | bd04ee2c7faf68db1f496f7a012f393f3347a509 | |
| parent | 432c4a80e9b03505f73e85828bd152a5efc2f98b (diff) | |
| download | rust-f414afbf9f6928038fbf00c8a08ded93269f12d3.tar.gz rust-f414afbf9f6928038fbf00c8a08ded93269f12d3.zip | |
Move freshness test to bootstrap
| -rw-r--r-- | src/bootstrap/Cargo.lock | 83 | ||||
| -rw-r--r-- | src/bootstrap/Cargo.toml | 1 | ||||
| -rw-r--r-- | src/bootstrap/src/core/config/tests.rs | 170 | ||||
| -rw-r--r-- | src/bootstrap/src/utils/mod.rs | 2 | ||||
| -rw-r--r-- | src/bootstrap/src/utils/tests/git.rs | 143 | ||||
| -rw-r--r-- | src/bootstrap/src/utils/tests/mod.rs | 1 | ||||
| -rw-r--r-- | src/build_helper/Cargo.toml | 3 | ||||
| -rw-r--r-- | src/build_helper/src/git.rs | 4 | ||||
| -rw-r--r-- | src/build_helper/src/git/tests.rs | 305 |
9 files changed, 396 insertions, 316 deletions
diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index beb9ea93989..d415668f54a 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -56,6 +56,7 @@ dependencies = [ "sha2", "sysinfo", "tar", + "tempfile", "termcolor", "toml", "tracing", @@ -234,13 +235,19 @@ dependencies = [ ] [[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" 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", ] @@ -267,6 +274,18 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -356,6 +375,12 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -487,6 +512,12 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -548,11 +579,24 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -679,6 +723,19 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -825,6 +882,15 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -991,14 +1057,23 @@ 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" 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]] diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index 23aa87a7407..712a9b04c1a 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -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. diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index affabd49720..04e328c8b1b 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -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 } + ); + }); +} diff --git a/src/bootstrap/src/utils/mod.rs b/src/bootstrap/src/utils/mod.rs index caef8ce3088..169fcec303e 100644 --- a/src/bootstrap/src/utils/mod.rs +++ b/src/bootstrap/src/utils/mod.rs @@ -20,4 +20,4 @@ pub(crate) mod tracing; pub(crate) mod metrics; #[cfg(test)] -mod tests; +pub(crate) mod tests; diff --git a/src/bootstrap/src/utils/tests/git.rs b/src/bootstrap/src/utils/tests/git.rs new file mode 100644 index 00000000000..59581dd9c37 --- /dev/null +++ b/src/bootstrap/src/utils/tests/git.rs @@ -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); +} diff --git a/src/bootstrap/src/utils/tests/mod.rs b/src/bootstrap/src/utils/tests/mod.rs index 0791f7a6e20..73d55db994c 100644 --- a/src/bootstrap/src/utils/tests/mod.rs +++ b/src/bootstrap/src/utils/tests/mod.rs @@ -1 +1,2 @@ +pub mod git; mod shared_helpers_tests; diff --git a/src/build_helper/Cargo.toml b/src/build_helper/Cargo.toml index bdead0a36de..66894e1abc4 100644 --- a/src/build_helper/Cargo.toml +++ b/src/build_helper/Cargo.toml @@ -8,6 +8,3 @@ edition = "2021" [dependencies] serde = "1" serde_derive = "1" - -[dev-dependencies] -tempfile = "3.19" diff --git a/src/build_helper/src/git.rs b/src/build_helper/src/git.rs index e40e5db3028..8b019c5929a 100644 --- a/src/build_helper/src/git.rs +++ b/src/build_helper/src/git.rs @@ -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, diff --git a/src/build_helper/src/git/tests.rs b/src/build_helper/src/git/tests.rs deleted file mode 100644 index 43bffb8befa..00000000000 --- a/src/build_helper/src/git/tests.rs +++ /dev/null @@ -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); -} |
