1
0
Fork 0
mirror of https://github.com/imjasonh/testscript-rs synced 2026-07-08 09:05:34 +00:00
No description
Find a file
Copilot fd479ece24
Add support for running specific test files (#32)
Adds a new `.files()` method to the Builder API that allows running
specific test files instead of discovering all `.txt` files in a
directory. This mirrors the functionality of Go's testscript `Files`
parameter.

## New API

```rust
// Run only specific test files
testscript::run("testdata")
    .files(["hello.txt", "exists.txt"])
    .execute()
    .unwrap();

// Combine with other features
testscript::run("testdata")
    .files(["hello.txt", "exists.txt"])
    .setup(|env| { /* setup code */ Ok(()) })
    .condition("custom", true)
    .preserve_work_on_failure(true)
    .execute()
    .unwrap();
```

## Benefits

- **Selective testing**: Run only specific test scenarios during
development
- **Faster CI**: Run subsets of tests in parallel jobs
- **Go compatibility**: Matches `testscript.Run(t,
testscript.Params{Files: []string{...}})`
- **Flexible paths**: Supports relative paths, absolute paths, and
filenames

## Implementation Details

- Adds optional `files` field to `RunParams` 
- Enhances internal `run()` function to handle file lists vs glob
discovery
- Preserves execution order as specified by user
- Comprehensive error handling for missing files and invalid paths
- Full backward compatibility - existing code unchanged

## Path Support

```rust
testscript::run("testdata")
    .files([
        "hello.txt",              // filename in testdata dir
        "subdir/nested.txt",      // relative path  
        "/abs/path/to/test.txt"   // absolute path
    ])
    .execute()
    .unwrap();
```

The feature includes focused test coverage with 7 test cases covering
essential scenarios including error conditions and path types. Based on
feedback, redundant tests were removed to maintain a clean and
meaningful test suite.

Fixes imjasonh/testscript-rs#7

<!-- START COPILOT CODING AGENT SUFFIX -->



<details>

<summary>Original prompt</summary>

> 
> ----
> 
> *This section details on the original issue you should resolve*
> 
> <issue_title>Add support for running specific test files</issue_title>
> <issue_description>## Feature Request: Run Specific Test Files
> 
> Go's testscript supports running specific files via the `Files`
parameter:
> 
> ```go
> testscript.Run(t, testscript.Params{
>     Files: []string{"specific_test.txt", "another_test.txt"},
> })
> ```
> 
> ## Proposed API
> 
> ```rust
> testscript::run("testdata")
>     .files(&["hello.txt", "exists.txt"])
>     .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
> 
> ## Implementation
> 
> - Extend the Builder API with a `.files()` method
> - Create a separate `run_files()` entry point
> - Support both absolute and relative paths
> - Validate that specified files exist</issue_description>
> 
> ## Comments on the Issue (you are @copilot in this section)
> 
> <comments>
> </comments>
> 


</details>
Fixes imjasonh/testscript-rs#7

<!-- START COPILOT CODING AGENT TIPS -->
---

 Let Copilot coding agent [set things up for
you](https://github.com/imjasonh/testscript-rs/issues/new?title=+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot)
— coding agent works faster and does higher quality work when set up for
your repo.

---------

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-30 03:36:07 -04:00
.github/workflows [WIP] Add fuzz testing for parser resilience (#26) 2025-09-27 22:03:15 +00:00
examples Add TestWork functionality to preserve working directories on failure (#24) 2025-09-27 19:03:10 +00:00
fuzz Add comprehensive fuzz testing infrastructure with automated CI/CD integration (#25) 2025-09-27 19:36:31 +00:00
src Add support for running specific test files (#32) 2025-09-30 03:36:07 -04:00
testdata Revert "skip [net] tests on CI" (#33) 2025-09-30 07:34:09 +00:00
tests Add support for running specific test files (#32) 2025-09-30 03:36:07 -04:00
.gitignore initial commit 2025-09-26 18:41:14 -04:00
.pre-commit-config.yaml add pre-commit config (#15) 2025-09-26 23:57:51 +00:00
Cargo.lock Add comprehensive fuzz testing infrastructure with automated CI/CD integration (#25) 2025-09-27 19:36:31 +00:00
Cargo.toml Add comprehensive fuzz testing infrastructure with automated CI/CD integration (#25) 2025-09-27 19:36:31 +00:00
CLAUDE.md Add WorkdirRoot configuration for custom work directories (#27) 2025-09-30 06:57:10 +00:00
FUZZ.md Add comprehensive fuzz testing infrastructure with automated CI/CD integration (#25) 2025-09-27 19:36:31 +00:00
LICENSE add stuff 2025-09-26 18:53:41 -04:00
README.md Improve README.md with more examples and details (#28) 2025-09-30 06:20:26 +00:00

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 a simple DSL 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, which was itself extracted from internal packages in the Go stdlib, having originally been written by Russ Cox.

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

Given a file testdata/simple.txt:

echo "hello"    # print "hello"
stdout "hello"  # check that stdout includes "hello"

You can run this in your tests:

use testscript_rs::testscript;

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

The test will find every testscript file in ./testdata, and execute it.

A more realistic example might look like this:

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

With Custom Commands

testscript::run("testdata")
    .command("custom-cmd", |env, args| {
        // Your custom command implementation, e.g., the entrypoint of your CLI, so you don't have to `cargo build` it as above.
        println!("Running custom command with args: {:?}", args);
        Ok(())
    })
    .condition("feature-enabled", true)
    .preserve_work_on_failure(true)  // Debug failed tests
    .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
  • symlink - Create symbolic links

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 for Easier 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.