Skip to content

Commit

Permalink
Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Kobzol committed Feb 17, 2025
1 parent 77bd646 commit 8083fd4
Show file tree
Hide file tree
Showing 5 changed files with 324 additions and 39 deletions.
70 changes: 70 additions & 0 deletions src/ci/citool/Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"clap",
"insta",
"serde",
"serde_json",
"serde_yaml",
Expand Down Expand Up @@ -115,6 +116,24 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"

[[package]]
name = "console"
version = "0.15.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea3c6ecd8059b57859df5c69830340ed3c41d30e3da0c1cbed90a96ac853041b"
dependencies = [
"encode_unicode",
"libc",
"once_cell",
"windows-sys",
]

[[package]]
name = "encode_unicode"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"

[[package]]
name = "equivalent"
version = "1.0.1"
Expand Down Expand Up @@ -143,6 +162,19 @@ dependencies = [
"hashbrown",
]

[[package]]
name = "insta"
version = "1.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71c1b125e30d93896b365e156c33dadfffab45ee8400afcbba4752f59de08a86"
dependencies = [
"console",
"linked-hash-map",
"once_cell",
"pin-project",
"similar",
]

[[package]]
name = "is_terminal_polyfill"
version = "1.70.1"
Expand All @@ -155,6 +187,18 @@ version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"

[[package]]
name = "libc"
version = "0.2.169"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"

[[package]]
name = "linked-hash-map"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"

[[package]]
name = "memchr"
version = "2.7.4"
Expand All @@ -167,6 +211,26 @@ version = "1.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"

[[package]]
name = "pin-project"
version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfe2e71e1471fe07709406bf725f710b02927c9c54b2b5b2ec0e8087d97c327d"
dependencies = [
"pin-project-internal",
]

[[package]]
name = "pin-project-internal"
version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6e859e6e5bd50440ab63c47e3ebabc90f26251f7c73c3d3e837b74a1cc3fa67"
dependencies = [
"proc-macro2",
"quote",
"syn",
]

[[package]]
name = "proc-macro2"
version = "1.0.93"
Expand Down Expand Up @@ -236,6 +300,12 @@ dependencies = [
"unsafe-libyaml",
]

[[package]]
name = "similar"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"

[[package]]
name = "strsim"
version = "0.11.1"
Expand Down
3 changes: 3 additions & 0 deletions src/ci/citool/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ serde = { version = "1", features = ["derive"] }
serde_yaml = "0.9"
serde_json = "1"

[dev-dependencies]
insta = "1"

# Tell cargo that citool is its own workspace.
# If this is omitted, cargo will look for a workspace elsewhere.
# We want to avoid this, since citool is independent of the other crates.
Expand Down
81 changes: 42 additions & 39 deletions src/ci/citool/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::collections::HashMap;
use std::path::Path;
use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use std::process::Command;

use anyhow::Context;
Expand All @@ -17,13 +17,13 @@ struct Job {
name: String,
/// GitHub runner on which the job should be executed
os: String,
env: HashMap<String, Value>,
env: BTreeMap<String, Value>,
/// Should the job be only executed on a specific channel?
#[serde(default)]
only_on_channel: Option<String>,
/// Rest of attributes that will be passed through to GitHub actions
#[serde(flatten)]
extra_keys: HashMap<String, Value>,
extra_keys: BTreeMap<String, Value>,
}

