Source file src/cmd/internal/script/scripttest/readme.go

     1  // Copyright 2024 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package scripttest
     6  
     7  import (
     8  	"bytes"
     9  	"cmd/internal/script"
    10  	"internal/diff"
    11  	"internal/testenv"
    12  	"os"
    13  	"path/filepath"
    14  	"strings"
    15  	"testing"
    16  	"text/template"
    17  )
    18  
    19  func checkScriptReadme(t *testing.T, engine *script.Engine, env []string, scriptspath, gotool string, fixReadme bool) {
    20  	var args struct {
    21  		Language   string
    22  		Commands   string
    23  		Conditions string
    24  	}
    25  
    26  	cmds := new(strings.Builder)
    27  	if err := engine.ListCmds(cmds, true); err != nil {
    28  		t.Fatal(err)
    29  	}
    30  	args.Commands = cmds.String()
    31  
    32  	conds := new(strings.Builder)
    33  	if err := engine.ListConds(conds, nil); err != nil {
    34  		t.Fatal(err)
    35  	}
    36  	args.Conditions = conds.String()
    37  
    38  	doc := new(strings.Builder)
    39  	cmd := testenv.Command(t, gotool, "doc", "cmd/internal/script")
    40  	cmd.Env = env
    41  	cmd.Stdout = doc
    42  	if err := cmd.Run(); err != nil {
    43  		t.Fatal(cmd, ":", err)
    44  	}
    45  	_, lang, ok := strings.Cut(doc.String(), "# Script Language\n\n")
    46  	if !ok {
    47  		t.Fatalf("%q did not include Script Language section", cmd)
    48  	}
    49  	lang, _, ok = strings.Cut(lang, "\n\nvar ")
    50  	if !ok {
    51  		t.Fatalf("%q did not include vars after Script Language section", cmd)
    52  	}
    53  	args.Language = lang
    54  
    55  	tmpl := template.Must(template.New("README").Parse(readmeTmpl[1:]))
    56  	buf := new(bytes.Buffer)
    57  	if err := tmpl.Execute(buf, args); err != nil {
    58  		t.Fatal(err)
    59  	}
    60  
    61  	readmePath := filepath.Join(scriptspath, "README")
    62  	old, err := os.ReadFile(readmePath)
    63  	if err != nil {
    64  		t.Fatal(err)
    65  	}
    66  	diff := diff.Diff(readmePath, old, "readmeTmpl", buf.Bytes())
    67  	if diff == nil {
    68  		t.Logf("%s is up to date.", readmePath)
    69  		return
    70  	}
    71  
    72  	if fixReadme {
    73  		if err := os.WriteFile(readmePath, buf.Bytes(), 0666); err != nil {
    74  			t.Fatal(err)
    75  		}
    76  		t.Logf("wrote %d bytes to %s", buf.Len(), readmePath)
    77  	} else {
    78  		t.Logf("\n%s", diff)
    79  		t.Errorf("%s is stale. To update, run 'go generate cmd/go'.", readmePath)
    80  	}
    81  }
    82  
    83  const readmeTmpl = `
    84  This file is generated by 'go generate'. DO NOT EDIT.
    85  
    86  This directory holds test scripts *.txt run during 'go test cmd/<toolname>'.
    87  To run a specific script foo.txt
    88  
    89  	go test cmd/<toolname> -run=Script/^foo$
    90  
    91  In general script files should have short names: a few words,
    92   not whole sentences.
    93  The first word should be the general category of behavior being tested,
    94  often the name of a go subcommand (build, link, compile, ...) or concept (vendor, pattern).
    95  
    96  Each script is a text archive (go doc internal/txtar).
    97  The script begins with an actual command script to run
    98  followed by the content of zero or more supporting files to
    99  create in the script's temporary file system before it starts executing.
   100  
   101  As an example, run_hello.txt says:
   102  
   103  	# hello world
   104  	go run hello.go
   105  	stderr 'hello world'
   106  	! stdout .
   107  
   108  	-- hello.go --
   109  	package main
   110  	func main() { println("hello world") }
   111  
   112  Each script runs in a fresh temporary work directory tree, available to scripts as $WORK.
   113  Scripts also have access to other environment variables, including:
   114  
   115  	GOARCH=<target GOARCH>
   116  	GOOS=<target GOOS>
   117  	TMPDIR=$WORK/tmp
   118  	devnull=<value of os.DevNull>
   119  	goversion=<current Go version; for example, 1.12>
   120  
   121  On Plan 9, the variables $path and $home are set instead of $PATH and $HOME.
   122  On Windows, the variables $USERPROFILE and $TMP are set instead of
   123  $HOME and $TMPDIR.
   124  
   125  The lines at the top of the script are a sequence of commands to be executed by
   126  a small script engine configured in .../cmd/internal/script/scripttest/run.go (not the system shell).
   127  
   128  {{.Language}}
   129  
   130  When TestScript runs a script and the script fails, by default TestScript shows
   131  the execution of the most recent phase of the script (since the last # comment)
   132  and only shows the # comments for earlier phases.
   133  
   134  Note also that in reported output, the actual name of the per-script temporary directory
   135  has been consistently replaced with the literal string $WORK.
   136  
   137  The available commands are:
   138  {{.Commands}}
   139  
   140  The available conditions are:
   141  {{.Conditions}}
   142  `
   143  

View as plain text