aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/internal/script/scripttest
diff options
context:
space:
mode:
authorThan McIntosh <thanm@google.com>2024-07-30 14:20:53 +0000
committerThan McIntosh <thanm@google.com>2024-07-31 13:21:20 +0000
commit8246171baee397165318638390c37e07b5b3ebc7 (patch)
treee8b680035b2347524fce388ecd916729cd107117 /src/cmd/internal/script/scripttest
parentb60f88d81022e4172e44aef2f0bdade87ed6916d (diff)
downloadgo-8246171baee397165318638390c37e07b5b3ebc7.tar.xz
cmd: add README generation for compiler + linker script tests
Add in automatic README generation and README consistency checking for the cmd/compile and cmd/link script tests. This code is adapted from the similar facility in cmd/go (e.g. scriptreadme_test.go); the README helps folks writing new tests understand the mechanics. Updates #68606. Change-Id: I8ff7ff8e814abd4385bd670440511b2c60a4cef6 Reviewed-on: https://go-review.googlesource.com/c/go/+/601756 Reviewed-by: Cherry Mui <cherryyz@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Diffstat (limited to 'src/cmd/internal/script/scripttest')
-rw-r--r--src/cmd/internal/script/scripttest/readme.go142
-rw-r--r--src/cmd/internal/script/scripttest/run.go23
2 files changed, 162 insertions, 3 deletions
diff --git a/src/cmd/internal/script/scripttest/readme.go b/src/cmd/internal/script/scripttest/readme.go
new file mode 100644
index 0000000000..af7397223f
--- /dev/null
+++ b/src/cmd/internal/script/scripttest/readme.go
@@ -0,0 +1,142 @@
+// Copyright 2024 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 scripttest
+
+import (
+ "bytes"
+ "cmd/internal/script"
+ "internal/diff"
+ "internal/testenv"
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+ "text/template"
+)
+
+func checkScriptReadme(t *testing.T, engine *script.Engine, env []string, scriptspath, gotool string, fixReadme bool) {
+ 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, gotool, "doc", "cmd/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)
+ }
+
+ readmePath := filepath.Join(scriptspath, "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'. DO NOT EDIT.
+
+This directory holds test scripts *.txt run during 'go test cmd/<toolname>'.
+To run a specific script foo.txt
+
+ go test cmd/<toolname> -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 (build, link, compile, ...) 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>
+ GOOS=<target GOOS>
+ TMPDIR=$WORK/tmp
+ 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 .../cmd/internal/script/scripttest/run.go (not the system shell).
+
+{{.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.
+
+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 available commands are:
+{{.Commands}}
+
+The available conditions are:
+{{.Conditions}}
+`
diff --git a/src/cmd/internal/script/scripttest/run.go b/src/cmd/internal/script/scripttest/run.go
index 29eb6f88f2..8dff13e22e 100644
--- a/src/cmd/internal/script/scripttest/run.go
+++ b/src/cmd/internal/script/scripttest/run.go
@@ -34,7 +34,7 @@ type ToolReplacement struct {
// is that we'll be called from the top level cmd/X dir for tool X,
// and that instead of executing the install tool X we'll use the
// test binary instead.
-func RunToolScriptTest(t *testing.T, repls []ToolReplacement, pattern string) {
+func RunToolScriptTest(t *testing.T, repls []ToolReplacement, scriptsdir string, fixReadme bool) {
// Nearly all script tests involve doing builds, so don't
// bother here if we don't have "go build".
testenv.MustHaveGoBuild(t)
@@ -125,9 +125,9 @@ func RunToolScriptTest(t *testing.T, repls []ToolReplacement, pattern string) {
// Add in commands for "go" and "cc".
testgo := filepath.Join(tgr, "bin", "go")
gocmd := script.Program(testgo, interrupt, gracePeriod)
- cccmd := script.Program(goEnv("CC"), interrupt, gracePeriod)
addcmd("go", gocmd)
- addcmd("cc", cccmd)
+ cmdExec := cmds["exec"]
+ addcmd("cc", scriptCC(cmdExec, goEnv("CC")))
// Add various helpful conditions related to builds and toolchain use.
goHostOS, goHostArch := goEnv("GOHOSTOS"), goEnv("GOHOSTARCH")
@@ -153,8 +153,13 @@ func RunToolScriptTest(t *testing.T, repls []ToolReplacement, pattern string) {
Quiet: !testing.Verbose(),
}
+ t.Run("README", func(t *testing.T) {
+ checkScriptReadme(t, engine, env, scriptsdir, gotool, fixReadme)
+ })
+
// ... and kick off tests.
ctx := context.Background()
+ pattern := filepath.Join(scriptsdir, "*.txt")
RunTests(t, ctx, engine, env, pattern)
}
@@ -253,3 +258,15 @@ func tempEnvName() string {
return "TMPDIR"
}
}
+
+// scriptCC runs the platform C compiler.
+func scriptCC(cmdExec script.Cmd, ccexe string) script.Cmd {
+ return script.Command(
+ script.CmdUsage{
+ Summary: "run the platform C compiler",
+ Args: "args...",
+ },
+ func(s *script.State, args ...string) (script.WaitFunc, error) {
+ return cmdExec.Run(s, append([]string{ccexe}, args...)...)
+ })
+}