Skip to content

Commit fdc03d5

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 fdc03d5

5 files changed

Lines changed: 68 additions & 8 deletions

File tree

testscript/cmd.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,15 @@ var scriptCmds = map[string]func(*TestScript, bool, []string){
4848
"wait": (*TestScript).cmdWait,
4949
}
5050

51+
var builtinCmds map[string]bool
52+
53+
func init() {
54+
builtinCmds = make(map[string]bool, len(scriptCmds))
55+
for name := range scriptCmds {
56+
builtinCmds[name] = true
57+
}
58+
}
59+
5160
// cd changes to a different directory.
5261
func (ts *TestScript) cmdCd(neg bool, args []string) {
5362
if neg {

testscript/exe.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,15 @@ 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 will work when run via "exec".
46+
// For example, a map entry with key "foo" will be available as "exec foo".
47+
// Just like any other "exec" command, they set stdout and stderr, and can be
48+
// run in the background by passing "&" as a final argument.
49+
//
50+
// For backwards compatibility, the commands declared in the map can be run
51+
// without "exec" - that is, "foo" will behave like "exec foo".
52+
// This can be disabled with Params.RequireExplicitExec to keep consistency
53+
// across test scripts, and to keep separate process executions explicit.
5054
//
5155
// This function returns an exit code to pass to os.Exit, after calling m.Run.
5256
func RunMain(m TestingM, commands map[string]func() int) (exitCode int) {
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: 9 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"
@@ -512,6 +518,9 @@ Script:
512518

513519
// Run command.
514520
cmd := scriptCmds[args[0]]
521+
if cmd != nil && !builtinCmds[args[0]] && ts.params.RequireExplicitExec {
522+
ts.Fatalf("use 'exec %s' rather than '%s' (as per RequireExplicitExec)", args[0], args[0])
523+
}
515524
if cmd == nil {
516525
cmd = ts.params.Cmds[args[0]]
517526
}

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)