Skip to content

Commit 9d15b66

Browse files
committed
testscript: add Params.RequireExplicitExec
We also document how top-level commands fed to RunMain work with and without "exec" the same way, and how RequireExplicitExec can drop backwards compatibility for greater consistency. Fixes #163.
1 parent af73bbc commit 9d15b66

File tree

5 files changed

+57
-9
lines changed

5 files changed

+57
-9
lines changed

testscript/cmd.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import (
2424
// Keep list and the implementations below sorted by name.
2525
//
2626
// NOTE: If you make changes here, update doc.go.
27-
//
2827
var scriptCmds = map[string]func(*TestScript, bool, []string){
2928
"cd": (*TestScript).cmdCd,
3029
"chmod": (*TestScript).cmdChmod,

testscript/exe.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,13 @@ func IgnoreMissedCoverage() {
4242
// code to pass to os.Exit. It's OK for a command function to
4343
// exit itself, but this may result in loss of coverage information.
4444
//
45-
// When Run is called, these commands will be available as
46-
// testscript commands; note that these commands behave like
47-
// commands run with the "exec" command: they set stdout
48-
// and stderr, and can be run in the background by passing "&"
49-
// as a final argument.
45+
// When Run is called, these commands are installed as regular commands in the shell
46+
// path, so can be invoked with "exec" or via any other command (for example a shell script).
47+
//
48+
// For backwards compatibility, the commands declared in the map can be run
49+
// without "exec" - that is, "foo" will behave like "exec foo".
50+
// This can be disabled with Params.RequireExplicitExec to keep consistency
51+
// across test scripts, and to keep separate process executions explicit.
5052
//
5153
// This function returns an exit code to pass to os.Exit, after calling m.Run.
5254
func RunMain(m TestingM, commands map[string]func() int) (exitCode int) {
@@ -119,6 +121,9 @@ func RunMain(m TestingM, commands map[string]func() int) (exitCode int) {
119121
return 2
120122
}
121123
scriptCmds[name] = func(ts *TestScript, neg bool, args []string) {
124+
if ts.params.RequireExplicitExec {
125+
ts.Fatalf("use 'exec %s' rather than '%s' (because RequireExplicitExec is enabled)", args[0], args[0])
126+
}
122127
ts.cmdExec(neg, append([]string{name}, args...))
123128
}
124129
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Check that RequireExplicitExec works;
2+
# it should reject `fprintargs` in favor of `exec fprintargs`,
3+
# but it shouldn't complain about `some-param-cmd`,
4+
# as that Params.Cmds entry won't work via `exec some-param-cmd`.
5+
6+
unquote scripts-implicit/testscript.txt
7+
unquote scripts-explicit/testscript.txt
8+
9+
testscript scripts-implicit
10+
testscript scripts-explicit
11+
12+
! testscript -verbose -explicit-exec scripts-implicit
13+
testscript -verbose -explicit-exec scripts-explicit
14+
15+
-- scripts-implicit/testscript.txt --
16+
>fprintargs stdout right
17+
>cmp stdout expect
18+
>
19+
>some-param-cmd
20+
>! exec some-param-cmd
21+
>
22+
>-- expect --
23+
>right
24+
-- scripts-explicit/testscript.txt --
25+
>exec fprintargs stdout right
26+
>cmp stdout expect
27+
>
28+
>some-param-cmd
29+
>! exec some-param-cmd
30+
>
31+
>-- expect --
32+
>right

testscript/testscript.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,12 @@ type Params struct {
155155
// a manual change will be needed if it is not unquoted in the
156156
// script.
157157
UpdateScripts bool
158+
159+
// RequireExplicitExec requires that commands passed to RunMain must be used
160+
// in test scripts via `exec cmd` and not simply `cmd`. This can help keep
161+
// consistency across test scripts as well as keep separate process
162+
// executions explicit.
163+
RequireExplicitExec bool
158164
}
159165

160166
// RunDir runs the tests in the given directory. All files in dir with a ".txt"

testscript/testscript_test.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -182,12 +182,13 @@ func TestScripts(t *testing.T) {
182182
// Run testscript in testscript. Oooh! Meta!
183183
fset := flag.NewFlagSet("testscript", flag.ContinueOnError)
184184
fUpdate := fset.Bool("update", false, "update scripts when cmp fails")
185+
fExplicitExec := fset.Bool("explicit-exec", false, "require explicit use of exec for commands")
185186
fVerbose := fset.Bool("verbose", false, "be verbose with output")
186187
if err := fset.Parse(args); err != nil {
187188
ts.Fatalf("failed to parse args for testscript: %v", err)
188189
}
189190
if fset.NArg() != 1 {
190-
ts.Fatalf("testscript [-verbose] [-update] <dir>")
191+
ts.Fatalf("testscript [-verbose] [-update] [-explicit-exec] <dir>")
191192
}
192193
dir := fset.Arg(0)
193194
t := &fakeT{ts: ts, verbose: *fVerbose}
@@ -200,8 +201,13 @@ func TestScripts(t *testing.T) {
200201
}
201202
}()
202203
RunT(t, Params{
203-
Dir: ts.MkAbs(dir),
204-
UpdateScripts: *fUpdate,
204+
Dir: ts.MkAbs(dir),
205+
UpdateScripts: *fUpdate,
206+
RequireExplicitExec: *fExplicitExec,
207+
Cmds: map[string]func(ts *TestScript, neg bool, args []string){
208+
"some-param-cmd": func(ts *TestScript, neg bool, args []string) {
209+
},
210+
},
205211
})
206212
}()
207213
ts.stdout = strings.Replace(t.log.String(), ts.workdir, "$WORK", -1)

0 commit comments

Comments
 (0)