Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

testscript: port "mv" and "! cmp" from upstream #168

Merged
merged 1 commit into from Aug 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
39 changes: 25 additions & 14 deletions testscript/cmd.go
Expand Up @@ -35,15 +35,16 @@ var scriptCmds = map[string]func(*TestScript, bool, []string){
"exists": (*TestScript).cmdExists,
"grep": (*TestScript).cmdGrep,
"mkdir": (*TestScript).cmdMkdir,
"mv": (*TestScript).cmdMv,
"rm": (*TestScript).cmdRm,
"unquote": (*TestScript).cmdUnquote,
"skip": (*TestScript).cmdSkip,
"stdin": (*TestScript).cmdStdin,
"stderr": (*TestScript).cmdStderr,
"stdin": (*TestScript).cmdStdin,
"stdout": (*TestScript).cmdStdout,
"stop": (*TestScript).cmdStop,
"symlink": (*TestScript).cmdSymlink,
"unix2dos": (*TestScript).cmdUNIX2DOS,
"unquote": (*TestScript).cmdUnquote,
"wait": (*TestScript).cmdWait,
}

Expand Down Expand Up @@ -95,29 +96,22 @@ func (ts *TestScript) cmdChmod(neg bool, args []string) {

// cmp compares two files.
func (ts *TestScript) cmdCmp(neg bool, args []string) {
if neg {
// It would be strange to say "this file can have any content except this precise byte sequence".
ts.Fatalf("unsupported: ! cmp")
}
if len(args) != 2 {
ts.Fatalf("usage: cmp file1 file2")
}

ts.doCmdCmp(args, false)
ts.doCmdCmp(neg, args, false)
}

// cmpenv compares two files with environment variable substitution.
func (ts *TestScript) cmdCmpenv(neg bool, args []string) {
if neg {
ts.Fatalf("unsupported: ! cmpenv")
}
if len(args) != 2 {
ts.Fatalf("usage: cmpenv file1 file2")
}
ts.doCmdCmp(args, true)
ts.doCmdCmp(neg, args, true)
}

func (ts *TestScript) doCmdCmp(args []string, env bool) {
func (ts *TestScript) doCmdCmp(neg bool, args []string, env bool) {
name1, name2 := args[0], args[1]
text1 := ts.ReadFile(name1)

Expand All @@ -128,8 +122,15 @@ func (ts *TestScript) doCmdCmp(args []string, env bool) {
if env {
text2 = ts.expand(text2)
}
if text1 == text2 {
return
eq := text1 == text2
if neg {
if eq {
ts.Fatalf("%s and %s do not differ", name1, name2)
}
return // they differ, as expected
}
if eq {
return // they are equal, as expected
}
if ts.params.UpdateScripts && !env {
if scriptFile, ok := ts.scriptFiles[absName2]; ok {
Expand Down Expand Up @@ -324,6 +325,16 @@ func (ts *TestScript) cmdMkdir(neg bool, args []string) {
}
}

func (ts *TestScript) cmdMv(neg bool, args []string) {
if neg {
ts.Fatalf("unsupported: ! mv")
}
if len(args) != 2 {
ts.Fatalf("usage: mv old new")
}
ts.Check(os.Rename(ts.MkAbs(args[0]), ts.MkAbs(args[1])))
}

// unquote unquotes files.
func (ts *TestScript) cmdUnquote(neg bool, args []string) {
if neg {
Expand Down
217 changes: 111 additions & 106 deletions testscript/doc.go
Expand Up @@ -100,16 +100,16 @@ commands support this prefix. They are indicated below by [!] in the synopsis.
The command prefix [cond] indicates that the command on the rest of the line
should only run when the condition is satisfied. The predefined conditions are:

- [short] for testing.Short()
- [net] for whether the external network can be used
- [link] for whether the OS has hard link support
- [symlink] for whether the OS has symbolic link support
- [exec:prog] for whether prog is available for execution (found by exec.LookPath)
- [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
- [unix] for whether the OS is Unix-like (that is, would match the 'unix' build
constraint)
- [short] for testing.Short()
- [net] for whether the external network can be used
- [link] for whether the OS has hard link support
- [symlink] for whether the OS has symbolic link support
- [exec:prog] for whether prog is available for execution (found by exec.LookPath)
- [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
- [unix] for whether the OS is Unix-like (that is, would match the 'unix' build
constraint)

Any known values of GOOS and GOARCH can also be used as conditions. They will be
satisfied if the target OS or architecture match the specified value. For example,
Expand All @@ -122,102 +122,107 @@ Additional conditions can be added by passing a function to Params.Condition.

The predefined commands are:

- cd dir
Change to the given directory for future commands.

- chmod perm path...
Change the permissions of the files or directories named by the path arguments
to the given octal mode (000 to 777).

- cmp file1 file2
Check that the named files have the same content.
By convention, file1 is the actual data and file2 the expected data.
File1 can be "stdout" or "stderr" to use the standard output or standard error
from the most recent exec or wait command.
(If the files have differing content, the failure prints a diff.)

- cmpenv file1 file2
Like cmp, but environment variables in file2 are substituted before the
comparison. For example, $GOOS is replaced by the target GOOS.

- cp src... dst
Copy the listed files to the target file or existing directory.
src can include "stdout" or "stderr" to use the standard output or standard error
from the most recent exec or go command.

- env [key=value...]
With no arguments, print the environment (useful for debugging).
Otherwise add the listed key=value pairs to the environment.

- [!] exec program [args...] [&]
Run the given executable program with the arguments.
It must (or must not) succeed.
Note that 'exec' does not terminate the script (unlike in Unix shells).

If the last token is '&', the program executes in the background. The standard
output and standard error of the previous command is cleared, but the output
of the background process is buffered — and checking of its exit status is
delayed — until the next call to 'wait', 'skip', or 'stop' or the end of the
test. At the end of the test, any remaining background processes are
terminated using os.Interrupt (if supported) or os.Kill.

If the last token is '&word&` (where "word" is alphanumeric), the
command runs in the background but has a name, and can be waited
for specifically by passing the word to 'wait'.

Standard input can be provided using the stdin command; this will be
cleared after exec has been called.

- [!] exists [-readonly] file...
Each of the listed files or directories must (or must not) exist.
If -readonly is given, the files or directories must be unwritable.

- [!] grep [-count=N] pattern file
The file's content must (or must not) match the regular expression pattern.
For positive matches, -count=N specifies an exact number of matches to require.

- mkdir path...
Create the listed directories, if they do not already exists.

- unquote file...
Rewrite each file by replacing any leading ">" characters from
each line. This enables a file to contain substrings that look like
txtar file markers.
See also https://godoc.org/github.com/rogpeppe/go-internal/txtar#Unquote

- rm file...
Remove the listed files or directories.

- skip [message]
Mark the test skipped, including the message if given.

- stdin file
Set the standard input for the next exec command to the contents of the given file.
File can be "stdout" or "stderr" to use the standard output or standard error
from the most recent exec or wait command.

- [!] stderr [-count=N] pattern
Apply the grep command (see above) to the standard error
from the most recent exec or wait command.

- [!] stdout [-count=N] pattern
Apply the grep command (see above) to the standard output
from the most recent exec or wait command.

- stop [message]
Stop the test early (marking it as passing), including the message if given.

- symlink file -> target
Create file as a symlink to target. The -> (like in ls -l output) is required.

- wait [command]
Wait for all 'exec' and 'go' commands started in the background (with the '&'
token) to exit, and display success or failure status for them.
After a call to wait, the 'stderr' and 'stdout' commands will apply to the
concatenation of the corresponding streams of the background commands,
in the order in which those commands were started.

If an argument is specified, it waits for just that command.
- cd dir
Change to the given directory for future commands.

- chmod perm path...
Change the permissions of the files or directories named by the path arguments
to the given octal mode (000 to 777).

- [!] cmp file1 file2
Check that the named files have (or do not have) the same content.
By convention, file1 is the actual data and file2 the expected data.
File1 can be "stdout" or "stderr" to use the standard output or standard error
from the most recent exec or wait command.
(If the files have differing content and the command is not negated,
the failure prints a diff.)

- [!] cmpenv file1 file2
Like cmp, but environment variables in file2 are substituted before the
comparison. For example, $GOOS is replaced by the target GOOS.

- cp src... dst
Copy the listed files to the target file or existing directory.
src can include "stdout" or "stderr" to use the standard output or standard error
from the most recent exec or go command.

- env [key=value...]
With no arguments, print the environment (useful for debugging).
Otherwise add the listed key=value pairs to the environment.

- [!] exec program [args...] [&]
Run the given executable program with the arguments.
It must (or must not) succeed.
Note that 'exec' does not terminate the script (unlike in Unix shells).

If the last token is '&', the program executes in the background. The standard
output and standard error of the previous command is cleared, but the output
of the background process is buffered — and checking of its exit status is
delayed — until the next call to 'wait', 'skip', or 'stop' or the end of the
test. At the end of the test, any remaining background processes are
terminated using os.Interrupt (if supported) or os.Kill.

If the last token is '&word&` (where "word" is alphanumeric), the
command runs in the background but has a name, and can be waited
for specifically by passing the word to 'wait'.

Standard input can be provided using the stdin command; this will be
cleared after exec has been called.

- [!] exists [-readonly] file...
Each of the listed files or directories must (or must not) exist.
If -readonly is given, the files or directories must be unwritable.

- [!] grep [-count=N] pattern file
The file's content must (or must not) match the regular expression pattern.
For positive matches, -count=N specifies an exact number of matches to require.

- mkdir path...
Create the listed directories, if they do not already exists.

- mv path1 path2
mvdan marked this conversation as resolved.
Show resolved Hide resolved
Rename path1 to path2. OS-specific restrictions may apply when path1 and path2
are in different directories.

- rm file...
Remove the listed files or directories.

- skip [message]
Mark the test skipped, including the message if given.

- [!] stderr [-count=N] pattern
Apply the grep command (see above) to the standard error
from the most recent exec or wait command.

- stdin file
Set the standard input for the next exec command to the contents of the given file.
File can be "stdout" or "stderr" to use the standard output or standard error
from the most recent exec or wait command.

- [!] stdout [-count=N] pattern
Apply the grep command (see above) to the standard output
from the most recent exec or wait command.

- stop [message]
Stop the test early (marking it as passing), including the message if given.

- symlink file -> target
Create file as a symlink to target. The -> (like in ls -l output) is required.

- unquote file...
Rewrite each file by replacing any leading ">" characters from
each line. This enables a file to contain substrings that look like
txtar file markers.
See also https://godoc.org/github.com/rogpeppe/go-internal/txtar#Unquote

- wait [command]
Wait for all 'exec' and 'go' commands started in the background (with the '&'
token) to exit, and display success or failure status for them.
After a call to wait, the 'stderr' and 'stdout' commands will apply to the
concatenation of the corresponding streams of the background commands,
in the order in which those commands were started.

If an argument is specified, it waits for just that command.

When TestScript runs a script and the script fails, by default TestScript shows
the execution of the most recent phase of the script (since the last # comment)
Expand Down
2 changes: 1 addition & 1 deletion testscript/exe.go
Expand Up @@ -122,7 +122,7 @@ func RunMain(m TestingM, commands map[string]func() int) (exitCode int) {
}
scriptCmds[name] = func(ts *TestScript, neg bool, args []string) {
if ts.params.RequireExplicitExec {
ts.Fatalf("use 'exec %s' rather than '%s' (because RequireExplicitExec is enabled)", args[0], args[0])
ts.Fatalf("use 'exec %s' rather than '%s' (because RequireExplicitExec is enabled)", name, name)
}
ts.cmdExec(neg, append([]string{name}, args...))
}
Expand Down
9 changes: 8 additions & 1 deletion testscript/testdata/cpstdout.txt
Expand Up @@ -4,7 +4,14 @@
exec cat hello.text
cp stdout got
cmp got hello.text
! cmp got different.text

exists got
mv got new
! exists got
cmp new hello.text

-- hello.text --
hello world

-- different.text --
goodbye
4 changes: 2 additions & 2 deletions testscript/testdata/testscript_explicit_exec.txt
Expand Up @@ -9,8 +9,8 @@ unquote scripts-explicit/testscript.txt
testscript scripts-implicit
testscript scripts-explicit

! testscript -verbose -explicit-exec scripts-implicit
testscript -verbose -explicit-exec scripts-explicit
! testscript -explicit-exec scripts-implicit
testscript -explicit-exec scripts-explicit

-- scripts-implicit/testscript.txt --
>fprintargs stdout right
Expand Down