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: add Params.RequireExplicitExec #164

Merged
merged 1 commit into from Jul 6, 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
1 change: 0 additions & 1 deletion testscript/cmd.go
Expand Up @@ -24,7 +24,6 @@ import (
// Keep list and the implementations below sorted by name.
//
// NOTE: If you make changes here, update doc.go.
//
var scriptCmds = map[string]func(*TestScript, bool, []string){
"cd": (*TestScript).cmdCd,
"chmod": (*TestScript).cmdChmod,
Expand Down
15 changes: 10 additions & 5 deletions testscript/exe.go
Expand Up @@ -42,11 +42,13 @@ func IgnoreMissedCoverage() {
// code to pass to os.Exit. It's OK for a command function to
// exit itself, but this may result in loss of coverage information.
//
// When Run is called, these commands will be available as
// testscript commands; note that these commands behave like
// commands run with the "exec" command: they set stdout
// and stderr, and can be run in the background by passing "&"
// as a final argument.
// When Run is called, these commands are installed as regular commands in the shell
// path, so can be invoked with "exec" or via any other command (for example a shell script).
//
// For backwards compatibility, the commands declared in the map can be run
// without "exec" - that is, "foo" will behave like "exec foo".
// This can be disabled with Params.RequireExplicitExec to keep consistency
// across test scripts, and to keep separate process executions explicit.
//
// This function returns an exit code to pass to os.Exit, after calling m.Run.
func RunMain(m TestingM, commands map[string]func() int) (exitCode int) {
Expand Down Expand Up @@ -119,6 +121,9 @@ func RunMain(m TestingM, commands map[string]func() int) (exitCode int) {
return 2
}
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.cmdExec(neg, append([]string{name}, args...))
}
}
Expand Down
32 changes: 32 additions & 0 deletions testscript/testdata/testscript_explicit_exec.txt
@@ -0,0 +1,32 @@
# Check that RequireExplicitExec works;
# it should reject `fprintargs` in favor of `exec fprintargs`,
# but it shouldn't complain about `some-param-cmd`,
# as that Params.Cmds entry won't work via `exec some-param-cmd`.

unquote scripts-implicit/testscript.txt
unquote scripts-explicit/testscript.txt

testscript scripts-implicit
testscript scripts-explicit

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

-- scripts-implicit/testscript.txt --
>fprintargs stdout right
>cmp stdout expect
>
>some-param-cmd
>! exec some-param-cmd
>
>-- expect --
>right
-- scripts-explicit/testscript.txt --
>exec fprintargs stdout right
>cmp stdout expect
>
>some-param-cmd
>! exec some-param-cmd
>
>-- expect --
>right
6 changes: 6 additions & 0 deletions testscript/testscript.go
Expand Up @@ -155,6 +155,12 @@ type Params struct {
// a manual change will be needed if it is not unquoted in the
// script.
UpdateScripts bool

// RequireExplicitExec requires that commands passed to RunMain must be used
// in test scripts via `exec cmd` and not simply `cmd`. This can help keep
// consistency across test scripts as well as keep separate process
// executions explicit.
RequireExplicitExec bool
}

// RunDir runs the tests in the given directory. All files in dir with a ".txt"
Expand Down
12 changes: 9 additions & 3 deletions testscript/testscript_test.go
Expand Up @@ -182,12 +182,13 @@ func TestScripts(t *testing.T) {
// Run testscript in testscript. Oooh! Meta!
fset := flag.NewFlagSet("testscript", flag.ContinueOnError)
fUpdate := fset.Bool("update", false, "update scripts when cmp fails")
fExplicitExec := fset.Bool("explicit-exec", false, "require explicit use of exec for commands")
fVerbose := fset.Bool("verbose", false, "be verbose with output")
if err := fset.Parse(args); err != nil {
ts.Fatalf("failed to parse args for testscript: %v", err)
}
if fset.NArg() != 1 {
ts.Fatalf("testscript [-verbose] [-update] <dir>")
ts.Fatalf("testscript [-verbose] [-update] [-explicit-exec] <dir>")
}
dir := fset.Arg(0)
t := &fakeT{ts: ts, verbose: *fVerbose}
Expand All @@ -200,8 +201,13 @@ func TestScripts(t *testing.T) {
}
}()
RunT(t, Params{
Dir: ts.MkAbs(dir),
UpdateScripts: *fUpdate,
Dir: ts.MkAbs(dir),
UpdateScripts: *fUpdate,
RequireExplicitExec: *fExplicitExec,
Cmds: map[string]func(ts *TestScript, neg bool, args []string){
"some-param-cmd": func(ts *TestScript, neg bool, args []string) {
},
},
})
}()
ts.stdout = strings.Replace(t.log.String(), ts.workdir, "$WORK", -1)
Expand Down