1
0
Fork 0
mirror of https://github.com/imjasonh/testscript-rs synced 2026-07-09 17:46:52 +00:00
testscript-rs/src/lib.rs

456 lines
15 KiB
Rust
Raw Normal View History

//! # testscript-rs
//!
//! A Rust crate for testing command-line tools using filesystem-based script files,
//! mirroring the functionality of Go's `rogpeppe/go-internal/testscript`.
//!
//! This crate provides a framework for writing integration tests for CLI tools
//! using `.txtar` format files that contain both test scripts and file contents.
pub mod error;
pub mod parser;
pub mod run;
pub use error::{Error, Result};
pub use parser::{Command, Script, TxtarFile};
pub use run::{CommandFn, RunParams, SetupFn, TestEnvironment};
// Re-export for advanced users who need direct access
pub use run::run_test;
// Internal function used by the Builder - not part of public API
fn run(params: &mut RunParams, test_data_glob: &str) -> Result<()> {
use walkdir::WalkDir;
let mut test_files = Vec::new();
// If specific files are provided, use them directly
if let Some(ref files) = params.files {
// Parse the base directory from the glob pattern
let base_dir = if let Some(slash_pos) = test_data_glob.rfind('/') {
&test_data_glob[..slash_pos]
} else {
"."
};
for file in files {
let file_path = if file.starts_with('/') {
// Absolute path - use as-is
std::path::PathBuf::from(file)
} else if file.contains('/') {
// Relative path - resolve relative to base directory
std::path::PathBuf::from(base_dir).join(file)
} else {
// Just a filename - look in the base directory
std::path::PathBuf::from(base_dir).join(file)
};
// Validate that the file exists
if !file_path.exists() {
return Err(Error::Generic(format!(
"Test file not found: {}",
file_path.display()
)));
}
if !file_path.is_file() {
return Err(Error::Generic(format!(
"Path is not a file: {}",
file_path.display()
)));
}
test_files.push(file_path);
}
} else {
// Use the original glob-based discovery
let (base_dir, pattern) = if let Some(slash_pos) = test_data_glob.rfind('/') {
let base_dir = &test_data_glob[..slash_pos];
let pattern = &test_data_glob[slash_pos + 1..];
(base_dir, pattern)
} else {
(".", test_data_glob)
};
// Convert glob pattern to a simple matcher
let pattern_regex = pattern.replace("*", ".*");
let regex = regex::Regex::new(&format!("^{}$", pattern_regex))?;
// Walk the directory and collect matching files
for entry in WalkDir::new(base_dir).min_depth(1).max_depth(1) {
let entry = entry?;
if entry.file_type().is_file() {
if let Some(file_name) = entry.file_name().to_str() {
if regex.is_match(file_name) {
test_files.push(entry.path().to_path_buf());
}
}
}
}
}
// Sort test files for consistent execution order
test_files.sort();
if test_files.is_empty() {
if params.files.is_some() {
return Err(Error::Generic("No test files specified".to_string()));
} else {
return Err(Error::Generic(format!(
"No test files found matching pattern: {}",
test_data_glob
)));
}
}
// Run each test file
for test_file in test_files {
run::run_script(&test_file, params)
.map_err(|e| Error::Generic(format!("Test '{}' failed: {}", test_file.display(), e)))?;
}
Ok(())
}
/// Builder for configuring and running testscript tests
///
/// This provides a fluent interface for setting up and executing test scripts.
///
Add network and advanced condition support with automatic detection and CI optimization (#21) ## Add Network and Advanced Condition Support with Automatic Detection - COMPLETE ✅ This PR implements comprehensive network and advanced condition support for testscript-rs, bringing feature parity with Go's testscript package with automatic detection like the original. ### Core Features Implemented ✨ #### 1. **Network Conditions (`[net]`) - Always Available & CI-Optimized** - Auto-detects network availability with CI-optimized approach: - **TCP-first detection**: Tries TCP connection to DNS servers (faster than ping) - **Ping fallback**: Uses ping with shorter timeouts if TCP fails - Cross-platform implementation optimized for CI environments - Available by default like Go testscript - no manual setup required - Can be used in test scripts as `[net]` for network-required tests or `[!net]` to skip when offline #### 2. **Environment Variable Conditions (`[env:VAR]`)** - Dynamic condition checking for environment variables at execution time - No pre-registration required - conditions are evaluated when encountered - Supports negation: `[!env:MISSING_VAR]` to test when variables are not set - Perfect for CI-specific tests: `[env:CI] exec deploy --dry-run` #### 3. **Optimized Program Detection - Built-in Like Go** - **15+ essential programs detected automatically** including: - **Basic commands**: cat, echo, ls, mkdir, rm, cp, mv, grep, find - **Core development tools**: git, make, curl, python, node, docker - **System tools**: sleep, true, false, sh - Cross-platform program existence checking (uses `where` on Windows, `which` on Unix) - Optimized for CI performance with reduced startup time - No manual configuration needed - works exactly like Go testscript ### API Simplification **Simple, Go-like API** - everything detected automatically: ```rust testscript::run("testdata") .execute() // Network + 15+ programs detected automatically .unwrap(); ``` **Manual conditions still supported** for custom scenarios: ```rust testscript::run("testdata") .condition("custom", my_custom_check()) .execute() .unwrap(); ``` ### CI Optimization - **Network detection**: TCP-first approach with 500ms timeouts (vs 1000ms+ ping) - **Program list**: Optimized from 35+ to 15+ essential programs for faster startup - **Better reliability**: Handles restricted CI network environments - **Ubuntu CI compatibility**: Resolved timeout and network access issues ### Real-World Use Cases This enables powerful testing scenarios that work out of the box: ``` # Network-dependent API tests (automatic detection, CI-optimized) [net] exec curl https://api.example.com/health [net] stdout "OK" # Tool-specific tests (automatic detection) [exec:docker] exec docker run --rm hello-world [exec:docker] stdout "Hello from Docker!" [exec:git] exec git status [exec:python] exec python --version # Environment-based behavior [env:CI] exec deploy --dry-run --verbose [!env:PRODUCTION] exec run-dangerous-test # Platform + environment combinations [unix] [env:DISPLAY] exec gui-test --headless [windows] [exec:powershell] exec Get-Process ``` ### Implementation Details - **Network detection**: Multi-approach detection (TCP + ping fallback) optimized for CI reliability - **Program detection**: Essential program list covering most common development tools - **Dynamic evaluation**: Environment conditions are checked at execution time, not initialization - **Error handling**: Clear error messages for unknown conditions - **Cross-platform**: Works consistently across Windows, macOS, and Linux with CI optimizations ### Go Testscript Compatibility ✅ This implementation now matches Go testscript's behavior: - All common conditions available by default without setup - Network condition automatically detected with CI optimization - Essential program detection built-in with performance focus - Clean, simple API that "just works" - Environment variable conditions work dynamically - CI-friendly performance characteristics ### Testing Added comprehensive test coverage with 7 new test cases covering: - Network condition detection (automatic, CI-optimized) - Environment variable condition parsing and evaluation - Optimized program detection (automatic) - Condition combination scenarios - Cross-platform compatibility - Helper function validation All existing tests continue to pass (47 total), ensuring no regressions. ### Backward Compatibility This is a purely additive change. All existing functionality is preserved: - Platform conditions (`[unix]`, `[windows]`, `[linux]`, `[darwin]`) - Build type conditions (`[debug]`, `[release]`) - Manual `.condition()` API still works for custom conditions - All existing test scripts continue to work unchanged Fixes imjasonh/testscript-rs#10 <!-- START COPILOT CODING AGENT SUFFIX --> <details> <summary>Original prompt</summary> > > ---- > > *This section details on the original issue you should resolve* > > <issue_title>Add network and advanced condition support</issue_title> > <issue_description>## Feature Request: Advanced Condition Support > > Go's testscript supports advanced conditions beyond platform detection: > > ## Missing Conditions > > 1. **Network conditions**: `[net]` - Test network availability > 2. **Advanced platform conditions**: More granular OS/arch detection > 3. **Environment-based conditions**: Custom environment variable conditions > > ## Proposed API > > ```rust > testscript::run("testdata") > .condition("net", check_network_available()) > .condition("docker", command_exists("docker")) > .condition("env:CI", std::env::var("CI").is_ok()) > .execute() > .unwrap(); > ``` > > ## Built-in Condition Helpers > > ```rust > // Automatic network detection > testscript::run("testdata") > .auto_detect_network() > .auto_detect_programs(&["docker", "git", "npm"]) > .execute() > .unwrap(); > ``` > > ## Implementation Notes > > - Network detection could ping a reliable host > - Program detection should use cross-platform approach (not just `which`) > - Environment conditions could support pattern matching > - Consider caching condition results for performance > > ## Use Cases > > - **Docker tests**: `[docker] exec docker run hello-world` > - **Network tests**: `[net] exec curl example.com` > - **CI-specific tests**: `[env:CI] exec deploy --dry-run`</issue_description> > > ## Comments on the Issue (you are @copilot in this section) > > <comments> > </comments> > </details> Fixes imjasonh/testscript-rs#10 <!-- START COPILOT CODING AGENT TIPS --> --- 💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs. --------- Signed-off-by: Jason Hall <jason@chainguard.dev> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: imjasonh <210737+imjasonh@users.noreply.github.com> Co-authored-by: Jason Hall <jason@chainguard.dev>
2025-09-27 17:00:36 +00:00
/// ## Automatic Condition Detection
///
Add network and advanced condition support with automatic detection and CI optimization (#21) ## Add Network and Advanced Condition Support with Automatic Detection - COMPLETE ✅ This PR implements comprehensive network and advanced condition support for testscript-rs, bringing feature parity with Go's testscript package with automatic detection like the original. ### Core Features Implemented ✨ #### 1. **Network Conditions (`[net]`) - Always Available & CI-Optimized** - Auto-detects network availability with CI-optimized approach: - **TCP-first detection**: Tries TCP connection to DNS servers (faster than ping) - **Ping fallback**: Uses ping with shorter timeouts if TCP fails - Cross-platform implementation optimized for CI environments - Available by default like Go testscript - no manual setup required - Can be used in test scripts as `[net]` for network-required tests or `[!net]` to skip when offline #### 2. **Environment Variable Conditions (`[env:VAR]`)** - Dynamic condition checking for environment variables at execution time - No pre-registration required - conditions are evaluated when encountered - Supports negation: `[!env:MISSING_VAR]` to test when variables are not set - Perfect for CI-specific tests: `[env:CI] exec deploy --dry-run` #### 3. **Optimized Program Detection - Built-in Like Go** - **15+ essential programs detected automatically** including: - **Basic commands**: cat, echo, ls, mkdir, rm, cp, mv, grep, find - **Core development tools**: git, make, curl, python, node, docker - **System tools**: sleep, true, false, sh - Cross-platform program existence checking (uses `where` on Windows, `which` on Unix) - Optimized for CI performance with reduced startup time - No manual configuration needed - works exactly like Go testscript ### API Simplification **Simple, Go-like API** - everything detected automatically: ```rust testscript::run("testdata") .execute() // Network + 15+ programs detected automatically .unwrap(); ``` **Manual conditions still supported** for custom scenarios: ```rust testscript::run("testdata") .condition("custom", my_custom_check()) .execute() .unwrap(); ``` ### CI Optimization - **Network detection**: TCP-first approach with 500ms timeouts (vs 1000ms+ ping) - **Program list**: Optimized from 35+ to 15+ essential programs for faster startup - **Better reliability**: Handles restricted CI network environments - **Ubuntu CI compatibility**: Resolved timeout and network access issues ### Real-World Use Cases This enables powerful testing scenarios that work out of the box: ``` # Network-dependent API tests (automatic detection, CI-optimized) [net] exec curl https://api.example.com/health [net] stdout "OK" # Tool-specific tests (automatic detection) [exec:docker] exec docker run --rm hello-world [exec:docker] stdout "Hello from Docker!" [exec:git] exec git status [exec:python] exec python --version # Environment-based behavior [env:CI] exec deploy --dry-run --verbose [!env:PRODUCTION] exec run-dangerous-test # Platform + environment combinations [unix] [env:DISPLAY] exec gui-test --headless [windows] [exec:powershell] exec Get-Process ``` ### Implementation Details - **Network detection**: Multi-approach detection (TCP + ping fallback) optimized for CI reliability - **Program detection**: Essential program list covering most common development tools - **Dynamic evaluation**: Environment conditions are checked at execution time, not initialization - **Error handling**: Clear error messages for unknown conditions - **Cross-platform**: Works consistently across Windows, macOS, and Linux with CI optimizations ### Go Testscript Compatibility ✅ This implementation now matches Go testscript's behavior: - All common conditions available by default without setup - Network condition automatically detected with CI optimization - Essential program detection built-in with performance focus - Clean, simple API that "just works" - Environment variable conditions work dynamically - CI-friendly performance characteristics ### Testing Added comprehensive test coverage with 7 new test cases covering: - Network condition detection (automatic, CI-optimized) - Environment variable condition parsing and evaluation - Optimized program detection (automatic) - Condition combination scenarios - Cross-platform compatibility - Helper function validation All existing tests continue to pass (47 total), ensuring no regressions. ### Backward Compatibility This is a purely additive change. All existing functionality is preserved: - Platform conditions (`[unix]`, `[windows]`, `[linux]`, `[darwin]`) - Build type conditions (`[debug]`, `[release]`) - Manual `.condition()` API still works for custom conditions - All existing test scripts continue to work unchanged Fixes imjasonh/testscript-rs#10 <!-- START COPILOT CODING AGENT SUFFIX --> <details> <summary>Original prompt</summary> > > ---- > > *This section details on the original issue you should resolve* > > <issue_title>Add network and advanced condition support</issue_title> > <issue_description>## Feature Request: Advanced Condition Support > > Go's testscript supports advanced conditions beyond platform detection: > > ## Missing Conditions > > 1. **Network conditions**: `[net]` - Test network availability > 2. **Advanced platform conditions**: More granular OS/arch detection > 3. **Environment-based conditions**: Custom environment variable conditions > > ## Proposed API > > ```rust > testscript::run("testdata") > .condition("net", check_network_available()) > .condition("docker", command_exists("docker")) > .condition("env:CI", std::env::var("CI").is_ok()) > .execute() > .unwrap(); > ``` > > ## Built-in Condition Helpers > > ```rust > // Automatic network detection > testscript::run("testdata") > .auto_detect_network() > .auto_detect_programs(&["docker", "git", "npm"]) > .execute() > .unwrap(); > ``` > > ## Implementation Notes > > - Network detection could ping a reliable host > - Program detection should use cross-platform approach (not just `which`) > - Environment conditions could support pattern matching > - Consider caching condition results for performance > > ## Use Cases > > - **Docker tests**: `[docker] exec docker run hello-world` > - **Network tests**: `[net] exec curl example.com` > - **CI-specific tests**: `[env:CI] exec deploy --dry-run`</issue_description> > > ## Comments on the Issue (you are @copilot in this section) > > <comments> > </comments> > </details> Fixes imjasonh/testscript-rs#10 <!-- START COPILOT CODING AGENT TIPS --> --- 💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs. --------- Signed-off-by: Jason Hall <jason@chainguard.dev> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: imjasonh <210737+imjasonh@users.noreply.github.com> Co-authored-by: Jason Hall <jason@chainguard.dev>
2025-09-27 17:00:36 +00:00
/// The following conditions are automatically available without any setup:
///
/// - **Platform conditions**: `[unix]`, `[windows]`, `[linux]`, `[darwin]`, `[macos]`
/// - **Network condition**: `[net]` - Tests network connectivity by pinging reliable hosts
/// - **Build conditions**: `[debug]`, `[release]` - Based on compilation flags
/// - **Program conditions**: `[exec:program]` - Checks if a program is available in PATH
/// - **Environment conditions**: `[env:VAR]` - Dynamic checking of environment variables
/// - **Program existence**: `[exec:program]` - Checks if a program is available in PATH
/// - **Negation**: Use `!` to negate any condition, e.g. `[!windows]`, `[!env:CI]`, `[!exec:git]`
///
/// ## Examples
///
/// ### Basic Usage
/// ```no_run
/// use testscript_rs::testscript;
///
Add network and advanced condition support with automatic detection and CI optimization (#21) ## Add Network and Advanced Condition Support with Automatic Detection - COMPLETE ✅ This PR implements comprehensive network and advanced condition support for testscript-rs, bringing feature parity with Go's testscript package with automatic detection like the original. ### Core Features Implemented ✨ #### 1. **Network Conditions (`[net]`) - Always Available & CI-Optimized** - Auto-detects network availability with CI-optimized approach: - **TCP-first detection**: Tries TCP connection to DNS servers (faster than ping) - **Ping fallback**: Uses ping with shorter timeouts if TCP fails - Cross-platform implementation optimized for CI environments - Available by default like Go testscript - no manual setup required - Can be used in test scripts as `[net]` for network-required tests or `[!net]` to skip when offline #### 2. **Environment Variable Conditions (`[env:VAR]`)** - Dynamic condition checking for environment variables at execution time - No pre-registration required - conditions are evaluated when encountered - Supports negation: `[!env:MISSING_VAR]` to test when variables are not set - Perfect for CI-specific tests: `[env:CI] exec deploy --dry-run` #### 3. **Optimized Program Detection - Built-in Like Go** - **15+ essential programs detected automatically** including: - **Basic commands**: cat, echo, ls, mkdir, rm, cp, mv, grep, find - **Core development tools**: git, make, curl, python, node, docker - **System tools**: sleep, true, false, sh - Cross-platform program existence checking (uses `where` on Windows, `which` on Unix) - Optimized for CI performance with reduced startup time - No manual configuration needed - works exactly like Go testscript ### API Simplification **Simple, Go-like API** - everything detected automatically: ```rust testscript::run("testdata") .execute() // Network + 15+ programs detected automatically .unwrap(); ``` **Manual conditions still supported** for custom scenarios: ```rust testscript::run("testdata") .condition("custom", my_custom_check()) .execute() .unwrap(); ``` ### CI Optimization - **Network detection**: TCP-first approach with 500ms timeouts (vs 1000ms+ ping) - **Program list**: Optimized from 35+ to 15+ essential programs for faster startup - **Better reliability**: Handles restricted CI network environments - **Ubuntu CI compatibility**: Resolved timeout and network access issues ### Real-World Use Cases This enables powerful testing scenarios that work out of the box: ``` # Network-dependent API tests (automatic detection, CI-optimized) [net] exec curl https://api.example.com/health [net] stdout "OK" # Tool-specific tests (automatic detection) [exec:docker] exec docker run --rm hello-world [exec:docker] stdout "Hello from Docker!" [exec:git] exec git status [exec:python] exec python --version # Environment-based behavior [env:CI] exec deploy --dry-run --verbose [!env:PRODUCTION] exec run-dangerous-test # Platform + environment combinations [unix] [env:DISPLAY] exec gui-test --headless [windows] [exec:powershell] exec Get-Process ``` ### Implementation Details - **Network detection**: Multi-approach detection (TCP + ping fallback) optimized for CI reliability - **Program detection**: Essential program list covering most common development tools - **Dynamic evaluation**: Environment conditions are checked at execution time, not initialization - **Error handling**: Clear error messages for unknown conditions - **Cross-platform**: Works consistently across Windows, macOS, and Linux with CI optimizations ### Go Testscript Compatibility ✅ This implementation now matches Go testscript's behavior: - All common conditions available by default without setup - Network condition automatically detected with CI optimization - Essential program detection built-in with performance focus - Clean, simple API that "just works" - Environment variable conditions work dynamically - CI-friendly performance characteristics ### Testing Added comprehensive test coverage with 7 new test cases covering: - Network condition detection (automatic, CI-optimized) - Environment variable condition parsing and evaluation - Optimized program detection (automatic) - Condition combination scenarios - Cross-platform compatibility - Helper function validation All existing tests continue to pass (47 total), ensuring no regressions. ### Backward Compatibility This is a purely additive change. All existing functionality is preserved: - Platform conditions (`[unix]`, `[windows]`, `[linux]`, `[darwin]`) - Build type conditions (`[debug]`, `[release]`) - Manual `.condition()` API still works for custom conditions - All existing test scripts continue to work unchanged Fixes imjasonh/testscript-rs#10 <!-- START COPILOT CODING AGENT SUFFIX --> <details> <summary>Original prompt</summary> > > ---- > > *This section details on the original issue you should resolve* > > <issue_title>Add network and advanced condition support</issue_title> > <issue_description>## Feature Request: Advanced Condition Support > > Go's testscript supports advanced conditions beyond platform detection: > > ## Missing Conditions > > 1. **Network conditions**: `[net]` - Test network availability > 2. **Advanced platform conditions**: More granular OS/arch detection > 3. **Environment-based conditions**: Custom environment variable conditions > > ## Proposed API > > ```rust > testscript::run("testdata") > .condition("net", check_network_available()) > .condition("docker", command_exists("docker")) > .condition("env:CI", std::env::var("CI").is_ok()) > .execute() > .unwrap(); > ``` > > ## Built-in Condition Helpers > > ```rust > // Automatic network detection > testscript::run("testdata") > .auto_detect_network() > .auto_detect_programs(&["docker", "git", "npm"]) > .execute() > .unwrap(); > ``` > > ## Implementation Notes > > - Network detection could ping a reliable host > - Program detection should use cross-platform approach (not just `which`) > - Environment conditions could support pattern matching > - Consider caching condition results for performance > > ## Use Cases > > - **Docker tests**: `[docker] exec docker run hello-world` > - **Network tests**: `[net] exec curl example.com` > - **CI-specific tests**: `[env:CI] exec deploy --dry-run`</issue_description> > > ## Comments on the Issue (you are @copilot in this section) > > <comments> > </comments> > </details> Fixes imjasonh/testscript-rs#10 <!-- START COPILOT CODING AGENT TIPS --> --- 💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs. --------- Signed-off-by: Jason Hall <jason@chainguard.dev> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: imjasonh <210737+imjasonh@users.noreply.github.com> Co-authored-by: Jason Hall <jason@chainguard.dev>
2025-09-27 17:00:36 +00:00
/// // Simple usage - all conditions detected automatically
/// testscript::run("testdata").execute().unwrap();
Add network and advanced condition support with automatic detection and CI optimization (#21) ## Add Network and Advanced Condition Support with Automatic Detection - COMPLETE ✅ This PR implements comprehensive network and advanced condition support for testscript-rs, bringing feature parity with Go's testscript package with automatic detection like the original. ### Core Features Implemented ✨ #### 1. **Network Conditions (`[net]`) - Always Available & CI-Optimized** - Auto-detects network availability with CI-optimized approach: - **TCP-first detection**: Tries TCP connection to DNS servers (faster than ping) - **Ping fallback**: Uses ping with shorter timeouts if TCP fails - Cross-platform implementation optimized for CI environments - Available by default like Go testscript - no manual setup required - Can be used in test scripts as `[net]` for network-required tests or `[!net]` to skip when offline #### 2. **Environment Variable Conditions (`[env:VAR]`)** - Dynamic condition checking for environment variables at execution time - No pre-registration required - conditions are evaluated when encountered - Supports negation: `[!env:MISSING_VAR]` to test when variables are not set - Perfect for CI-specific tests: `[env:CI] exec deploy --dry-run` #### 3. **Optimized Program Detection - Built-in Like Go** - **15+ essential programs detected automatically** including: - **Basic commands**: cat, echo, ls, mkdir, rm, cp, mv, grep, find - **Core development tools**: git, make, curl, python, node, docker - **System tools**: sleep, true, false, sh - Cross-platform program existence checking (uses `where` on Windows, `which` on Unix) - Optimized for CI performance with reduced startup time - No manual configuration needed - works exactly like Go testscript ### API Simplification **Simple, Go-like API** - everything detected automatically: ```rust testscript::run("testdata") .execute() // Network + 15+ programs detected automatically .unwrap(); ``` **Manual conditions still supported** for custom scenarios: ```rust testscript::run("testdata") .condition("custom", my_custom_check()) .execute() .unwrap(); ``` ### CI Optimization - **Network detection**: TCP-first approach with 500ms timeouts (vs 1000ms+ ping) - **Program list**: Optimized from 35+ to 15+ essential programs for faster startup - **Better reliability**: Handles restricted CI network environments - **Ubuntu CI compatibility**: Resolved timeout and network access issues ### Real-World Use Cases This enables powerful testing scenarios that work out of the box: ``` # Network-dependent API tests (automatic detection, CI-optimized) [net] exec curl https://api.example.com/health [net] stdout "OK" # Tool-specific tests (automatic detection) [exec:docker] exec docker run --rm hello-world [exec:docker] stdout "Hello from Docker!" [exec:git] exec git status [exec:python] exec python --version # Environment-based behavior [env:CI] exec deploy --dry-run --verbose [!env:PRODUCTION] exec run-dangerous-test # Platform + environment combinations [unix] [env:DISPLAY] exec gui-test --headless [windows] [exec:powershell] exec Get-Process ``` ### Implementation Details - **Network detection**: Multi-approach detection (TCP + ping fallback) optimized for CI reliability - **Program detection**: Essential program list covering most common development tools - **Dynamic evaluation**: Environment conditions are checked at execution time, not initialization - **Error handling**: Clear error messages for unknown conditions - **Cross-platform**: Works consistently across Windows, macOS, and Linux with CI optimizations ### Go Testscript Compatibility ✅ This implementation now matches Go testscript's behavior: - All common conditions available by default without setup - Network condition automatically detected with CI optimization - Essential program detection built-in with performance focus - Clean, simple API that "just works" - Environment variable conditions work dynamically - CI-friendly performance characteristics ### Testing Added comprehensive test coverage with 7 new test cases covering: - Network condition detection (automatic, CI-optimized) - Environment variable condition parsing and evaluation - Optimized program detection (automatic) - Condition combination scenarios - Cross-platform compatibility - Helper function validation All existing tests continue to pass (47 total), ensuring no regressions. ### Backward Compatibility This is a purely additive change. All existing functionality is preserved: - Platform conditions (`[unix]`, `[windows]`, `[linux]`, `[darwin]`) - Build type conditions (`[debug]`, `[release]`) - Manual `.condition()` API still works for custom conditions - All existing test scripts continue to work unchanged Fixes imjasonh/testscript-rs#10 <!-- START COPILOT CODING AGENT SUFFIX --> <details> <summary>Original prompt</summary> > > ---- > > *This section details on the original issue you should resolve* > > <issue_title>Add network and advanced condition support</issue_title> > <issue_description>## Feature Request: Advanced Condition Support > > Go's testscript supports advanced conditions beyond platform detection: > > ## Missing Conditions > > 1. **Network conditions**: `[net]` - Test network availability > 2. **Advanced platform conditions**: More granular OS/arch detection > 3. **Environment-based conditions**: Custom environment variable conditions > > ## Proposed API > > ```rust > testscript::run("testdata") > .condition("net", check_network_available()) > .condition("docker", command_exists("docker")) > .condition("env:CI", std::env::var("CI").is_ok()) > .execute() > .unwrap(); > ``` > > ## Built-in Condition Helpers > > ```rust > // Automatic network detection > testscript::run("testdata") > .auto_detect_network() > .auto_detect_programs(&["docker", "git", "npm"]) > .execute() > .unwrap(); > ``` > > ## Implementation Notes > > - Network detection could ping a reliable host > - Program detection should use cross-platform approach (not just `which`) > - Environment conditions could support pattern matching > - Consider caching condition results for performance > > ## Use Cases > > - **Docker tests**: `[docker] exec docker run hello-world` > - **Network tests**: `[net] exec curl example.com` > - **CI-specific tests**: `[env:CI] exec deploy --dry-run`</issue_description> > > ## Comments on the Issue (you are @copilot in this section) > > <comments> > </comments> > </details> Fixes imjasonh/testscript-rs#10 <!-- START COPILOT CODING AGENT TIPS --> --- 💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs. --------- Signed-off-by: Jason Hall <jason@chainguard.dev> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: imjasonh <210737+imjasonh@users.noreply.github.com> Co-authored-by: Jason Hall <jason@chainguard.dev>
2025-09-27 17:00:36 +00:00
/// ```
///
/// ### Running Specific Test Files
/// ```no_run
/// use testscript_rs::testscript;
///
/// // Run only specific test files instead of all .txt files
/// testscript::run("testdata")
/// .files(["hello.txt", "exists.txt"])
/// .execute()
/// .unwrap();
/// ```
///
Add TestWork functionality to preserve working directories on failure (#24) - [x] Analyze current codebase and understand test execution flow - [x] Add preserve_work_on_failure field to RunParams struct - [x] Add preserve_work_on_failure() method to Builder - [x] Modify TestEnvironment to support preserving work directories - [x] Update run_script_impl to handle work directory preservation on failure - [x] Add comprehensive tests to validate the new functionality - [x] Update documentation with usage examples - [x] Update README with TestWork section showing debugging capabilities - [x] Fix clippy warnings for code quality - [x] Validate functionality works correctly in both debug and release modes - [x] Fix formatting issues that caused CI failure - [x] Replace std::mem::forget() with idiomatic TempDir::keep() method ## Improved Code Quality ✅ Replaced the non-idiomatic use of `std::mem::forget()` with the proper `TempDir::keep()` method for preserving temporary directories. This approach: - Is more explicit about the intent to preserve the directory - Is the recommended way according to tempfile crate documentation - Removes the need for manual memory management workarounds - Makes the code more readable and maintainable All tests continue to pass and functionality remains unchanged. <!-- START COPILOT CODING AGENT SUFFIX --> <details> <summary>Original prompt</summary> > > ---- > > *This section details on the original issue you should resolve* > > <issue_title>Add TestWork functionality to preserve working directories</issue_title> > <issue_description>## Feature Request: TestWork Functionality > > Go's testscript supports a `TestWork` parameter that preserves working directories when tests fail, making debugging much easier. > > ## Proposed API > > ```rust > testscript::run("testdata") > .preserve_work_on_failure(true) > .execute() > .unwrap(); > ``` > > ## Implementation > > - When a test fails and this option is enabled, print the work directory path > - Don't clean up the temporary directory on test failure > - Could also support preserving on success for debugging > > ## Benefits > > - **Easier debugging**: Inspect files created during failed tests > - **Development workflow**: Understand what went wrong > - **Compatibility**: Matches Go testscript behavior > > ## Example Output > > ``` > Test failed. Work directory preserved at: /tmp/testscript-work-abc123 > You can inspect the test environment: > cd /tmp/testscript-work-abc123 > ls -la > ```</issue_description> > > ## Comments on the Issue (you are @copilot in this section) > > <comments> > </comments> > </details> Fixes imjasonh/testscript-rs#6 <!-- START COPILOT CODING AGENT TIPS --> --- 💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click [here](https://survey3.medallia.com/?EAHeSx-AP01bZqG0Ld9QLQ) to start the survey. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: imjasonh <210737+imjasonh@users.noreply.github.com> Co-authored-by: Jason Hall <jason@chainguard.dev>
2025-09-27 19:03:10 +00:00
/// ### With Custom Setup and Work Directory Preservation
Add network and advanced condition support with automatic detection and CI optimization (#21) ## Add Network and Advanced Condition Support with Automatic Detection - COMPLETE ✅ This PR implements comprehensive network and advanced condition support for testscript-rs, bringing feature parity with Go's testscript package with automatic detection like the original. ### Core Features Implemented ✨ #### 1. **Network Conditions (`[net]`) - Always Available & CI-Optimized** - Auto-detects network availability with CI-optimized approach: - **TCP-first detection**: Tries TCP connection to DNS servers (faster than ping) - **Ping fallback**: Uses ping with shorter timeouts if TCP fails - Cross-platform implementation optimized for CI environments - Available by default like Go testscript - no manual setup required - Can be used in test scripts as `[net]` for network-required tests or `[!net]` to skip when offline #### 2. **Environment Variable Conditions (`[env:VAR]`)** - Dynamic condition checking for environment variables at execution time - No pre-registration required - conditions are evaluated when encountered - Supports negation: `[!env:MISSING_VAR]` to test when variables are not set - Perfect for CI-specific tests: `[env:CI] exec deploy --dry-run` #### 3. **Optimized Program Detection - Built-in Like Go** - **15+ essential programs detected automatically** including: - **Basic commands**: cat, echo, ls, mkdir, rm, cp, mv, grep, find - **Core development tools**: git, make, curl, python, node, docker - **System tools**: sleep, true, false, sh - Cross-platform program existence checking (uses `where` on Windows, `which` on Unix) - Optimized for CI performance with reduced startup time - No manual configuration needed - works exactly like Go testscript ### API Simplification **Simple, Go-like API** - everything detected automatically: ```rust testscript::run("testdata") .execute() // Network + 15+ programs detected automatically .unwrap(); ``` **Manual conditions still supported** for custom scenarios: ```rust testscript::run("testdata") .condition("custom", my_custom_check()) .execute() .unwrap(); ``` ### CI Optimization - **Network detection**: TCP-first approach with 500ms timeouts (vs 1000ms+ ping) - **Program list**: Optimized from 35+ to 15+ essential programs for faster startup - **Better reliability**: Handles restricted CI network environments - **Ubuntu CI compatibility**: Resolved timeout and network access issues ### Real-World Use Cases This enables powerful testing scenarios that work out of the box: ``` # Network-dependent API tests (automatic detection, CI-optimized) [net] exec curl https://api.example.com/health [net] stdout "OK" # Tool-specific tests (automatic detection) [exec:docker] exec docker run --rm hello-world [exec:docker] stdout "Hello from Docker!" [exec:git] exec git status [exec:python] exec python --version # Environment-based behavior [env:CI] exec deploy --dry-run --verbose [!env:PRODUCTION] exec run-dangerous-test # Platform + environment combinations [unix] [env:DISPLAY] exec gui-test --headless [windows] [exec:powershell] exec Get-Process ``` ### Implementation Details - **Network detection**: Multi-approach detection (TCP + ping fallback) optimized for CI reliability - **Program detection**: Essential program list covering most common development tools - **Dynamic evaluation**: Environment conditions are checked at execution time, not initialization - **Error handling**: Clear error messages for unknown conditions - **Cross-platform**: Works consistently across Windows, macOS, and Linux with CI optimizations ### Go Testscript Compatibility ✅ This implementation now matches Go testscript's behavior: - All common conditions available by default without setup - Network condition automatically detected with CI optimization - Essential program detection built-in with performance focus - Clean, simple API that "just works" - Environment variable conditions work dynamically - CI-friendly performance characteristics ### Testing Added comprehensive test coverage with 7 new test cases covering: - Network condition detection (automatic, CI-optimized) - Environment variable condition parsing and evaluation - Optimized program detection (automatic) - Condition combination scenarios - Cross-platform compatibility - Helper function validation All existing tests continue to pass (47 total), ensuring no regressions. ### Backward Compatibility This is a purely additive change. All existing functionality is preserved: - Platform conditions (`[unix]`, `[windows]`, `[linux]`, `[darwin]`) - Build type conditions (`[debug]`, `[release]`) - Manual `.condition()` API still works for custom conditions - All existing test scripts continue to work unchanged Fixes imjasonh/testscript-rs#10 <!-- START COPILOT CODING AGENT SUFFIX --> <details> <summary>Original prompt</summary> > > ---- > > *This section details on the original issue you should resolve* > > <issue_title>Add network and advanced condition support</issue_title> > <issue_description>## Feature Request: Advanced Condition Support > > Go's testscript supports advanced conditions beyond platform detection: > > ## Missing Conditions > > 1. **Network conditions**: `[net]` - Test network availability > 2. **Advanced platform conditions**: More granular OS/arch detection > 3. **Environment-based conditions**: Custom environment variable conditions > > ## Proposed API > > ```rust > testscript::run("testdata") > .condition("net", check_network_available()) > .condition("docker", command_exists("docker")) > .condition("env:CI", std::env::var("CI").is_ok()) > .execute() > .unwrap(); > ``` > > ## Built-in Condition Helpers > > ```rust > // Automatic network detection > testscript::run("testdata") > .auto_detect_network() > .auto_detect_programs(&["docker", "git", "npm"]) > .execute() > .unwrap(); > ``` > > ## Implementation Notes > > - Network detection could ping a reliable host > - Program detection should use cross-platform approach (not just `which`) > - Environment conditions could support pattern matching > - Consider caching condition results for performance > > ## Use Cases > > - **Docker tests**: `[docker] exec docker run hello-world` > - **Network tests**: `[net] exec curl example.com` > - **CI-specific tests**: `[env:CI] exec deploy --dry-run`</issue_description> > > ## Comments on the Issue (you are @copilot in this section) > > <comments> > </comments> > </details> Fixes imjasonh/testscript-rs#10 <!-- START COPILOT CODING AGENT TIPS --> --- 💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs. --------- Signed-off-by: Jason Hall <jason@chainguard.dev> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: imjasonh <210737+imjasonh@users.noreply.github.com> Co-authored-by: Jason Hall <jason@chainguard.dev>
2025-09-27 17:00:36 +00:00
/// ```no_run
/// use testscript_rs::testscript;
///
/// testscript::run("testdata")
/// .setup(|env| {
/// // Compile your CLI tool
/// std::process::Command::new("cargo")
/// .args(["build", "--bin", "my-cli"])
/// .status()
/// .expect("Failed to build");
/// Ok(())
/// })
/// .command("my-cmd", |_env, _args| {
/// // Custom command implementation
/// Ok(())
/// })
Add network and advanced condition support with automatic detection and CI optimization (#21) ## Add Network and Advanced Condition Support with Automatic Detection - COMPLETE ✅ This PR implements comprehensive network and advanced condition support for testscript-rs, bringing feature parity with Go's testscript package with automatic detection like the original. ### Core Features Implemented ✨ #### 1. **Network Conditions (`[net]`) - Always Available & CI-Optimized** - Auto-detects network availability with CI-optimized approach: - **TCP-first detection**: Tries TCP connection to DNS servers (faster than ping) - **Ping fallback**: Uses ping with shorter timeouts if TCP fails - Cross-platform implementation optimized for CI environments - Available by default like Go testscript - no manual setup required - Can be used in test scripts as `[net]` for network-required tests or `[!net]` to skip when offline #### 2. **Environment Variable Conditions (`[env:VAR]`)** - Dynamic condition checking for environment variables at execution time - No pre-registration required - conditions are evaluated when encountered - Supports negation: `[!env:MISSING_VAR]` to test when variables are not set - Perfect for CI-specific tests: `[env:CI] exec deploy --dry-run` #### 3. **Optimized Program Detection - Built-in Like Go** - **15+ essential programs detected automatically** including: - **Basic commands**: cat, echo, ls, mkdir, rm, cp, mv, grep, find - **Core development tools**: git, make, curl, python, node, docker - **System tools**: sleep, true, false, sh - Cross-platform program existence checking (uses `where` on Windows, `which` on Unix) - Optimized for CI performance with reduced startup time - No manual configuration needed - works exactly like Go testscript ### API Simplification **Simple, Go-like API** - everything detected automatically: ```rust testscript::run("testdata") .execute() // Network + 15+ programs detected automatically .unwrap(); ``` **Manual conditions still supported** for custom scenarios: ```rust testscript::run("testdata") .condition("custom", my_custom_check()) .execute() .unwrap(); ``` ### CI Optimization - **Network detection**: TCP-first approach with 500ms timeouts (vs 1000ms+ ping) - **Program list**: Optimized from 35+ to 15+ essential programs for faster startup - **Better reliability**: Handles restricted CI network environments - **Ubuntu CI compatibility**: Resolved timeout and network access issues ### Real-World Use Cases This enables powerful testing scenarios that work out of the box: ``` # Network-dependent API tests (automatic detection, CI-optimized) [net] exec curl https://api.example.com/health [net] stdout "OK" # Tool-specific tests (automatic detection) [exec:docker] exec docker run --rm hello-world [exec:docker] stdout "Hello from Docker!" [exec:git] exec git status [exec:python] exec python --version # Environment-based behavior [env:CI] exec deploy --dry-run --verbose [!env:PRODUCTION] exec run-dangerous-test # Platform + environment combinations [unix] [env:DISPLAY] exec gui-test --headless [windows] [exec:powershell] exec Get-Process ``` ### Implementation Details - **Network detection**: Multi-approach detection (TCP + ping fallback) optimized for CI reliability - **Program detection**: Essential program list covering most common development tools - **Dynamic evaluation**: Environment conditions are checked at execution time, not initialization - **Error handling**: Clear error messages for unknown conditions - **Cross-platform**: Works consistently across Windows, macOS, and Linux with CI optimizations ### Go Testscript Compatibility ✅ This implementation now matches Go testscript's behavior: - All common conditions available by default without setup - Network condition automatically detected with CI optimization - Essential program detection built-in with performance focus - Clean, simple API that "just works" - Environment variable conditions work dynamically - CI-friendly performance characteristics ### Testing Added comprehensive test coverage with 7 new test cases covering: - Network condition detection (automatic, CI-optimized) - Environment variable condition parsing and evaluation - Optimized program detection (automatic) - Condition combination scenarios - Cross-platform compatibility - Helper function validation All existing tests continue to pass (47 total), ensuring no regressions. ### Backward Compatibility This is a purely additive change. All existing functionality is preserved: - Platform conditions (`[unix]`, `[windows]`, `[linux]`, `[darwin]`) - Build type conditions (`[debug]`, `[release]`) - Manual `.condition()` API still works for custom conditions - All existing test scripts continue to work unchanged Fixes imjasonh/testscript-rs#10 <!-- START COPILOT CODING AGENT SUFFIX --> <details> <summary>Original prompt</summary> > > ---- > > *This section details on the original issue you should resolve* > > <issue_title>Add network and advanced condition support</issue_title> > <issue_description>## Feature Request: Advanced Condition Support > > Go's testscript supports advanced conditions beyond platform detection: > > ## Missing Conditions > > 1. **Network conditions**: `[net]` - Test network availability > 2. **Advanced platform conditions**: More granular OS/arch detection > 3. **Environment-based conditions**: Custom environment variable conditions > > ## Proposed API > > ```rust > testscript::run("testdata") > .condition("net", check_network_available()) > .condition("docker", command_exists("docker")) > .condition("env:CI", std::env::var("CI").is_ok()) > .execute() > .unwrap(); > ``` > > ## Built-in Condition Helpers > > ```rust > // Automatic network detection > testscript::run("testdata") > .auto_detect_network() > .auto_detect_programs(&["docker", "git", "npm"]) > .execute() > .unwrap(); > ``` > > ## Implementation Notes > > - Network detection could ping a reliable host > - Program detection should use cross-platform approach (not just `which`) > - Environment conditions could support pattern matching > - Consider caching condition results for performance > > ## Use Cases > > - **Docker tests**: `[docker] exec docker run hello-world` > - **Network tests**: `[net] exec curl example.com` > - **CI-specific tests**: `[env:CI] exec deploy --dry-run`</issue_description> > > ## Comments on the Issue (you are @copilot in this section) > > <comments> > </comments> > </details> Fixes imjasonh/testscript-rs#10 <!-- START COPILOT CODING AGENT TIPS --> --- 💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs. --------- Signed-off-by: Jason Hall <jason@chainguard.dev> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: imjasonh <210737+imjasonh@users.noreply.github.com> Co-authored-by: Jason Hall <jason@chainguard.dev>
2025-09-27 17:00:36 +00:00
/// .condition("custom", my_custom_check())
Add TestWork functionality to preserve working directories on failure (#24) - [x] Analyze current codebase and understand test execution flow - [x] Add preserve_work_on_failure field to RunParams struct - [x] Add preserve_work_on_failure() method to Builder - [x] Modify TestEnvironment to support preserving work directories - [x] Update run_script_impl to handle work directory preservation on failure - [x] Add comprehensive tests to validate the new functionality - [x] Update documentation with usage examples - [x] Update README with TestWork section showing debugging capabilities - [x] Fix clippy warnings for code quality - [x] Validate functionality works correctly in both debug and release modes - [x] Fix formatting issues that caused CI failure - [x] Replace std::mem::forget() with idiomatic TempDir::keep() method ## Improved Code Quality ✅ Replaced the non-idiomatic use of `std::mem::forget()` with the proper `TempDir::keep()` method for preserving temporary directories. This approach: - Is more explicit about the intent to preserve the directory - Is the recommended way according to tempfile crate documentation - Removes the need for manual memory management workarounds - Makes the code more readable and maintainable All tests continue to pass and functionality remains unchanged. <!-- START COPILOT CODING AGENT SUFFIX --> <details> <summary>Original prompt</summary> > > ---- > > *This section details on the original issue you should resolve* > > <issue_title>Add TestWork functionality to preserve working directories</issue_title> > <issue_description>## Feature Request: TestWork Functionality > > Go's testscript supports a `TestWork` parameter that preserves working directories when tests fail, making debugging much easier. > > ## Proposed API > > ```rust > testscript::run("testdata") > .preserve_work_on_failure(true) > .execute() > .unwrap(); > ``` > > ## Implementation > > - When a test fails and this option is enabled, print the work directory path > - Don't clean up the temporary directory on test failure > - Could also support preserving on success for debugging > > ## Benefits > > - **Easier debugging**: Inspect files created during failed tests > - **Development workflow**: Understand what went wrong > - **Compatibility**: Matches Go testscript behavior > > ## Example Output > > ``` > Test failed. Work directory preserved at: /tmp/testscript-work-abc123 > You can inspect the test environment: > cd /tmp/testscript-work-abc123 > ls -la > ```</issue_description> > > ## Comments on the Issue (you are @copilot in this section) > > <comments> > </comments> > </details> Fixes imjasonh/testscript-rs#6 <!-- START COPILOT CODING AGENT TIPS --> --- 💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click [here](https://survey3.medallia.com/?EAHeSx-AP01bZqG0Ld9QLQ) to start the survey. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: imjasonh <210737+imjasonh@users.noreply.github.com> Co-authored-by: Jason Hall <jason@chainguard.dev>
2025-09-27 19:03:10 +00:00
/// .preserve_work_on_failure(true) // Preserve work directory on test failure
/// .execute()
/// .unwrap();
Add network and advanced condition support with automatic detection and CI optimization (#21) ## Add Network and Advanced Condition Support with Automatic Detection - COMPLETE ✅ This PR implements comprehensive network and advanced condition support for testscript-rs, bringing feature parity with Go's testscript package with automatic detection like the original. ### Core Features Implemented ✨ #### 1. **Network Conditions (`[net]`) - Always Available & CI-Optimized** - Auto-detects network availability with CI-optimized approach: - **TCP-first detection**: Tries TCP connection to DNS servers (faster than ping) - **Ping fallback**: Uses ping with shorter timeouts if TCP fails - Cross-platform implementation optimized for CI environments - Available by default like Go testscript - no manual setup required - Can be used in test scripts as `[net]` for network-required tests or `[!net]` to skip when offline #### 2. **Environment Variable Conditions (`[env:VAR]`)** - Dynamic condition checking for environment variables at execution time - No pre-registration required - conditions are evaluated when encountered - Supports negation: `[!env:MISSING_VAR]` to test when variables are not set - Perfect for CI-specific tests: `[env:CI] exec deploy --dry-run` #### 3. **Optimized Program Detection - Built-in Like Go** - **15+ essential programs detected automatically** including: - **Basic commands**: cat, echo, ls, mkdir, rm, cp, mv, grep, find - **Core development tools**: git, make, curl, python, node, docker - **System tools**: sleep, true, false, sh - Cross-platform program existence checking (uses `where` on Windows, `which` on Unix) - Optimized for CI performance with reduced startup time - No manual configuration needed - works exactly like Go testscript ### API Simplification **Simple, Go-like API** - everything detected automatically: ```rust testscript::run("testdata") .execute() // Network + 15+ programs detected automatically .unwrap(); ``` **Manual conditions still supported** for custom scenarios: ```rust testscript::run("testdata") .condition("custom", my_custom_check()) .execute() .unwrap(); ``` ### CI Optimization - **Network detection**: TCP-first approach with 500ms timeouts (vs 1000ms+ ping) - **Program list**: Optimized from 35+ to 15+ essential programs for faster startup - **Better reliability**: Handles restricted CI network environments - **Ubuntu CI compatibility**: Resolved timeout and network access issues ### Real-World Use Cases This enables powerful testing scenarios that work out of the box: ``` # Network-dependent API tests (automatic detection, CI-optimized) [net] exec curl https://api.example.com/health [net] stdout "OK" # Tool-specific tests (automatic detection) [exec:docker] exec docker run --rm hello-world [exec:docker] stdout "Hello from Docker!" [exec:git] exec git status [exec:python] exec python --version # Environment-based behavior [env:CI] exec deploy --dry-run --verbose [!env:PRODUCTION] exec run-dangerous-test # Platform + environment combinations [unix] [env:DISPLAY] exec gui-test --headless [windows] [exec:powershell] exec Get-Process ``` ### Implementation Details - **Network detection**: Multi-approach detection (TCP + ping fallback) optimized for CI reliability - **Program detection**: Essential program list covering most common development tools - **Dynamic evaluation**: Environment conditions are checked at execution time, not initialization - **Error handling**: Clear error messages for unknown conditions - **Cross-platform**: Works consistently across Windows, macOS, and Linux with CI optimizations ### Go Testscript Compatibility ✅ This implementation now matches Go testscript's behavior: - All common conditions available by default without setup - Network condition automatically detected with CI optimization - Essential program detection built-in with performance focus - Clean, simple API that "just works" - Environment variable conditions work dynamically - CI-friendly performance characteristics ### Testing Added comprehensive test coverage with 7 new test cases covering: - Network condition detection (automatic, CI-optimized) - Environment variable condition parsing and evaluation - Optimized program detection (automatic) - Condition combination scenarios - Cross-platform compatibility - Helper function validation All existing tests continue to pass (47 total), ensuring no regressions. ### Backward Compatibility This is a purely additive change. All existing functionality is preserved: - Platform conditions (`[unix]`, `[windows]`, `[linux]`, `[darwin]`) - Build type conditions (`[debug]`, `[release]`) - Manual `.condition()` API still works for custom conditions - All existing test scripts continue to work unchanged Fixes imjasonh/testscript-rs#10 <!-- START COPILOT CODING AGENT SUFFIX --> <details> <summary>Original prompt</summary> > > ---- > > *This section details on the original issue you should resolve* > > <issue_title>Add network and advanced condition support</issue_title> > <issue_description>## Feature Request: Advanced Condition Support > > Go's testscript supports advanced conditions beyond platform detection: > > ## Missing Conditions > > 1. **Network conditions**: `[net]` - Test network availability > 2. **Advanced platform conditions**: More granular OS/arch detection > 3. **Environment-based conditions**: Custom environment variable conditions > > ## Proposed API > > ```rust > testscript::run("testdata") > .condition("net", check_network_available()) > .condition("docker", command_exists("docker")) > .condition("env:CI", std::env::var("CI").is_ok()) > .execute() > .unwrap(); > ``` > > ## Built-in Condition Helpers > > ```rust > // Automatic network detection > testscript::run("testdata") > .auto_detect_network() > .auto_detect_programs(&["docker", "git", "npm"]) > .execute() > .unwrap(); > ``` > > ## Implementation Notes > > - Network detection could ping a reliable host > - Program detection should use cross-platform approach (not just `which`) > - Environment conditions could support pattern matching > - Consider caching condition results for performance > > ## Use Cases > > - **Docker tests**: `[docker] exec docker run hello-world` > - **Network tests**: `[net] exec curl example.com` > - **CI-specific tests**: `[env:CI] exec deploy --dry-run`</issue_description> > > ## Comments on the Issue (you are @copilot in this section) > > <comments> > </comments> > </details> Fixes imjasonh/testscript-rs#10 <!-- START COPILOT CODING AGENT TIPS --> --- 💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs. --------- Signed-off-by: Jason Hall <jason@chainguard.dev> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: imjasonh <210737+imjasonh@users.noreply.github.com> Co-authored-by: Jason Hall <jason@chainguard.dev>
2025-09-27 17:00:36 +00:00
///
/// fn my_custom_check() -> bool {
/// // Your custom condition logic
/// true
/// }
/// ```
Add WorkdirRoot configuration for custom work directories (#27) - [x] Add `workdir_root` field to `RunParams` struct - [x] Add `workdir_root` method to `Builder` API - [x] Modify `TestEnvironment::new()` to accept optional root directory - [x] Update `TestEnvironment::new()` to use `tempfile::TempDir::new_in()` when root is specified - [x] Add validation for root directory existence and writability - [x] Add tests for the new workdir_root functionality - [x] Update documentation and examples - [x] Address PR feedback: remove unnecessary files and improve tests - [x] Fix code formatting with cargo fmt - [x] Add rustfmt.toml configuration and update CLAUDE.md - [x] Improve basic test to verify actual workdir usage ## Recent Changes **Enhanced basic functionality test:** - Modified `test_workdir_root_basic_functionality` to induce a failure and use `preserve_work_on_failure(true)` - Test now verifies that the workdir was actually created in the custom root directory - Validates that test files exist in the preserved workdir with expected content - **Removed** redundant `test_workdir_root_with_preserve_work` test as the functionality is now covered by the improved basic test The test suite is now more meaningful and comprehensive, with 4 focused tests that provide real validation of the workdir_root functionality: - Basic functionality with actual workdir verification - Error handling for nonexistent directories - Error handling for non-directory paths - API chaining validation All tests pass and the implementation properly validates that custom workdir roots are used as expected. <!-- START COPILOT CODING AGENT TIPS --> --- 💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: imjasonh <210737+imjasonh@users.noreply.github.com> Co-authored-by: Jason Hall <jason@chainguard.dev>
2025-09-30 06:57:10 +00:00
///
/// ### With Custom Work Directory Root
/// ```no_run
/// use testscript_rs::testscript;
///
/// // Use a custom location for test working directories
/// testscript::run("testdata")
/// .workdir_root("/tmp/my-app-tests")
/// .preserve_work_on_failure(true) // Combine features
/// .execute()
/// .unwrap();
/// ```
pub struct Builder {
dir: String,
params: RunParams,
}
impl Builder {
/// Create a new builder for the given test directory
fn new(dir: impl Into<String>) -> Self {
Self {
dir: dir.into(),
params: RunParams::new(),
}
}
/// Add a setup function that runs before each test script
///
/// The setup function receives a reference to the test environment and can
/// perform actions like compiling binaries or setting up test data.
pub fn setup<F>(mut self, func: F) -> Self
where
F: Fn(&TestEnvironment) -> Result<()> + 'static,
{
self.params = self.params.setup(func);
self
}
/// Add a custom command that can be used in test scripts
///
/// # Arguments
/// * `name` - The command name as it will appear in test scripts
/// * `func` - The function to execute when the command is called
pub fn command(mut self, name: &str, func: CommandFn) -> Self {
self.params = self.params.command(name, func);
self
}
/// Set a condition value for conditional command execution
///
Add network and advanced condition support with automatic detection and CI optimization (#21) ## Add Network and Advanced Condition Support with Automatic Detection - COMPLETE ✅ This PR implements comprehensive network and advanced condition support for testscript-rs, bringing feature parity with Go's testscript package with automatic detection like the original. ### Core Features Implemented ✨ #### 1. **Network Conditions (`[net]`) - Always Available & CI-Optimized** - Auto-detects network availability with CI-optimized approach: - **TCP-first detection**: Tries TCP connection to DNS servers (faster than ping) - **Ping fallback**: Uses ping with shorter timeouts if TCP fails - Cross-platform implementation optimized for CI environments - Available by default like Go testscript - no manual setup required - Can be used in test scripts as `[net]` for network-required tests or `[!net]` to skip when offline #### 2. **Environment Variable Conditions (`[env:VAR]`)** - Dynamic condition checking for environment variables at execution time - No pre-registration required - conditions are evaluated when encountered - Supports negation: `[!env:MISSING_VAR]` to test when variables are not set - Perfect for CI-specific tests: `[env:CI] exec deploy --dry-run` #### 3. **Optimized Program Detection - Built-in Like Go** - **15+ essential programs detected automatically** including: - **Basic commands**: cat, echo, ls, mkdir, rm, cp, mv, grep, find - **Core development tools**: git, make, curl, python, node, docker - **System tools**: sleep, true, false, sh - Cross-platform program existence checking (uses `where` on Windows, `which` on Unix) - Optimized for CI performance with reduced startup time - No manual configuration needed - works exactly like Go testscript ### API Simplification **Simple, Go-like API** - everything detected automatically: ```rust testscript::run("testdata") .execute() // Network + 15+ programs detected automatically .unwrap(); ``` **Manual conditions still supported** for custom scenarios: ```rust testscript::run("testdata") .condition("custom", my_custom_check()) .execute() .unwrap(); ``` ### CI Optimization - **Network detection**: TCP-first approach with 500ms timeouts (vs 1000ms+ ping) - **Program list**: Optimized from 35+ to 15+ essential programs for faster startup - **Better reliability**: Handles restricted CI network environments - **Ubuntu CI compatibility**: Resolved timeout and network access issues ### Real-World Use Cases This enables powerful testing scenarios that work out of the box: ``` # Network-dependent API tests (automatic detection, CI-optimized) [net] exec curl https://api.example.com/health [net] stdout "OK" # Tool-specific tests (automatic detection) [exec:docker] exec docker run --rm hello-world [exec:docker] stdout "Hello from Docker!" [exec:git] exec git status [exec:python] exec python --version # Environment-based behavior [env:CI] exec deploy --dry-run --verbose [!env:PRODUCTION] exec run-dangerous-test # Platform + environment combinations [unix] [env:DISPLAY] exec gui-test --headless [windows] [exec:powershell] exec Get-Process ``` ### Implementation Details - **Network detection**: Multi-approach detection (TCP + ping fallback) optimized for CI reliability - **Program detection**: Essential program list covering most common development tools - **Dynamic evaluation**: Environment conditions are checked at execution time, not initialization - **Error handling**: Clear error messages for unknown conditions - **Cross-platform**: Works consistently across Windows, macOS, and Linux with CI optimizations ### Go Testscript Compatibility ✅ This implementation now matches Go testscript's behavior: - All common conditions available by default without setup - Network condition automatically detected with CI optimization - Essential program detection built-in with performance focus - Clean, simple API that "just works" - Environment variable conditions work dynamically - CI-friendly performance characteristics ### Testing Added comprehensive test coverage with 7 new test cases covering: - Network condition detection (automatic, CI-optimized) - Environment variable condition parsing and evaluation - Optimized program detection (automatic) - Condition combination scenarios - Cross-platform compatibility - Helper function validation All existing tests continue to pass (47 total), ensuring no regressions. ### Backward Compatibility This is a purely additive change. All existing functionality is preserved: - Platform conditions (`[unix]`, `[windows]`, `[linux]`, `[darwin]`) - Build type conditions (`[debug]`, `[release]`) - Manual `.condition()` API still works for custom conditions - All existing test scripts continue to work unchanged Fixes imjasonh/testscript-rs#10 <!-- START COPILOT CODING AGENT SUFFIX --> <details> <summary>Original prompt</summary> > > ---- > > *This section details on the original issue you should resolve* > > <issue_title>Add network and advanced condition support</issue_title> > <issue_description>## Feature Request: Advanced Condition Support > > Go's testscript supports advanced conditions beyond platform detection: > > ## Missing Conditions > > 1. **Network conditions**: `[net]` - Test network availability > 2. **Advanced platform conditions**: More granular OS/arch detection > 3. **Environment-based conditions**: Custom environment variable conditions > > ## Proposed API > > ```rust > testscript::run("testdata") > .condition("net", check_network_available()) > .condition("docker", command_exists("docker")) > .condition("env:CI", std::env::var("CI").is_ok()) > .execute() > .unwrap(); > ``` > > ## Built-in Condition Helpers > > ```rust > // Automatic network detection > testscript::run("testdata") > .auto_detect_network() > .auto_detect_programs(&["docker", "git", "npm"]) > .execute() > .unwrap(); > ``` > > ## Implementation Notes > > - Network detection could ping a reliable host > - Program detection should use cross-platform approach (not just `which`) > - Environment conditions could support pattern matching > - Consider caching condition results for performance > > ## Use Cases > > - **Docker tests**: `[docker] exec docker run hello-world` > - **Network tests**: `[net] exec curl example.com` > - **CI-specific tests**: `[env:CI] exec deploy --dry-run`</issue_description> > > ## Comments on the Issue (you are @copilot in this section) > > <comments> > </comments> > </details> Fixes imjasonh/testscript-rs#10 <!-- START COPILOT CODING AGENT TIPS --> --- 💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs. --------- Signed-off-by: Jason Hall <jason@chainguard.dev> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: imjasonh <210737+imjasonh@users.noreply.github.com> Co-authored-by: Jason Hall <jason@chainguard.dev>
2025-09-27 17:00:36 +00:00
/// Use this to add custom conditions beyond the built-in ones.
/// Many common conditions are automatically detected (see Builder docs for details).
///
/// # Arguments
/// * `name` - The condition name (use in scripts as `[name]`)
/// * `value` - Whether the condition is met
///
/// # Built-in Conditions (automatically available)
/// - `net` - Network connectivity
/// - `unix`, `windows`, `linux`, `darwin` - Platform detection
/// - `debug`, `release` - Build type
/// - `exec:program` - Program availability (35+ programs)
/// - `env:VAR` - Environment variables (dynamic)
///
/// # Examples
/// ```no_run
/// use testscript_rs::testscript;
///
/// testscript::run("testdata")
/// .condition("feature_enabled", cfg!(feature = "advanced"))
/// .condition("has_gpu", check_gpu_available())
/// .execute()
/// .unwrap();
///
/// fn check_gpu_available() -> bool {
/// // Your GPU detection logic
/// false
/// }
/// ```
pub fn condition(mut self, name: &str, value: bool) -> Self {
self.params = self.params.condition(name, value);
self
}
Add UpdateScripts functionality for test maintenance (#20) - [x] Analyze existing codebase structure and architecture - [x] Review test files to understand expected UpdateScripts functionality - [x] Understand current API and parameter structure - [x] Add `update_scripts` parameter to RunParams struct - [x] Add `update_scripts` method to Builder API - [x] Support UPDATE_SCRIPTS environment variable detection - [x] Implement update functionality in stdout/stderr command execution - [x] Modify output comparison to capture and update actual output when in update mode - [x] Add script file modification logic to update test files - [x] Create focused tests for update functionality - [x] Validate changes work end-to-end - [x] Update documentation in README.md - [x] Clean up temporary files - [x] Move UpdateScripts section to bottom of README per review feedback - [x] Fix code formatting issues with cargo fmt ## Implementation Summary Successfully implemented complete UpdateScripts functionality for test maintenance in testscript-rs: ### Key Features - **API support**: `.update_scripts(true)` method on Builder - **Environment variable**: `UPDATE_SCRIPTS=1` environment variable support - **Smart updating**: Only updates `stdout`/`stderr` expectations, preserves file structure - **Proper quoting**: Handles complex output with appropriate shell quoting - **Error handling**: Continues execution instead of failing in update mode ### API Usage ```rust // Via API testscript::run("testdata") .update_scripts(true) .execute() .unwrap(); // Via environment variable UPDATE_SCRIPTS=1 cargo test ``` ### Implementation Details - Added `update_scripts: bool` field to `RunParams` - Modified `run_script_impl` to collect updates instead of failing on mismatches - Implemented `apply_script_updates` function for file modification - Added comprehensive test coverage with 4 test cases - Updated README.md with usage examples (moved to bottom per review feedback) - Fixed code formatting issues identified by CI ### Testing - All existing tests continue to pass - New functionality tested via API and environment variable - Manual validation confirms proper file updating - Normal mode behavior preserved (still fails on mismatches when disabled) - Code passes formatting checks and linting The implementation matches Go's testscript behavior and provides essential test maintenance functionality. <!-- START COPILOT CODING AGENT SUFFIX --> <details> <summary>Original prompt</summary> > > ---- > > *This section details on the original issue you should resolve* > > <issue_title>Add UpdateScripts functionality for test maintenance</issue_title> > <issue_description>## Feature Request: UpdateScripts (Test Maintenance Mode) > > Go's testscript supports an `UpdateScripts` parameter that automatically updates test files with actual command output. This is invaluable for test maintenance. > > ## Proposed API > > ```rust > testscript::run("testdata") > .update_scripts(true) > .execute() > .unwrap(); > ``` > > Or via environment variable: > ```bash > UPDATE_SCRIPTS=1 cargo test > ``` > > ## How It Works > > When enabled, instead of failing on output mismatches: > 1. **Capture actual output** > 2. **Update the test file** with the actual output > 3. **Continue to next test** > > ## Example > > Before (failing test): > ``` > exec my-tool --version > stdout "my-tool 1.0" > ``` > > After running with update mode: > ``` > exec my-tool --version > stdout "my-tool 2.1.0" > ``` > > ## Benefits > > - **Easy test maintenance**: Update expected outputs after changes > - **Reduce manual work**: No hand-editing of test files > - **Go compatibility**: Matches upstream behavior > - **Development workflow**: Essential for evolving CLI tools > > ## Implementation Notes > > - Should only update `stdout`/`stderr` expectations > - Preserve comments and file structure > - Add safety checks to prevent accidental overwrites</issue_description> > > ## Comments on the Issue (you are @copilot in this section) > > <comments> > </comments> > </details> Fixes imjasonh/testscript-rs#8 <!-- START COPILOT CODING AGENT TIPS --> --- 💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click [here](https://survey3.medallia.com/?EAHeSx-AP01bZqG0Ld9QLQ) to start the survey. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: imjasonh <210737+imjasonh@users.noreply.github.com>
2025-09-26 21:47:09 -04:00
/// Enable or disable updating test scripts with actual output
///
/// When enabled, instead of failing on output mismatches, the test files
/// will be updated with the actual command output.
pub fn update_scripts(mut self, update: bool) -> Self {
self.params = self.params.update_scripts(update);
self
}
Add TestWork functionality to preserve working directories on failure (#24) - [x] Analyze current codebase and understand test execution flow - [x] Add preserve_work_on_failure field to RunParams struct - [x] Add preserve_work_on_failure() method to Builder - [x] Modify TestEnvironment to support preserving work directories - [x] Update run_script_impl to handle work directory preservation on failure - [x] Add comprehensive tests to validate the new functionality - [x] Update documentation with usage examples - [x] Update README with TestWork section showing debugging capabilities - [x] Fix clippy warnings for code quality - [x] Validate functionality works correctly in both debug and release modes - [x] Fix formatting issues that caused CI failure - [x] Replace std::mem::forget() with idiomatic TempDir::keep() method ## Improved Code Quality ✅ Replaced the non-idiomatic use of `std::mem::forget()` with the proper `TempDir::keep()` method for preserving temporary directories. This approach: - Is more explicit about the intent to preserve the directory - Is the recommended way according to tempfile crate documentation - Removes the need for manual memory management workarounds - Makes the code more readable and maintainable All tests continue to pass and functionality remains unchanged. <!-- START COPILOT CODING AGENT SUFFIX --> <details> <summary>Original prompt</summary> > > ---- > > *This section details on the original issue you should resolve* > > <issue_title>Add TestWork functionality to preserve working directories</issue_title> > <issue_description>## Feature Request: TestWork Functionality > > Go's testscript supports a `TestWork` parameter that preserves working directories when tests fail, making debugging much easier. > > ## Proposed API > > ```rust > testscript::run("testdata") > .preserve_work_on_failure(true) > .execute() > .unwrap(); > ``` > > ## Implementation > > - When a test fails and this option is enabled, print the work directory path > - Don't clean up the temporary directory on test failure > - Could also support preserving on success for debugging > > ## Benefits > > - **Easier debugging**: Inspect files created during failed tests > - **Development workflow**: Understand what went wrong > - **Compatibility**: Matches Go testscript behavior > > ## Example Output > > ``` > Test failed. Work directory preserved at: /tmp/testscript-work-abc123 > You can inspect the test environment: > cd /tmp/testscript-work-abc123 > ls -la > ```</issue_description> > > ## Comments on the Issue (you are @copilot in this section) > > <comments> > </comments> > </details> Fixes imjasonh/testscript-rs#6 <!-- START COPILOT CODING AGENT TIPS --> --- 💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click [here](https://survey3.medallia.com/?EAHeSx-AP01bZqG0Ld9QLQ) to start the survey. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: imjasonh <210737+imjasonh@users.noreply.github.com> Co-authored-by: Jason Hall <jason@chainguard.dev>
2025-09-27 19:03:10 +00:00
/// Enable or disable preserving working directories when tests fail
///
/// When enabled, if a test fails, the working directory will be preserved
/// and its path will be printed to stderr for debugging purposes.
/// This matches the behavior of Go's testscript TestWork functionality.
///
/// # Examples
/// ```no_run
/// use testscript_rs::testscript;
///
/// testscript::run("testdata")
/// .preserve_work_on_failure(true)
/// .execute()
/// .unwrap();
/// ```
///
/// When a test fails, you'll see output like:
/// ```text
/// Test failed. Work directory preserved at: /tmp/testscript-work-abc123
/// You can inspect the test environment:
/// cd /tmp/testscript-work-abc123
/// ls -la
/// ```
pub fn preserve_work_on_failure(mut self, preserve: bool) -> Self {
self.params = self.params.preserve_work_on_failure(preserve);
self
}
Add WorkdirRoot configuration for custom work directories (#27) - [x] Add `workdir_root` field to `RunParams` struct - [x] Add `workdir_root` method to `Builder` API - [x] Modify `TestEnvironment::new()` to accept optional root directory - [x] Update `TestEnvironment::new()` to use `tempfile::TempDir::new_in()` when root is specified - [x] Add validation for root directory existence and writability - [x] Add tests for the new workdir_root functionality - [x] Update documentation and examples - [x] Address PR feedback: remove unnecessary files and improve tests - [x] Fix code formatting with cargo fmt - [x] Add rustfmt.toml configuration and update CLAUDE.md - [x] Improve basic test to verify actual workdir usage ## Recent Changes **Enhanced basic functionality test:** - Modified `test_workdir_root_basic_functionality` to induce a failure and use `preserve_work_on_failure(true)` - Test now verifies that the workdir was actually created in the custom root directory - Validates that test files exist in the preserved workdir with expected content - **Removed** redundant `test_workdir_root_with_preserve_work` test as the functionality is now covered by the improved basic test The test suite is now more meaningful and comprehensive, with 4 focused tests that provide real validation of the workdir_root functionality: - Basic functionality with actual workdir verification - Error handling for nonexistent directories - Error handling for non-directory paths - API chaining validation All tests pass and the implementation properly validates that custom workdir roots are used as expected. <!-- START COPILOT CODING AGENT TIPS --> --- 💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: imjasonh <210737+imjasonh@users.noreply.github.com> Co-authored-by: Jason Hall <jason@chainguard.dev>
2025-09-30 06:57:10 +00:00
/// Set the root directory for test working directories
///
/// When specified, test directories will be created inside this root directory
/// instead of the system default temporary directory. This is useful for:
/// - **Debugging**: Use a known location for test directories
/// - **Performance**: Use faster storage (e.g., tmpfs on Linux)
/// - **CI environments**: Use specific temp locations
/// - **Security**: Isolate test directories to specific paths
///
/// # Arguments
/// * `root` - The directory path where test working directories should be created
///
/// # Examples
/// ```no_run
/// use testscript_rs::testscript;
///
/// // Use custom location for test directories
/// testscript::run("testdata")
/// .workdir_root("/tmp/my-app-tests")
/// .preserve_work_on_failure(true) // Combine with other features
/// .execute()
/// .unwrap();
/// ```
///
/// This creates test directories like `/tmp/my-app-tests/testscript-abc123/`.
///
/// # Notes
/// - The root directory must exist and be writable
/// - If not specified, uses the system default temporary directory
/// - Each test still gets its own isolated subdirectory
pub fn workdir_root<P: Into<std::path::PathBuf>>(mut self, root: P) -> Self {
self.params = self.params.workdir_root(root);
self
}
/// Run only specific test files instead of discovering all .txt files
///
/// When specified, only these files will be executed instead of discovering
/// all .txt files in the directory. Files can be specified as:
/// - Relative paths (relative to the test directory): `"hello.txt"`
/// - Absolute paths: `"/path/to/test.txt"`
/// - Just filenames: `"test.txt"` (looked up in the test directory)
///
/// # Arguments
/// * `files` - An iterator of file paths to run
///
/// # Examples
/// ```no_run
/// use testscript_rs::testscript;
///
/// // Run specific test files
/// testscript::run("testdata")
/// .files(["hello.txt", "exists.txt"])
/// .execute()
/// .unwrap();
///
/// // Using Vec<String>
/// let test_files = vec!["hello.txt".to_string(), "exists.txt".to_string()];
/// testscript::run("testdata")
/// .files(test_files)
/// .execute()
/// .unwrap();
/// ```
///
/// # Benefits
/// - **Selective testing**: Run only specific test scenarios
/// - **Faster development**: Skip unrelated tests during development
/// - **CI optimization**: Run subsets of tests in different jobs
/// - **Go compatibility**: Match Go testscript functionality
pub fn files<I, S>(mut self, files: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
self.params = self.params.files(files);
self
}
/// Execute all test scripts in the configured directory
///
/// This will discover all `.txt` files in the directory and run them as test scripts.
/// Each test runs in isolation with its own temporary directory.
///
/// # Returns
/// `Ok(())` if all tests pass, or the first error encountered.
pub fn execute(mut self) -> Result<()> {
let pattern = format!("{}/*.txt", self.dir);
run(&mut self.params, &pattern)
}
}
/// Create a new testscript builder for the given directory
///
/// This is the main entry point for running testscript tests.
///
/// # Examples
///
/// ```no_run
/// use testscript_rs::testscript;
///
/// // Run all tests in testdata directory
/// testscript::run("testdata").execute().unwrap();
///
/// // Run only specific test files
/// testscript::run("testdata")
/// .files(["hello.txt", "exists.txt"])
/// .execute()
/// .unwrap();
/// ```
pub mod testscript {
use super::*;
/// Create a new testscript builder for the given directory
pub fn run(dir: impl Into<String>) -> Builder {
Builder::new(dir)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn basic_integration_test() {
// Test the parser directly with a simple script
let script_content = r#"exec echo hello
stdout hello
-- file.txt --
content"#;
let script = crate::parser::parse(script_content).unwrap();
assert_eq!(script.commands.len(), 2);
assert_eq!(script.files.len(), 1);
}
#[test]
fn test_example() {
use std::fs;
use tempfile::TempDir;
let temp_dir = TempDir::new().unwrap();
let testdata_dir = temp_dir.path().join("testdata");
fs::create_dir(&testdata_dir).unwrap();
let test_content = r#"exec echo "API works!"
stdout "API works!"
-- test.txt --
content"#;
fs::write(testdata_dir.join("api_test.txt"), test_content).unwrap();
let result = testscript::run(testdata_dir.to_string_lossy()).execute();
assert!(result.is_ok(), "API example failed: {:?}", result);
}
}