Acceptance criteria for the lib/runcmd Subplot library

By: The Subplot project

2025-10-13 10:01

Table of Contents

1 Introduction

The Subplot library runcmd for Python provides scenario steps and their implementations for running Unix commands and examining the results. The library consists of a bindings file lib/runcmd.yaml and implementations in Python in lib/runcmd.py or in the Rust subplotlib step library. There is no Bash version.

This document explains the acceptance criteria for the library and how they're verified. It uses the steps and functions from the lib/runcmd library. The scenarios all have the same structure: run a command, then examine the exit code, standard output (stdout for short), or standard error output (stderr) of the command.

The scenarios use the Unix commands true and false to generate exit codes, and echo to produce stdout. To generate stderr, they use the little helper script below.

#!/bin/sh
echo "$@" 1>&2
#!/bin/sh
echo $HOME

2 Check exit code

These scenarios verify the exit code. To make it easier to write scenarios in language that flows more naturally, there are a couple of variations.

2.1 Successful execution

1 when I run true
2 then exit code is 0
3 then command is successful
 
4 then I can run true
5 then exit code is 0
6 then command is successful

2.2 Successful execution in a sub-directory

1 given a directory xyzzy
2 when I run, in xyzzy, pwd
3 then exit code is 0
4 then command is successful
5 then stdout contains "/xyzzy"

2.3 Execution in a sub-directory does not affect HOME

1 given a directory xyzzy
2 given executable file showhome from showhome.sh
3 when I prepend .. to PATH
4 when I run, in xyzzy, showhome
5 then exit code is 0
6 then command is successful
7 then stdout doesn't contain "/xyzzy"

2.4 Failed execution

1 when I try to run false
2 then exit code is not 0
3 then command fails

2.5 Failed execution in a sub-directory

1 given a directory xyzzy
2 when I try to run, in xyzzy, false
3 then exit code is not 0
4 then command fails

3 Check we can prepend to $PATH

This scenario verifies that we can add a directory to the beginning of the PATH environment variable, so that we can have runcmd invoke a binary from our build tree rather than from system directories. This is especially useful for testing new versions of software that's already installed on the system.

1 given executable file ls from ls.sh
2 when I prepend . to PATH
3 when I run ls
4 then command is successful
5 then stdout contains "custom ls, not system ls"
#!/bin/sh
echo "custom ls, not system ls"

4 Check output has what we want

These scenarios verify that stdout or stderr do have something we want to have.

4.1 Check stdout is exactly as wanted

Note that the string is surrounded by double quotes to make it clear to the reader what's inside. Also, C-style string escapes are understood.

1 when I run echo hello, world
2 then stdout is exactly "hello, world "

4.2 Check stderr is exactly as wanted

1 given helper script err.sh for runcmd
2 when I run sh err.sh hello, world
3 then stderr is exactly "hello, world "

Exact string comparisons are not always enough, so we can verify a sub-string is in output.

1 when I run echo hello, world
2 then stdout contains "world "
3 then exit code is 0
1 given helper script err.sh for runcmd
2 when I run sh err.sh hello, world
3 then stderr contains "world "

4.5 Check stdout using regular expressions

Fixed strings are not always enough, so we can verify output matches a regular expression. Note that the regular expression is not delimited and does not get any C-style string escaped decoded.

1 when I run echo hello, world
2 then stdout matches regex world$

4.6 Check stderr using regular expressions

1 given helper script err.sh for runcmd
2 when I run sh err.sh hello, world
3 then stderr matches regex world$

5 Check output doesn't have what we want to avoid

These scenarios verify that the stdout or stderr do not have something we want to avoid.

5.1 Check stdout is not exactly something

1 when I run echo hi
2 then stdout isn't exactly "hello, world "

5.2 Check stderr is not exactly something

1 given helper script err.sh for runcmd
2 when I run sh err.sh hi
3 then stderr isn't exactly "hello, world "

5.3 Check stdout doesn't contain sub-string

1 when I run echo hi
2 then stdout doesn't contain "world"

5.4 Check stderr doesn't contain sub-string

1 given helper script err.sh for runcmd
2 when I run sh err.sh hi
3 then stderr doesn't contain "world"

5.5 Check stdout doesn't match regular expression

1 when I run echo hi
2 then stdout doesn't match regex world$

5.6 Check stderr doesn't match regular expressions

1 given helper script err.sh for runcmd
2 when I run sh err.sh hi
3 then stderr doesn't match regex world$

5.7 Setting and clearing of environment variables

A default scenario environment won't contain extra values unless they're passed in deliberately with SUBPLOT_ENV_* names.

1 when I run /usr/bin/env
2 then stdout doesn't contain "FOO=bar"

We can set variables to ensure they end up set

1 given the environment variable FOO=bar
2 when I run /usr/bin/env
3 then stdout contains "FOO=bar"

And they will remain set until the scenario finishes

1 when I run /usr/bin/env
2 then stdout contains "FOO=bar"

Unless we clear the variable explicitly

1 given the unset environment variable FOO
2 when I run /usr/bin/env
3 then stdout doesn't contain "FOO=bar"

6 Check redirection

6.1 Stdin comes from a named file

1 given file hello.txt
2 when I redirect stdin from hello.txt
3 when I run cat
4 then stdout is exactly "hello, world "
hello, world

6.2 Stdin contains exactly

1 when I send exactly "hello, there" to stdin
2 when I run cat
3 then stdout is exactly "hello, there"