Skip to content

Commit

Permalink
testscript: fix RequireExplicitExec error messages
Browse files Browse the repository at this point in the history
They used args[0], which is the first argument to the command,
rather than the command name itself:

	> gofumpt foo.go
	FAIL: [...] use 'exec foo.go' rather than 'foo.go' (because RequireExplicitExec is enabled)

I believe I introduced this regression when refactoring the pull request
due to Roger's review, as I moved the code out of TestMain, where
args[0] was still the name of the command to be run.

Note that I can't easily add a regression test here, because the
testscript command being used in the tests is not a top-level command
run as a separate process, so it does not produce stdout nor stderr.
The change seems easy enough to review manually,
and our tests don't strictly check all error messages anyway.

While here, remove the unnecessary use of -verbose.
  • Loading branch information
mvdan committed Aug 3, 2022
1 parent 2431384 commit 24c64e7
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 122 deletions.
35 changes: 23 additions & 12 deletions testscript/cmd.go
Expand Up @@ -35,6 +35,7 @@ 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,
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.
- 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
- mv path1 path2
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.
- 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.
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

0 comments on commit 24c64e7

Please sign in to comment.