mirror of https://go.googlesource.com/go
267 lines
7.9 KiB
Go
267 lines
7.9 KiB
Go
// Copyright 2022 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package main_test
|
|
|
|
import (
|
|
"bytes"
|
|
"cmd/go/internal/script"
|
|
"flag"
|
|
"internal/diff"
|
|
"internal/testenv"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
"text/template"
|
|
)
|
|
|
|
var fixReadme = flag.Bool("fixreadme", false, "if true, update ../testdata/script/README")
|
|
|
|
func checkScriptReadme(t *testing.T, engine *script.Engine, env []string) {
|
|
var args struct {
|
|
Language string
|
|
Commands string
|
|
Conditions string
|
|
}
|
|
|
|
cmds := new(strings.Builder)
|
|
if err := engine.ListCmds(cmds, true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
args.Commands = cmds.String()
|
|
|
|
conds := new(strings.Builder)
|
|
if err := engine.ListConds(conds, nil); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
args.Conditions = conds.String()
|
|
|
|
doc := new(strings.Builder)
|
|
cmd := testenv.Command(t, testGo, "doc", "cmd/go/internal/script")
|
|
cmd.Env = env
|
|
cmd.Stdout = doc
|
|
if err := cmd.Run(); err != nil {
|
|
t.Fatal(cmd, ":", err)
|
|
}
|
|
_, lang, ok := strings.Cut(doc.String(), "# Script Language\n\n")
|
|
if !ok {
|
|
t.Fatalf("%q did not include Script Language section", cmd)
|
|
}
|
|
lang, _, ok = strings.Cut(lang, "\n\nvar ")
|
|
if !ok {
|
|
t.Fatalf("%q did not include vars after Script Language section", cmd)
|
|
}
|
|
args.Language = lang
|
|
|
|
tmpl := template.Must(template.New("README").Parse(readmeTmpl[1:]))
|
|
buf := new(bytes.Buffer)
|
|
if err := tmpl.Execute(buf, args); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
const readmePath = "testdata/script/README"
|
|
old, err := os.ReadFile(readmePath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
diff := diff.Diff(readmePath, old, "readmeTmpl", buf.Bytes())
|
|
if diff == nil {
|
|
t.Logf("%s is up to date.", readmePath)
|
|
return
|
|
}
|
|
|
|
if *fixReadme {
|
|
if err := os.WriteFile(readmePath, buf.Bytes(), 0666); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Logf("wrote %d bytes to %s", buf.Len(), readmePath)
|
|
} else {
|
|
t.Logf("\n%s", diff)
|
|
t.Errorf("%s is stale. To update, run 'go generate cmd/go'.", readmePath)
|
|
}
|
|
}
|
|
|
|
const readmeTmpl = `
|
|
This file is generated by 'go generate cmd/go'. DO NOT EDIT.
|
|
|
|
This directory holds test scripts *.txt run during 'go test cmd/go'.
|
|
To run a specific script foo.txt
|
|
|
|
go test cmd/go -run=Script/^foo$
|
|
|
|
In general script files should have short names: a few words, not whole sentences.
|
|
The first word should be the general category of behavior being tested,
|
|
often the name of a go subcommand (list, build, test, ...) or concept (vendor, pattern).
|
|
|
|
Each script is a text archive (go doc internal/txtar).
|
|
The script begins with an actual command script to run
|
|
followed by the content of zero or more supporting files to
|
|
create in the script's temporary file system before it starts executing.
|
|
|
|
As an example, run_hello.txt says:
|
|
|
|
# hello world
|
|
go run hello.go
|
|
stderr 'hello world'
|
|
! stdout .
|
|
|
|
-- hello.go --
|
|
package main
|
|
func main() { println("hello world") }
|
|
|
|
Each script runs in a fresh temporary work directory tree, available to scripts as $WORK.
|
|
Scripts also have access to other environment variables, including:
|
|
|
|
GOARCH=<target GOARCH>
|
|
GOCACHE=<actual GOCACHE being used outside the test>
|
|
GOEXE=<executable file suffix: .exe on Windows, empty on other systems>
|
|
GOOS=<target GOOS>
|
|
GOPATH=$WORK/gopath
|
|
GOPROXY=<local module proxy serving from cmd/go/testdata/mod>
|
|
GOROOT=<actual GOROOT>
|
|
TESTGO_GOROOT=<GOROOT used to build cmd/go, for use in tests that may change GOROOT>
|
|
HOME=/no-home
|
|
PATH=<actual PATH>
|
|
TMPDIR=$WORK/tmp
|
|
GODEBUG=<actual GODEBUG>
|
|
devnull=<value of os.DevNull>
|
|
goversion=<current Go version; for example, 1.12>
|
|
|
|
On Plan 9, the variables $path and $home are set instead of $PATH and $HOME.
|
|
On Windows, the variables $USERPROFILE and $TMP are set instead of
|
|
$HOME and $TMPDIR.
|
|
|
|
The lines at the top of the script are a sequence of commands to be executed by
|
|
a small script engine configured in ../../script_test.go (not the system shell).
|
|
|
|
The scripts' supporting files are unpacked relative to $GOPATH/src
|
|
(aka $WORK/gopath/src) and then the script begins execution in that directory as
|
|
well. Thus the example above runs in $WORK/gopath/src with GOPATH=$WORK/gopath
|
|
and $WORK/gopath/src/hello.go containing the listed contents.
|
|
|
|
{{.Language}}
|
|
|
|
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)
|
|
and only shows the # comments for earlier phases. For example, here is a
|
|
multi-phase script with a bug in it:
|
|
|
|
# GOPATH with p1 in d2, p2 in d2
|
|
env GOPATH=$WORK${/}d1${:}$WORK${/}d2
|
|
|
|
# build & install p1
|
|
env
|
|
go install -i p1
|
|
! stale p1
|
|
! stale p2
|
|
|
|
# modify p2 - p1 should appear stale
|
|
cp $WORK/p2x.go $WORK/d2/src/p2/p2.go
|
|
stale p1 p2
|
|
|
|
# build & install p1 again
|
|
go install -i p11
|
|
! stale p1
|
|
! stale p2
|
|
|
|
-- $WORK/d1/src/p1/p1.go --
|
|
package p1
|
|
import "p2"
|
|
func F() { p2.F() }
|
|
-- $WORK/d2/src/p2/p2.go --
|
|
package p2
|
|
func F() {}
|
|
-- $WORK/p2x.go --
|
|
package p2
|
|
func F() {}
|
|
func G() {}
|
|
|
|
The bug is that the final phase installs p11 instead of p1. The test failure looks like:
|
|
|
|
$ go test -run=Script
|
|
--- FAIL: TestScript (3.75s)
|
|
--- FAIL: TestScript/install_rebuild_gopath (0.16s)
|
|
script_test.go:223:
|
|
# GOPATH with p1 in d2, p2 in d2 (0.000s)
|
|
# build & install p1 (0.087s)
|
|
# modify p2 - p1 should appear stale (0.029s)
|
|
# build & install p1 again (0.022s)
|
|
> go install -i p11
|
|
[stderr]
|
|
can't load package: package p11: cannot find package "p11" in any of:
|
|
/Users/rsc/go/src/p11 (from $GOROOT)
|
|
$WORK/d1/src/p11 (from $GOPATH)
|
|
$WORK/d2/src/p11
|
|
[exit status 1]
|
|
FAIL: unexpected go command failure
|
|
|
|
script_test.go:73: failed at testdata/script/install_rebuild_gopath.txt:15 in $WORK/gopath/src
|
|
|
|
FAIL
|
|
exit status 1
|
|
FAIL cmd/go 4.875s
|
|
$
|
|
|
|
Note that the commands in earlier phases have been hidden, so that the relevant
|
|
commands are more easily found, and the elapsed time for a completed phase
|
|
is shown next to the phase heading. To see the entire execution, use "go test -v",
|
|
which also adds an initial environment dump to the beginning of the log.
|
|
|
|
Note also that in reported output, the actual name of the per-script temporary directory
|
|
has been consistently replaced with the literal string $WORK.
|
|
|
|
The cmd/go test flag -testwork (which must appear on the "go test" command line after
|
|
standard test flags) causes each test to log the name of its $WORK directory and other
|
|
environment variable settings and also to leave that directory behind when it exits,
|
|
for manual debugging of failing tests:
|
|
|
|
$ go test -run=Script -work
|
|
--- FAIL: TestScript (3.75s)
|
|
--- FAIL: TestScript/install_rebuild_gopath (0.16s)
|
|
script_test.go:223:
|
|
WORK=/tmp/cmd-go-test-745953508/script-install_rebuild_gopath
|
|
GOARCH=
|
|
GOCACHE=/Users/rsc/Library/Caches/go-build
|
|
GOOS=
|
|
GOPATH=$WORK/gopath
|
|
GOROOT=/Users/rsc/go
|
|
HOME=/no-home
|
|
TMPDIR=$WORK/tmp
|
|
exe=
|
|
|
|
# GOPATH with p1 in d2, p2 in d2 (0.000s)
|
|
# build & install p1 (0.085s)
|
|
# modify p2 - p1 should appear stale (0.030s)
|
|
# build & install p1 again (0.019s)
|
|
> go install -i p11
|
|
[stderr]
|
|
can't load package: package p11: cannot find package "p11" in any of:
|
|
/Users/rsc/go/src/p11 (from $GOROOT)
|
|
$WORK/d1/src/p11 (from $GOPATH)
|
|
$WORK/d2/src/p11
|
|
[exit status 1]
|
|
FAIL: unexpected go command failure
|
|
|
|
script_test.go:73: failed at testdata/script/install_rebuild_gopath.txt:15 in $WORK/gopath/src
|
|
|
|
FAIL
|
|
exit status 1
|
|
FAIL cmd/go 4.875s
|
|
$
|
|
|
|
$ WORK=/tmp/cmd-go-test-745953508/script-install_rebuild_gopath
|
|
$ cd $WORK/d1/src/p1
|
|
$ cat p1.go
|
|
package p1
|
|
import "p2"
|
|
func F() { p2.F() }
|
|
$
|
|
|
|
The available commands are:
|
|
{{.Commands}}
|
|
|
|
The available conditions are:
|
|
{{.Conditions}}
|
|
`
|