impl Job {
Expand All @@ -44,11 +44,11 @@ impl Job {
#[derive(serde::Deserialize, Debug)]
struct JobEnvironments {
#[serde(rename = "pr")]
pr_env: HashMap<String, Value>,
pr_env: BTreeMap<String, Value>,
#[serde(rename = "try")]
try_env: HashMap<String, Value>,
try_env: BTreeMap<String, Value>,
#[serde(rename = "auto")]
auto_env: HashMap<String, Value>,
auto_env: BTreeMap<String, Value>,
}

#[derive(serde::Deserialize, Debug)]
Expand All @@ -71,7 +71,7 @@ impl JobDatabase {
}

fn load_job_db(path: &Path) -> anyhow::Result<JobDatabase> {
let db = std::fs::read_to_string(path)?;
let db = read_to_string(path)?;
let mut db: Value = serde_yaml::from_str(&db)?;

// We need to expand merge keys (<<), because serde_yaml can't deal with them
Expand All @@ -92,9 +92,9 @@ struct GithubActionsJob {
/// prefix (PR/try/auto).
full_name: String,
os: String,
env: HashMap<String, String>,
env: BTreeMap<String, String>,
#[serde(flatten)]
extra_keys: HashMap<String, serde_json::Value>,
extra_keys: BTreeMap<String, serde_json::Value>,
}

/// Type of workflow that is being executed on CI
Expand All @@ -116,27 +116,17 @@ struct GitHubContext {

impl GitHubContext {
fn get_run_type(&self) -> Option<RunType> {
if self.event_name == "pull_request" {
return Some(RunType::PullRequest);
} else if self.event_name == "push" {
let is_try_build =
["refs/heads/try", "refs/heads/try-perf", "refs/heads/automation/bors/try"]
.iter()
.any(|r| **r == self.branch_ref);
// Unrolled branch from a rollup for testing perf
// This should **not** allow custom try jobs
let is_unrolled_perf_build = self.branch_ref == "refs/heads/try-perf";
if is_try_build {
let custom_jobs =
if !is_unrolled_perf_build { Some(self.get_custom_jobs()) } else { None };
return Some(RunType::TryJob { custom_jobs });
}

if self.branch_ref == "refs/heads/auto" {
return Some(RunType::AutoJob);
match (self.event_name.as_str(), self.branch_ref.as_str()) {
("pull_request", _) => Some(RunType::PullRequest),
("push", "refs/heads/try-perf") => Some(RunType::TryJob { custom_jobs: None }),
("push", "refs/heads/try" | "refs/heads/automation/bors/try") => {
let custom_jobs = self.get_custom_jobs();
let custom_jobs = if !custom_jobs.is_empty() { Some(custom_jobs) } else { None };
Some(RunType::TryJob { custom_jobs })
}
("push", "refs/heads/auto") => Some(RunType::AutoJob),
_ => None,
}
None
}

/// Tries to parse names of specific CI jobs that should be executed in the form of
Expand Down Expand Up @@ -175,7 +165,7 @@ fn skip_jobs(jobs: Vec<Job>, channel: &str) -> Vec<Job> {
.collect()
}

fn to_string_map(map: &HashMap<String, Value>) -> HashMap<String, String> {
fn to_string_map(map: &BTreeMap<String, Value>) -> BTreeMap<String, String> {
map.iter()
.map(|(key, value)| {
(
Expand Down Expand Up @@ -232,7 +222,7 @@ fn calculate_jobs(
let jobs = jobs
.into_iter()
.map(|job| {
let mut env: HashMap<String, String> = to_string_map(base_env);
let mut env: BTreeMap<String, String> = to_string_map(base_env);
env.extend(to_string_map(&job.env));
let full_name = format!("{prefix} - {}", job.name);

Expand Down Expand Up @@ -312,9 +302,9 @@ fn run_workflow_locally(db: JobDatabase, job_type: JobType, name: String) -> any
JobType::Auto => &db.auto_jobs,
JobType::PR => &db.pr_jobs,
};
let job = find_linux_job(&jobs, &name).with_context(|| format!("Cannot find job {name}"))?;
let job = find_linux_job(jobs, &name).with_context(|| format!("Cannot find job {name}"))?;

let mut custom_env: HashMap<String, String> = HashMap::new();
let mut custom_env: BTreeMap<String, String> = BTreeMap::new();
// Replicate src/ci/scripts/setup-environment.sh
// Adds custom environment variables to the job
if name.starts_with("dist-") {
Expand All @@ -340,7 +330,10 @@ fn run_workflow_locally(db: JobDatabase, job_type: JobType, name: String) -> any
enum Args {
/// Calculate a list of jobs that should be executed on CI.
/// Should only be used on CI inside GitHub actions.
CalculateJobMatrix,
CalculateJobMatrix {
#[clap(long)]
jobs_file: Option<PathBuf>,
},
/// Execute a given CI job locally.
#[clap(name = "run-local")]
RunJobLocally {
Expand All @@ -362,19 +355,29 @@ enum JobType {

fn main() -> anyhow::Result<()> {
let args = Args::parse();
let db = load_job_db(Path::new(JOBS_YML_PATH)).context("Cannot load jobs.yml")?;
let default_jobs_file = Path::new(JOBS_YML_PATH);
let load_db = |jobs_path| load_job_db(jobs_path).context("Cannot load jobs.yml");

match args {
Args::CalculateJobMatrix => {
Args::CalculateJobMatrix { jobs_file } => {
let jobs_path = jobs_file.as_deref().unwrap_or(default_jobs_file);
let gh_ctx = load_github_ctx()
.context("Cannot load environment variables from GitHub Actions")?;
let channel = std::fs::read_to_string(Path::new(CI_DIRECTORY).join("channel"))
let channel = read_to_string(Path::new(CI_DIRECTORY).join("channel"))
.context("Cannot read channel file")?;

calculate_job_matrix(db, gh_ctx, &channel).context("Failed to calculate job matrix")?;
calculate_job_matrix(load_db(jobs_path)?, gh_ctx, &channel)
.context("Failed to calculate job matrix")?;
}
Args::RunJobLocally { job_type, name } => {
run_workflow_locally(load_db(default_jobs_file)?, job_type, name)?
}
Args::RunJobLocally { job_type, name } => run_workflow_locally(db, job_type, name)?,
}

Ok(())
}

fn read_to_string<P: AsRef<Path>>(path: P) -> anyhow::Result<String> {
let error = format!("Cannot read file {:?}", path.as_ref());
std::fs::read_to_string(path).context(error)
}
64 changes: 64 additions & 0 deletions src/ci/citool/tests/jobs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use std::process::{Command, Stdio};

const TEST_JOBS_YML_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/test-jobs.yml");

#[test]
fn auto_jobs() {
let stdout = get_matrix("push", "commit", "refs/heads/auto");
insta::assert_snapshot!(stdout, @r#"
jobs=[{"name":"aarch64-gnu","full_name":"auto - aarch64-gnu","os":"ubuntu-22.04-arm","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","TOOLSTATE_PUBLISH":1},"free_disk":true},{"name":"x86_64-gnu-llvm-18-1","full_name":"auto - x86_64-gnu-llvm-18-1","os":"ubuntu-24.04","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","DOCKER_SCRIPT":"stage_2_test_set1.sh","IMAGE":"x86_64-gnu-llvm-18","READ_ONLY_SRC":"0","RUST_BACKTRACE":1,"TOOLSTATE_PUBLISH":1},"free_disk":true},{"name":"aarch64-apple","full_name":"auto - aarch64-apple","os":"macos-14","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","MACOSX_DEPLOYMENT_TARGET":11.0,"MACOSX_STD_DEPLOYMENT_TARGET":11.0,"NO_DEBUG_ASSERTIONS":1,"NO_LLVM_ASSERTIONS":1,"NO_OVERFLOW_CHECKS":1,"RUSTC_RETRY_LINKER_ON_SEGFAULT":1,"RUST_CONFIGURE_ARGS":"--enable-sanitizers --enable-profiler --set rust.jemalloc","SCRIPT":"./x.py --stage 2 test --host=aarch64-apple-darwin --target=aarch64-apple-darwin","SELECT_XCODE":"/Applications/Xcode_15.4.app","TOOLSTATE_PUBLISH":1,"USE_XCODE_CLANG":1}},{"name":"dist-i686-msvc","full_name":"auto - dist-i686-msvc","os":"windows-2022","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","CODEGEN_BACKENDS":"llvm,cranelift","DEPLOY_BUCKET":"rust-lang-ci2","DIST_REQUIRE_ALL_TOOLS":1,"RUST_CONFIGURE_ARGS":"--build=i686-pc-windows-msvc --host=i686-pc-windows-msvc --target=i686-pc-windows-msvc,i586-pc-windows-msvc --enable-full-tools --enable-profiler","SCRIPT":"python x.py dist bootstrap --include-default-paths","TOOLSTATE_PUBLISH":1}}]
run_type=auto
"#);
}

#[test]
fn try_jobs() {
let stdout = get_matrix("push", "commit", "refs/heads/try");
insta::assert_snapshot!(stdout, @r#"
jobs=[{"name":"dist-x86_64-linux","full_name":"try - dist-x86_64-linux","os":"ubuntu-22.04-16core-64gb","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","CODEGEN_BACKENDS":"llvm,cranelift","DEPLOY_BUCKET":"rust-lang-ci2","DIST_TRY_BUILD":1,"TOOLSTATE_PUBLISH":1}}]
run_type=try
"#);
}

#[test]
fn try_custom_jobs() {
let stdout = get_matrix(
"push",
r#"This is a test PR
try-job: aarch64-gnu
try-job: dist-i686-msvc"#,
"refs/heads/try",
);
insta::assert_snapshot!(stdout, @r#"
jobs=[{"name":"aarch64-gnu","full_name":"try - aarch64-gnu","os":"ubuntu-22.04-arm","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","DIST_TRY_BUILD":1,"TOOLSTATE_PUBLISH":1},"free_disk":true},{"name":"dist-i686-msvc","full_name":"try - dist-i686-msvc","os":"windows-2022","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","CODEGEN_BACKENDS":"llvm,cranelift","DEPLOY_BUCKET":"rust-lang-ci2","DIST_REQUIRE_ALL_TOOLS":1,"DIST_TRY_BUILD":1,"RUST_CONFIGURE_ARGS":"--build=i686-pc-windows-msvc --host=i686-pc-windows-msvc --target=i686-pc-windows-msvc,i586-pc-windows-msvc --enable-full-tools --enable-profiler","SCRIPT":"python x.py dist bootstrap --include-default-paths","TOOLSTATE_PUBLISH":1}}]
run_type=try
"#);
}

#[test]
fn pr_jobs() {
let stdout = get_matrix("pull_request", "commit", "refs/heads/pr/1234");
insta::assert_snapshot!(stdout, @r#"
jobs=[{"name":"mingw-check","full_name":"PR - mingw-check","os":"ubuntu-24.04","env":{"PR_CI_JOB":1},"free_disk":true},{"name":"mingw-check-tidy","full_name":"PR - mingw-check-tidy","os":"ubuntu-24.04","env":{"PR_CI_JOB":1},"continue_on_error":true,"free_disk":true}]
run_type=pr
"#);
}

fn get_matrix(event_name: &str, commit_msg: &str, branch_ref: &str) -> String {
let output = Command::new("cargo")
.args(["run", "-q", "calculate-job-matrix", "--jobs-file", TEST_JOBS_YML_PATH])
.env("GITHUB_EVENT_NAME", event_name)
.env("COMMIT_MESSAGE", commit_msg)
.env("GITHUB_REF", branch_ref)
.stdout(Stdio::piped())
.output()
.expect("Failed to execute command");

let stdout = String::from_utf8(output.stdout).unwrap();
let stderr = String::from_utf8(output.stderr).unwrap();
if !output.status.success() {
panic!("cargo run failed: {}\n{}", stdout, stderr);
}
stdout
}
Loading

0 comments on commit 8083fd4

Please sign in to comment.