diff options
Diffstat (limited to '.run/src')
| -rw-r--r-- | .run/src/bin/cov-test.rs | 61 | ||||
| -rw-r--r-- | .run/src/bin/test-examples.rs | 69 |
2 files changed, 90 insertions, 40 deletions
diff --git a/.run/src/bin/cov-test.rs b/.run/src/bin/cov-test.rs index 1b2342e..ff2c3fc 100644 --- a/.run/src/bin/cov-test.rs +++ b/.run/src/bin/cov-test.rs @@ -18,7 +18,6 @@ //! cargo install --git https://github.com/Weicao-CatilGrass/cargo-llvm-cov cargo-llvm-cov //! ``` -use std::collections::HashMap; use std::fs; use std::path::{Path, PathBuf}; @@ -34,15 +33,16 @@ const OUTPUT_DIR: &str = "docs/cov-test"; /// in one place so the final `report` can merge everything. const COV_TARGET_DIR: &str = ".temp/cov-llvm"; -/// Parsed `examples/test-examples.toml` (`test.<example> = [ { command, expect } ]`). +/// An example's `test.toml` (`[[runs]]` entries). #[derive(Deserialize)] struct TestConfig { - test: HashMap<String, Vec<TestCase>>, + runs: Vec<TestCase>, } +/// One `[[runs]]` entry of an example's `test.toml`. #[derive(Deserialize)] struct TestCase { - command: String, + input: Vec<String>, } fn main() { @@ -108,7 +108,7 @@ fn main() { } // 3. Examples: build each example with explicit RUSTFLAGS, then execute - // every command from test-examples.toml directly. + // every command declared in the example's test.toml directly. // // NOTE: `cargo llvm-cov run` cannot be used here. Its rustc wrapper // only instruments the crates of the *current* cargo project (with @@ -127,7 +127,7 @@ fn main() { } let examples = load_example_commands(&repo_root); let mut built = std::collections::HashSet::new(); - for (example, command) in &examples { + for (example, input) in &examples { if built.insert(example.clone()) { println_cargo_style!("Building: {}", example); run_cmd!(format!( @@ -150,7 +150,7 @@ fn main() { example ); match std::process::Command::new(&binary) - .args(command.split_whitespace()) + .args(input) .env("LLVM_PROFILE_FILE", &profraw) .status() { @@ -279,22 +279,43 @@ fn find_test_crate_manifests(repo_root: &Path) -> Vec<PathBuf> { manifests } -/// Parse `examples/test-examples.toml` into `(example_name, command)` pairs. -fn load_example_commands(repo_root: &Path) -> Vec<(String, String)> { - let content = - fs::read_to_string(repo_root.join("examples/test-examples.toml")).unwrap_or_else(|e| { - eprintln_cargo_style!("Failed to read examples/test-examples.toml: {}", e); +/// Parse every `examples/<name>/test.toml` into `(example_name, input)` pairs. +fn load_example_commands(repo_root: &Path) -> Vec<(String, Vec<String>)> { + let examples_dir = repo_root.join("examples"); + let mut entries: Vec<_> = std::fs::read_dir(&examples_dir) + .unwrap_or_else(|e| { + eprintln_cargo_style!("Failed to read {}: {}", examples_dir.display(), e); std::process::exit(1); - }); - let config: TestConfig = toml::from_str(&content).unwrap_or_else(|e| { - eprintln_cargo_style!("Failed to parse examples/test-examples.toml: {}", e); - std::process::exit(1); - }); + }) + .flatten() + .collect(); + entries.sort_by_key(|e| e.file_name()); let mut pairs = Vec::new(); - for (example, cases) in &config.test { - for case in cases { - pairs.push((example.clone(), case.command.clone())); + for entry in entries { + let path = entry.path(); + if !path.is_dir() { + continue; + } + let test_toml = path.join("test.toml"); + if !test_toml.is_file() { + continue; + } + let name = path + .file_name() + .and_then(|n| n.to_str()) + .unwrap_or_default() + .to_string(); + let content = fs::read_to_string(&test_toml).unwrap_or_else(|e| { + eprintln_cargo_style!("Failed to read {}: {}", test_toml.display(), e); + std::process::exit(1); + }); + let config: TestConfig = toml::from_str(&content).unwrap_or_else(|e| { + eprintln_cargo_style!("Failed to parse {}: {}", test_toml.display(), e); + std::process::exit(1); + }); + for case in config.runs { + pairs.push((name.clone(), case.input)); } } pairs diff --git a/.run/src/bin/test-examples.rs b/.run/src/bin/test-examples.rs index 539459e..55976ef 100644 --- a/.run/src/bin/test-examples.rs +++ b/.run/src/bin/test-examples.rs @@ -1,18 +1,20 @@ -use std::collections::HashMap; +use std::path::Path; use colored::Colorize; use indicatif::ProgressBar; use serde::Deserialize; use tools::{eprintln_cargo_style, println_cargo_style}; +/// An example's `test.toml` (`[[runs]]` entries). #[derive(Deserialize)] struct TestConfig { - test: HashMap<String, Vec<TestCase>>, + runs: Vec<TestCase>, } +/// A single `[[runs]]` entry of an example's `test.toml`. #[derive(Deserialize)] struct TestCase { - command: String, + input: Vec<String>, expect: Expect, } @@ -27,10 +29,10 @@ fn main() { #[cfg(windows)] let _ = colored::control::set_virtual_terminal(true); - let config = load_config(); + let configs = load_all_test_configs(); // Count total test cases upfront - let total: usize = config.test.values().map(|cases| cases.len()).sum(); + let total: usize = configs.iter().map(|(_, cases)| cases.len()).sum(); let bar = ProgressBar::new(total as u64); bar.set_style( indicatif::ProgressStyle::default_bar() @@ -43,7 +45,7 @@ fn main() { ); bar.set_message("examples"); - let passed = run_all_tests(&config, &bar); + let passed = run_all_tests(&configs, &bar); bar.finish_and_clear(); @@ -55,24 +57,51 @@ fn main() { } } -/// Parse test config from TOML file -fn load_config() -> TestConfig { - let content = std::fs::read_to_string("examples/test-examples.toml").unwrap_or_else(|e| { - eprintln_cargo_style!("Failed to read TOML config file: {}", e); +/// Load `examples/<name>/test.toml` for every example that has one, in +/// alphabetical order of the example directory name. +fn load_all_test_configs() -> Vec<(String, Vec<TestCase>)> { + let examples_dir = Path::new("examples"); + let mut configs = Vec::new(); + + let entries = std::fs::read_dir(examples_dir).unwrap_or_else(|e| { + eprintln_cargo_style!("Failed to read examples dir: {}", e); std::process::exit(1); }); - toml::from_str(&content).unwrap_or_else(|e| { - eprintln_cargo_style!("Failed to parse TOML config: {}", e); - std::process::exit(1); - }) + for entry in entries.flatten() { + let path = entry.path(); + if !path.is_dir() { + continue; + } + let test_toml = path.join("test.toml"); + if !test_toml.is_file() { + continue; + } + let name = path + .file_name() + .and_then(|n| n.to_str()) + .unwrap_or_default() + .to_string(); + let content = std::fs::read_to_string(&test_toml).unwrap_or_else(|e| { + eprintln_cargo_style!("Failed to read {}: {}", test_toml.display(), e); + std::process::exit(1); + }); + let config: TestConfig = toml::from_str(&content).unwrap_or_else(|e| { + eprintln_cargo_style!("Failed to parse {}: {}", test_toml.display(), e); + std::process::exit(1); + }); + configs.push((name, config.runs)); + } + + configs.sort_by(|a, b| a.0.cmp(&b.0)); + configs } /// Run all example test groups, return number passed -fn run_all_tests(config: &TestConfig, bar: &ProgressBar) -> usize { +fn run_all_tests(configs: &[(String, Vec<TestCase>)], bar: &ProgressBar) -> usize { let mut passed = 0; - for (example_name, test_cases) in &config.test { + for (example_name, test_cases) in configs { bar.set_message(example_name.clone()); if !build_example(example_name) { @@ -103,15 +132,15 @@ fn build_example(example_name: &str) -> bool { /// Run a single test case, return true on pass fn run_single_test(example_name: &str, test_case: &TestCase, bar: &ProgressBar) -> bool { let binary_path = format!(".temp/target/debug/{}", get_binary_name(example_name)); - let args: Vec<&str> = test_case.command.split_whitespace().collect(); + let command = test_case.input.join(" "); let output = match std::process::Command::new(&binary_path) - .args(&args) + .args(&test_case.input) .output() { Ok(o) => o, Err(e) => { - bar.println(format!("'{}' - failed to run: {}", test_case.command, e)); + bar.println(format!("'{command}' - failed to run: {e}")); return false; } }; @@ -127,7 +156,7 @@ fn run_single_test(example_name: &str, test_case: &TestCase, bar: &ProgressBar) if exit_ok && result_ok { true } else { - bar.println(format!("failed: '{}'", test_case.command)); + bar.println(format!("failed: '{command}'")); if !exit_ok { bar.println(format!( " Expected exit code: {}, actual: {}", |
