From 24c64e725dba574088acf1dc892bf5fae970160b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Sun, 24 Jul 2022 13:30:43 +0200 Subject: [PATCH] testscript: fix RequireExplicitExec error messages 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. --- testscript/cmd.go | 35 ++- testscript/doc.go | 217 +++++++++--------- testscript/exe.go | 2 +- testscript/testdata/cpstdout.txt | 9 +- .../testdata/testscript_explicit_exec.txt | 4 +- 5 files changed, 145 insertions(+), 122 deletions(-) diff --git a/testscript/cmd.go b/testscript/cmd.go index 59f12e63..fff16c47 100644 --- a/testscript/cmd.go +++ b/testscript/cmd.go @@ -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, @@ -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) @@ -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 { @@ -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 { diff --git a/testscript/doc.go b/testscript/doc.go index 5cee2023..ed6062d4 100644 --- a/testscript/doc.go +++ b/testscript/doc.go @@ -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, @@ -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) diff --git a/testscript/exe.go b/testscript/exe.go index 3c3579e7..e163a769 100644 --- a/testscript/exe.go +++ b/testscript/exe.go @@ -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...)) } diff --git a/testscript/testdata/cpstdout.txt b/testscript/testdata/cpstdout.txt index 9903043d..75c7812f 100644 --- a/testscript/testdata/cpstdout.txt +++ b/testscript/testdata/cpstdout.txt @@ -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 diff --git a/testscript/testdata/testscript_explicit_exec.txt b/testscript/testdata/testscript_explicit_exec.txt index c2fc0a6a..cc818696 100644 --- a/testscript/testdata/testscript_explicit_exec.txt +++ b/testscript/testdata/testscript_explicit_exec.txt @@ -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