1
0
Fork 0
mirror of https://github.com/imjasonh/testscript-rs synced 2026-07-08 09:05:34 +00:00
testscript-rs/README.md
Copilot fdb53b03fa
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

5.3 KiB

testscript-rs

CI Crates.io

A Rust crate for testing command-line tools using filesystem-based script files.

testscript-rs provides a framework for writing integration tests for CLI applications using the .txtar format, where test scripts and file contents are combined in a single file.

This crate is inspired by and aims to be compatible with Go's github.com/rogpeppe/go-internal/testscript package.

Testscript is primarily useful for describing testing scenarios involving executing commands and dealing with files. This makes it a good choice for testing CLI applications in a succinct and human-readable way.

Quick Example

use testscript_rs::testscript;

#[test]
fn test_my_cli() {
    testscript::run("testdata")
        .setup(|env| {
            // Compile your CLI tool
            std::process::Command::new("cargo")
                .args(["build", "--bin", "my-cli"])
                .status()?;

            // Copy binary to test environment
            std::fs::copy("target/debug/my-cli", env.work_dir.join("my-cli"))?;
            Ok(())
        })
        .execute()
        .unwrap();
}

With a test script in testdata/basic.txt:

# Test basic functionality
exec ./my-cli --version
stdout "my-cli 1.0"

exec ./my-cli process input.txt
cmp output.txt expected.txt

-- input.txt --
test content

-- expected.txt --
processed: test content

Running the test will compile the CLI program, make it available to the testscript environment, run the specified commands, and check its output.

Installation

Add testscript-rs to your Cargo.toml:

[dev-dependencies]
testscript-rs = "<release>"

Requires Rust 1.70 or later.

Usage

Basic Usage

use testscript_rs::testscript;

#[test]
fn test_cli() {
    testscript::run("testdata").execute().unwrap();
}

With Setup Hook

testscript::run("testdata")
    .setup(|env| {
        // Compile your binary before each test
        std::process::Command::new("cargo")
            .args(["build", "--bin", "my-tool"])
            .status()?;
        Ok(())
    })
    .execute()
    .unwrap();

With Custom Commands

testscript::run("testdata")
    .command("custom-cmd", |env, args| {
        // Your custom command implementation
        println!("Running custom command with args: {:?}", args);
        Ok(())
    })
    .condition("feature-enabled", true)
    .execute()
    .unwrap();

To call the custom command, in your testscript file:

custom-cmd arg1 arg2 arg3

Test Script Format

Test scripts use the txtar format. For complete format documentation, see the original Go testscript documentation.

Built-in Commands

  • exec - Execute external commands
  • cmp - Compare two files
  • stdout/stderr - Check command output (supports regex)
  • exists - Check file existence
  • mkdir - Create directories
  • cp - Copy files (supports stdout/stderr as source)
  • mv - Move/rename files
  • rm - Remove files/directories
  • chmod - Change file permissions
  • env - Set environment variables
  • cmpenv - Compare files with environment variable substitution
  • stdin - Set stdin for next command
  • cd - Change working directory
  • wait - Wait for background processes
  • kill - Kill background processes
  • skip - Skip test execution
  • stop - Stop test early (pass)
  • unquote - Remove leading > from file lines
  • grep - Search files with regex

Commands can be prefixed with conditions ([unix]) or negated (!).

Error Messages

testscript-rs provides detailed error messages with script context to make debugging easy:

Error in testdata/hello.txt at line 6:
  3 | stdout "this works"
  4 |
  5 | # This command will fail
> 6 | exec nonexistent-command arg1 arg2
  7 | stdout "should not get here"
  8 |

Note: Some features of testscript in Go are not supported in this Rust port:

  • [gc] for whether Go was built with gc
  • [gccgo] for whether Go was built with gccgo
  • [go1.x] for whether the Go version is 1.x or later

Examples

See examples/sample-cli/ and its testdata directory for more examples.

There are also more tests in testdata that demonstrate and check this implementations behavior.

UpdateScripts (Test Maintenance)

UpdateScripts automatically updates test files with actual command output, making test maintenance easier:

// Enable via API
testscript::run("testdata")
    .update_scripts(true)
    .execute()
    .unwrap();

Or via environment variable:

UPDATE_SCRIPTS=1 cargo test

When enabled, instead of failing on output mismatches, the test files will be updated with actual command output:

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"

This feature only updates stdout and stderr expectations while preserving file structure and comments.