diff options
| author | Than McIntosh <thanm@google.com> | 2024-07-30 14:20:53 +0000 |
|---|---|---|
| committer | Than McIntosh <thanm@google.com> | 2024-07-31 13:21:20 +0000 |
| commit | 8246171baee397165318638390c37e07b5b3ebc7 (patch) | |
| tree | e8b680035b2347524fce388ecd916729cd107117 /src/cmd/internal/script/scripttest | |
| parent | b60f88d81022e4172e44aef2f0bdade87ed6916d (diff) | |
| download | go-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.go | 142 | ||||
| -rw-r--r-- | src/cmd/internal/script/scripttest/run.go | 23 |
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...)...) + }) +} |
