Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 1 | // skip |
| 2 | |
Dan Willemsen | 0c15709 | 2016-07-08 13:57:52 -0700 | [diff] [blame] | 3 | // Copyright 2012 The Go Authors. All rights reserved. |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 4 | // Use of this source code is governed by a BSD-style |
| 5 | // license that can be found in the LICENSE file. |
| 6 | |
| 7 | // Run runs tests in the test directory. |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 8 | package main |
| 9 | |
| 10 | import ( |
| 11 | "bytes" |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 12 | "encoding/json" |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 13 | "errors" |
| 14 | "flag" |
| 15 | "fmt" |
Dan Willemsen | 31b9b84 | 2021-08-31 12:51:40 -0700 | [diff] [blame] | 16 | "go/build" |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 17 | "go/build/constraint" |
Dan Willemsen | 6ff2325 | 2015-09-15 13:49:18 -0700 | [diff] [blame] | 18 | "hash/fnv" |
| 19 | "io" |
Colin Cross | 846c316 | 2021-05-14 11:11:40 -0700 | [diff] [blame] | 20 | "io/fs" |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 21 | "io/ioutil" |
| 22 | "log" |
| 23 | "os" |
| 24 | "os/exec" |
| 25 | "path" |
| 26 | "path/filepath" |
| 27 | "regexp" |
| 28 | "runtime" |
| 29 | "sort" |
| 30 | "strconv" |
| 31 | "strings" |
| 32 | "time" |
| 33 | "unicode" |
| 34 | ) |
| 35 | |
| 36 | var ( |
| 37 | verbose = flag.Bool("v", false, "verbose. if set, parallelism is set to 1.") |
Dan Willemsen | 0c15709 | 2016-07-08 13:57:52 -0700 | [diff] [blame] | 38 | keep = flag.Bool("k", false, "keep. keep temporary directory.") |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 39 | numParallel = flag.Int("n", runtime.NumCPU(), "number of parallel tests to run") |
| 40 | summary = flag.Bool("summary", false, "show summary of results") |
Patrice Arruda | 7f4776e | 2020-06-25 11:55:41 -0700 | [diff] [blame] | 41 | allCodegen = flag.Bool("all_codegen", defaultAllCodeGen(), "run all goos/goarch for codegen") |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 42 | showSkips = flag.Bool("show_skips", false, "show skipped tests") |
Dan Willemsen | 0c15709 | 2016-07-08 13:57:52 -0700 | [diff] [blame] | 43 | runSkips = flag.Bool("run_skips", false, "run skipped tests (ignore skip and build tags)") |
| 44 | linkshared = flag.Bool("linkshared", false, "") |
Dan Willemsen | 6ff2325 | 2015-09-15 13:49:18 -0700 | [diff] [blame] | 45 | updateErrors = flag.Bool("update_errors", false, "update error messages in test file based on compiler output") |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 46 | runoutputLimit = flag.Int("l", defaultRunOutputLimit(), "number of parallel runoutput tests to run") |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 47 | force = flag.Bool("f", false, "ignore expected-failure test lists") |
Dan Willemsen | 6ff2325 | 2015-09-15 13:49:18 -0700 | [diff] [blame] | 48 | |
| 49 | shard = flag.Int("shard", 0, "shard index to run. Only applicable if -shards is non-zero.") |
| 50 | shards = flag.Int("shards", 0, "number of shards. If 0, all tests are run. This is used by the continuous build.") |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 51 | ) |
| 52 | |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 53 | type envVars struct { |
| 54 | GOOS string |
| 55 | GOARCH string |
| 56 | GOEXPERIMENT string |
| 57 | CGO_ENABLED string |
| 58 | } |
| 59 | |
| 60 | var env = func() (res envVars) { |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 61 | cmd := exec.Command(goTool(), "env", "-json") |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 62 | stdout, err := cmd.StdoutPipe() |
| 63 | if err != nil { |
| 64 | log.Fatal("StdoutPipe:", err) |
| 65 | } |
| 66 | if err := cmd.Start(); err != nil { |
| 67 | log.Fatal("Start:", err) |
| 68 | } |
| 69 | if err := json.NewDecoder(stdout).Decode(&res); err != nil { |
| 70 | log.Fatal("Decode:", err) |
| 71 | } |
| 72 | if err := cmd.Wait(); err != nil { |
| 73 | log.Fatal("Wait:", err) |
| 74 | } |
| 75 | return |
| 76 | }() |
| 77 | |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 78 | // TODO(mdempsky): This will give false negatives if the unified |
| 79 | // experiment is enabled by default, but presumably at that point we |
| 80 | // won't need to disable tests for it anymore anyway. |
| 81 | var unifiedEnabled = strings.Contains(","+env.GOEXPERIMENT+",", ",unified,") |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 82 | |
Patrice Arruda | 7f4776e | 2020-06-25 11:55:41 -0700 | [diff] [blame] | 83 | // defaultAllCodeGen returns the default value of the -all_codegen |
| 84 | // flag. By default, we prefer to be fast (returning false), except on |
| 85 | // the linux-amd64 builder that's already very fast, so we get more |
| 86 | // test coverage on trybots. See https://golang.org/issue/34297. |
| 87 | func defaultAllCodeGen() bool { |
| 88 | return os.Getenv("GO_BUILDER_NAME") == "linux-amd64" |
| 89 | } |
| 90 | |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 91 | var ( |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 92 | goos = env.GOOS |
| 93 | goarch = env.GOARCH |
| 94 | cgoEnabled, _ = strconv.ParseBool(env.CGO_ENABLED) |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 95 | |
| 96 | // dirs are the directories to look for *.go files in. |
| 97 | // TODO(bradfitz): just use all directories? |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 98 | dirs = []string{".", "ken", "chan", "interface", "syntax", "dwarf", "fixedbugs", "codegen", "runtime", "abi", "typeparam", "typeparam/mdempsky"} |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 99 | |
| 100 | // ratec controls the max number of tests running at a time. |
| 101 | ratec chan bool |
| 102 | |
| 103 | // toRun is the channel of tests to run. |
| 104 | // It is nil until the first test is started. |
| 105 | toRun chan *test |
| 106 | |
| 107 | // rungatec controls the max number of runoutput tests |
| 108 | // executed in parallel as they can each consume a lot of memory. |
| 109 | rungatec chan bool |
| 110 | ) |
| 111 | |
| 112 | // maxTests is an upper bound on the total number of tests. |
| 113 | // It is used as a channel buffer size to make sure sends don't block. |
| 114 | const maxTests = 5000 |
| 115 | |
| 116 | func main() { |
| 117 | flag.Parse() |
| 118 | |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 119 | findExecCmd() |
| 120 | |
| 121 | // Disable parallelism if printing or if using a simulator. |
| 122 | if *verbose || len(findExecCmd()) > 0 { |
| 123 | *numParallel = 1 |
Colin Cross | 1371fe4 | 2019-03-19 21:08:48 -0700 | [diff] [blame] | 124 | *runoutputLimit = 1 |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 125 | } |
| 126 | |
| 127 | ratec = make(chan bool, *numParallel) |
| 128 | rungatec = make(chan bool, *runoutputLimit) |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 129 | |
| 130 | var tests []*test |
| 131 | if flag.NArg() > 0 { |
| 132 | for _, arg := range flag.Args() { |
| 133 | if arg == "-" || arg == "--" { |
| 134 | // Permit running: |
| 135 | // $ go run run.go - env.go |
| 136 | // $ go run run.go -- env.go |
| 137 | // $ go run run.go - ./fixedbugs |
| 138 | // $ go run run.go -- ./fixedbugs |
| 139 | continue |
| 140 | } |
| 141 | if fi, err := os.Stat(arg); err == nil && fi.IsDir() { |
| 142 | for _, baseGoFile := range goFiles(arg) { |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 143 | tests = append(tests, startTest(arg, baseGoFile)) |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 144 | } |
| 145 | } else if strings.HasSuffix(arg, ".go") { |
| 146 | dir, file := filepath.Split(arg) |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 147 | tests = append(tests, startTest(dir, file)) |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 148 | } else { |
| 149 | log.Fatalf("can't yet deal with non-directory and non-go file %q", arg) |
| 150 | } |
| 151 | } |
| 152 | } else { |
| 153 | for _, dir := range dirs { |
| 154 | for _, baseGoFile := range goFiles(dir) { |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 155 | tests = append(tests, startTest(dir, baseGoFile)) |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 156 | } |
| 157 | } |
| 158 | } |
| 159 | |
| 160 | failed := false |
| 161 | resCount := map[string]int{} |
| 162 | for _, test := range tests { |
| 163 | <-test.donec |
| 164 | status := "ok " |
| 165 | errStr := "" |
Dan Willemsen | 0c15709 | 2016-07-08 13:57:52 -0700 | [diff] [blame] | 166 | if e, isSkip := test.err.(skipError); isSkip { |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 167 | test.err = nil |
Dan Willemsen | 0c15709 | 2016-07-08 13:57:52 -0700 | [diff] [blame] | 168 | errStr = "unexpected skip for " + path.Join(test.dir, test.gofile) + ": " + string(e) |
Dan Willemsen | 6ff2325 | 2015-09-15 13:49:18 -0700 | [diff] [blame] | 169 | status = "FAIL" |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 170 | } |
| 171 | if test.err != nil { |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 172 | errStr = test.err.Error() |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 173 | if test.expectFail { |
| 174 | errStr += " (expected)" |
| 175 | } else { |
| 176 | status = "FAIL" |
| 177 | } |
| 178 | } else if test.expectFail { |
| 179 | status = "FAIL" |
| 180 | errStr = "unexpected success" |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 181 | } |
| 182 | if status == "FAIL" { |
| 183 | failed = true |
| 184 | } |
| 185 | resCount[status]++ |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 186 | dt := fmt.Sprintf("%.3fs", test.dt.Seconds()) |
| 187 | if status == "FAIL" { |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 188 | fmt.Printf("# go run run.go -- %s\n%s\nFAIL\t%s\t%s\n", |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 189 | path.Join(test.dir, test.gofile), |
| 190 | errStr, test.goFileName(), dt) |
| 191 | continue |
| 192 | } |
| 193 | if !*verbose { |
| 194 | continue |
| 195 | } |
| 196 | fmt.Printf("%s\t%s\t%s\n", status, test.goFileName(), dt) |
| 197 | } |
| 198 | |
| 199 | if *summary { |
| 200 | for k, v := range resCount { |
| 201 | fmt.Printf("%5d %s\n", v, k) |
| 202 | } |
| 203 | } |
| 204 | |
| 205 | if failed { |
| 206 | os.Exit(1) |
| 207 | } |
| 208 | } |
| 209 | |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 210 | // goTool reports the path of the go tool to use to run the tests. |
| 211 | // If possible, use the same Go used to run run.go, otherwise |
| 212 | // fallback to the go version found in the PATH. |
| 213 | func goTool() string { |
| 214 | var exeSuffix string |
| 215 | if runtime.GOOS == "windows" { |
| 216 | exeSuffix = ".exe" |
| 217 | } |
| 218 | path := filepath.Join(runtime.GOROOT(), "bin", "go"+exeSuffix) |
| 219 | if _, err := os.Stat(path); err == nil { |
| 220 | return path |
| 221 | } |
| 222 | // Just run "go" from PATH |
| 223 | return "go" |
| 224 | } |
| 225 | |
Dan Willemsen | 6ff2325 | 2015-09-15 13:49:18 -0700 | [diff] [blame] | 226 | func shardMatch(name string) bool { |
| 227 | if *shards == 0 { |
| 228 | return true |
| 229 | } |
| 230 | h := fnv.New32() |
| 231 | io.WriteString(h, name) |
| 232 | return int(h.Sum32()%uint32(*shards)) == *shard |
| 233 | } |
| 234 | |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 235 | func goFiles(dir string) []string { |
| 236 | f, err := os.Open(dir) |
Patrice Arruda | 7f4776e | 2020-06-25 11:55:41 -0700 | [diff] [blame] | 237 | if err != nil { |
| 238 | log.Fatal(err) |
| 239 | } |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 240 | dirnames, err := f.Readdirnames(-1) |
Patrice Arruda | 7f4776e | 2020-06-25 11:55:41 -0700 | [diff] [blame] | 241 | f.Close() |
| 242 | if err != nil { |
| 243 | log.Fatal(err) |
| 244 | } |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 245 | names := []string{} |
| 246 | for _, name := range dirnames { |
Dan Willemsen | 6ff2325 | 2015-09-15 13:49:18 -0700 | [diff] [blame] | 247 | if !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") && shardMatch(name) { |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 248 | names = append(names, name) |
| 249 | } |
| 250 | } |
| 251 | sort.Strings(names) |
| 252 | return names |
| 253 | } |
| 254 | |
| 255 | type runCmd func(...string) ([]byte, error) |
| 256 | |
Dan Willemsen | c78f714 | 2017-07-26 13:08:14 -0700 | [diff] [blame] | 257 | func compileFile(runcmd runCmd, longname string, flags []string) (out []byte, err error) { |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 258 | cmd := []string{goTool(), "tool", "compile", "-e", "-p=p"} |
Dan Willemsen | c78f714 | 2017-07-26 13:08:14 -0700 | [diff] [blame] | 259 | cmd = append(cmd, flags...) |
Dan Willemsen | 0c15709 | 2016-07-08 13:57:52 -0700 | [diff] [blame] | 260 | if *linkshared { |
| 261 | cmd = append(cmd, "-dynlink", "-installsuffix=dynlink") |
| 262 | } |
| 263 | cmd = append(cmd, longname) |
| 264 | return runcmd(cmd...) |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 265 | } |
| 266 | |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 267 | func compileInDir(runcmd runCmd, dir string, flags []string, pkgname string, names ...string) (out []byte, err error) { |
| 268 | cmd := []string{goTool(), "tool", "compile", "-e", "-D", "test", "-I", "."} |
| 269 | if pkgname == "main" { |
| 270 | cmd = append(cmd, "-p=main") |
| 271 | } else { |
| 272 | pkgname = path.Join("test", strings.TrimSuffix(names[0], ".go")) |
| 273 | cmd = append(cmd, "-o", pkgname+".a", "-p", pkgname) |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 274 | } |
Dan Willemsen | 0c15709 | 2016-07-08 13:57:52 -0700 | [diff] [blame] | 275 | cmd = append(cmd, flags...) |
| 276 | if *linkshared { |
| 277 | cmd = append(cmd, "-dynlink", "-installsuffix=dynlink") |
| 278 | } |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 279 | for _, name := range names { |
| 280 | cmd = append(cmd, filepath.Join(dir, name)) |
| 281 | } |
| 282 | return runcmd(cmd...) |
| 283 | } |
| 284 | |
Colin Cross | efed634 | 2019-09-07 08:34:44 -0700 | [diff] [blame] | 285 | func linkFile(runcmd runCmd, goname string, ldflags []string) (err error) { |
Dan Willemsen | 6ff2325 | 2015-09-15 13:49:18 -0700 | [diff] [blame] | 286 | pfile := strings.Replace(goname, ".go", ".o", -1) |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 287 | cmd := []string{goTool(), "tool", "link", "-w", "-o", "a.exe", "-L", "."} |
Dan Willemsen | 0c15709 | 2016-07-08 13:57:52 -0700 | [diff] [blame] | 288 | if *linkshared { |
| 289 | cmd = append(cmd, "-linkshared", "-installsuffix=dynlink") |
| 290 | } |
Colin Cross | efed634 | 2019-09-07 08:34:44 -0700 | [diff] [blame] | 291 | if ldflags != nil { |
| 292 | cmd = append(cmd, ldflags...) |
| 293 | } |
Dan Willemsen | 0c15709 | 2016-07-08 13:57:52 -0700 | [diff] [blame] | 294 | cmd = append(cmd, pfile) |
| 295 | _, err = runcmd(cmd...) |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 296 | return |
| 297 | } |
| 298 | |
| 299 | // skipError describes why a test was skipped. |
| 300 | type skipError string |
| 301 | |
| 302 | func (s skipError) Error() string { return string(s) } |
| 303 | |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 304 | // test holds the state of a test. |
| 305 | type test struct { |
| 306 | dir, gofile string |
| 307 | donec chan bool // closed when done |
| 308 | dt time.Duration |
| 309 | |
Dan Willemsen | bbdf664 | 2017-01-13 22:57:23 -0800 | [diff] [blame] | 310 | src string |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 311 | |
| 312 | tempDir string |
| 313 | err error |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 314 | |
| 315 | // expectFail indicates whether the (overall) test recipe is |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 316 | // expected to fail under the current test configuration (e.g., |
| 317 | // GOEXPERIMENT=unified). |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 318 | expectFail bool |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 319 | } |
| 320 | |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 321 | // initExpectFail initializes t.expectFail based on the build+test |
| 322 | // configuration. |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 323 | func (t *test) initExpectFail() { |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 324 | if *force { |
| 325 | return |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 326 | } |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 327 | |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 328 | failureSets := []map[string]bool{types2Failures} |
Dan Willemsen | 14b5f99 | 2022-03-10 14:27:21 -0800 | [diff] [blame] | 329 | |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 330 | // Note: gccgo supports more 32-bit architectures than this, but |
| 331 | // hopefully the 32-bit failures are fixed before this matters. |
| 332 | switch goarch { |
| 333 | case "386", "arm", "mips", "mipsle": |
| 334 | failureSets = append(failureSets, types2Failures32Bit) |
| 335 | } |
| 336 | |
| 337 | if unifiedEnabled { |
| 338 | failureSets = append(failureSets, unifiedFailures) |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 339 | } else { |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 340 | failureSets = append(failureSets, go118Failures) |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 341 | } |
| 342 | |
| 343 | filename := strings.Replace(t.goFileName(), "\\", "/", -1) // goFileName() uses \ on Windows |
| 344 | |
| 345 | for _, set := range failureSets { |
| 346 | if set[filename] { |
| 347 | t.expectFail = true |
| 348 | return |
| 349 | } |
| 350 | } |
| 351 | } |
| 352 | |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 353 | func startTest(dir, gofile string) *test { |
| 354 | t := &test{ |
| 355 | dir: dir, |
| 356 | gofile: gofile, |
| 357 | donec: make(chan bool, 1), |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 358 | } |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 359 | if toRun == nil { |
| 360 | toRun = make(chan *test, maxTests) |
| 361 | go runTests() |
| 362 | } |
| 363 | select { |
| 364 | case toRun <- t: |
| 365 | default: |
| 366 | panic("toRun buffer size (maxTests) is too small") |
| 367 | } |
| 368 | return t |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 369 | } |
| 370 | |
| 371 | // runTests runs tests in parallel, but respecting the order they |
| 372 | // were enqueued on the toRun channel. |
| 373 | func runTests() { |
| 374 | for { |
| 375 | ratec <- true |
| 376 | t := <-toRun |
| 377 | go func() { |
| 378 | t.run() |
| 379 | <-ratec |
| 380 | }() |
| 381 | } |
| 382 | } |
| 383 | |
| 384 | var cwd, _ = os.Getwd() |
| 385 | |
| 386 | func (t *test) goFileName() string { |
| 387 | return filepath.Join(t.dir, t.gofile) |
| 388 | } |
| 389 | |
| 390 | func (t *test) goDirName() string { |
| 391 | return filepath.Join(t.dir, strings.Replace(t.gofile, ".go", ".dir", -1)) |
| 392 | } |
| 393 | |
| 394 | func goDirFiles(longdir string) (filter []os.FileInfo, err error) { |
| 395 | files, dirErr := ioutil.ReadDir(longdir) |
| 396 | if dirErr != nil { |
| 397 | return nil, dirErr |
| 398 | } |
| 399 | for _, gofile := range files { |
| 400 | if filepath.Ext(gofile.Name()) == ".go" { |
| 401 | filter = append(filter, gofile) |
| 402 | } |
| 403 | } |
| 404 | return |
| 405 | } |
| 406 | |
Colin Cross | 1371fe4 | 2019-03-19 21:08:48 -0700 | [diff] [blame] | 407 | var packageRE = regexp.MustCompile(`(?m)^package ([\p{Lu}\p{Ll}\w]+)`) |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 408 | |
Colin Cross | efed634 | 2019-09-07 08:34:44 -0700 | [diff] [blame] | 409 | func getPackageNameFromSource(fn string) (string, error) { |
| 410 | data, err := ioutil.ReadFile(fn) |
| 411 | if err != nil { |
| 412 | return "", err |
| 413 | } |
| 414 | pkgname := packageRE.FindStringSubmatch(string(data)) |
| 415 | if pkgname == nil { |
| 416 | return "", fmt.Errorf("cannot find package name in %s", fn) |
| 417 | } |
| 418 | return pkgname[1], nil |
| 419 | } |
| 420 | |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 421 | type goDirPkg struct { |
| 422 | name string |
| 423 | files []string |
| 424 | } |
| 425 | |
Dan Willemsen | 0c15709 | 2016-07-08 13:57:52 -0700 | [diff] [blame] | 426 | // If singlefilepkgs is set, each file is considered a separate package |
| 427 | // even if the package names are the same. |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 428 | func goDirPackages(longdir string, singlefilepkgs bool) ([]*goDirPkg, error) { |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 429 | files, err := goDirFiles(longdir) |
| 430 | if err != nil { |
| 431 | return nil, err |
| 432 | } |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 433 | var pkgs []*goDirPkg |
| 434 | m := make(map[string]*goDirPkg) |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 435 | for _, file := range files { |
| 436 | name := file.Name() |
Colin Cross | efed634 | 2019-09-07 08:34:44 -0700 | [diff] [blame] | 437 | pkgname, err := getPackageNameFromSource(filepath.Join(longdir, name)) |
Patrice Arruda | 7f4776e | 2020-06-25 11:55:41 -0700 | [diff] [blame] | 438 | if err != nil { |
| 439 | log.Fatal(err) |
| 440 | } |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 441 | p, ok := m[pkgname] |
Dan Willemsen | 0c15709 | 2016-07-08 13:57:52 -0700 | [diff] [blame] | 442 | if singlefilepkgs || !ok { |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 443 | p = &goDirPkg{name: pkgname} |
| 444 | pkgs = append(pkgs, p) |
| 445 | m[pkgname] = p |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 446 | } |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 447 | p.files = append(p.files, name) |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 448 | } |
| 449 | return pkgs, nil |
| 450 | } |
| 451 | |
| 452 | type context struct { |
Dan Willemsen | 31b9b84 | 2021-08-31 12:51:40 -0700 | [diff] [blame] | 453 | GOOS string |
| 454 | GOARCH string |
| 455 | cgoEnabled bool |
| 456 | noOptEnv bool |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 457 | } |
| 458 | |
| 459 | // shouldTest looks for build tags in a source file and returns |
| 460 | // whether the file should be used according to the tags. |
| 461 | func shouldTest(src string, goos, goarch string) (ok bool, whyNot string) { |
Dan Willemsen | 0c15709 | 2016-07-08 13:57:52 -0700 | [diff] [blame] | 462 | if *runSkips { |
| 463 | return true, "" |
| 464 | } |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 465 | for _, line := range strings.Split(src, "\n") { |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 466 | if strings.HasPrefix(line, "package ") { |
| 467 | break |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 468 | } |
Colin Cross | 1371fe4 | 2019-03-19 21:08:48 -0700 | [diff] [blame] | 469 | |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 470 | if expr, err := constraint.Parse(line); err == nil { |
| 471 | gcFlags := os.Getenv("GO_GCFLAGS") |
| 472 | ctxt := &context{ |
| 473 | GOOS: goos, |
| 474 | GOARCH: goarch, |
| 475 | cgoEnabled: cgoEnabled, |
| 476 | noOptEnv: strings.Contains(gcFlags, "-N") || strings.Contains(gcFlags, "-l"), |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 477 | } |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 478 | |
| 479 | if !expr.Eval(ctxt.match) { |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 480 | return false, line |
| 481 | } |
| 482 | } |
| 483 | } |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 484 | return true, "" |
| 485 | } |
| 486 | |
| 487 | func (ctxt *context) match(name string) bool { |
| 488 | if name == "" { |
| 489 | return false |
| 490 | } |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 491 | |
| 492 | // Tags must be letters, digits, underscores or dots. |
| 493 | // Unlike in Go identifiers, all digits are fine (e.g., "386"). |
| 494 | for _, c := range name { |
| 495 | if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' { |
| 496 | return false |
| 497 | } |
| 498 | } |
| 499 | |
Dan Willemsen | 31b9b84 | 2021-08-31 12:51:40 -0700 | [diff] [blame] | 500 | if strings.HasPrefix(name, "goexperiment.") { |
| 501 | for _, tag := range build.Default.ToolTags { |
| 502 | if tag == name { |
| 503 | return true |
| 504 | } |
| 505 | } |
| 506 | return false |
| 507 | } |
| 508 | |
| 509 | if name == "cgo" && ctxt.cgoEnabled { |
| 510 | return true |
| 511 | } |
| 512 | |
Colin Cross | 846c316 | 2021-05-14 11:11:40 -0700 | [diff] [blame] | 513 | if name == ctxt.GOOS || name == ctxt.GOARCH || name == "gc" { |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 514 | return true |
| 515 | } |
| 516 | |
Colin Cross | 1371fe4 | 2019-03-19 21:08:48 -0700 | [diff] [blame] | 517 | if ctxt.noOptEnv && name == "gcflags_noopt" { |
| 518 | return true |
| 519 | } |
| 520 | |
Dan Willemsen | 0c15709 | 2016-07-08 13:57:52 -0700 | [diff] [blame] | 521 | if name == "test_run" { |
| 522 | return true |
| 523 | } |
| 524 | |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 525 | return false |
| 526 | } |
| 527 | |
| 528 | func init() { checkShouldTest() } |
| 529 | |
Dan Willemsen | e1b3b18 | 2018-02-27 19:36:27 -0800 | [diff] [blame] | 530 | // goGcflags returns the -gcflags argument to use with go build / go run. |
Colin Cross | 1371fe4 | 2019-03-19 21:08:48 -0700 | [diff] [blame] | 531 | // This must match the flags used for building the standard library, |
Dan Willemsen | e1b3b18 | 2018-02-27 19:36:27 -0800 | [diff] [blame] | 532 | // or else the commands will rebuild any needed packages (like runtime) |
| 533 | // over and over. |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 534 | func (t *test) goGcflags() string { |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 535 | return "-gcflags=all=" + os.Getenv("GO_GCFLAGS") |
Patrice Arruda | 7f4776e | 2020-06-25 11:55:41 -0700 | [diff] [blame] | 536 | } |
| 537 | |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 538 | func (t *test) goGcflagsIsEmpty() bool { |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 539 | return "" == os.Getenv("GO_GCFLAGS") |
Dan Willemsen | e1b3b18 | 2018-02-27 19:36:27 -0800 | [diff] [blame] | 540 | } |
| 541 | |
Colin Cross | 846c316 | 2021-05-14 11:11:40 -0700 | [diff] [blame] | 542 | var errTimeout = errors.New("command exceeded time limit") |
| 543 | |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 544 | // run runs a test. |
| 545 | func (t *test) run() { |
| 546 | start := time.Now() |
| 547 | defer func() { |
| 548 | t.dt = time.Since(start) |
| 549 | close(t.donec) |
| 550 | }() |
| 551 | |
| 552 | srcBytes, err := ioutil.ReadFile(t.goFileName()) |
| 553 | if err != nil { |
| 554 | t.err = err |
| 555 | return |
| 556 | } |
| 557 | t.src = string(srcBytes) |
| 558 | if t.src[0] == '\n' { |
| 559 | t.err = skipError("starts with newline") |
| 560 | return |
| 561 | } |
Dan Willemsen | 6ff2325 | 2015-09-15 13:49:18 -0700 | [diff] [blame] | 562 | |
| 563 | // Execution recipe stops at first blank line. |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 564 | action, _, ok := strings.Cut(t.src, "\n\n") |
| 565 | if !ok { |
Dan Willemsen | 31b9b84 | 2021-08-31 12:51:40 -0700 | [diff] [blame] | 566 | t.err = fmt.Errorf("double newline ending execution recipe not found in %s", t.goFileName()) |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 567 | return |
| 568 | } |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 569 | if firstLine, rest, ok := strings.Cut(action, "\n"); ok && strings.Contains(firstLine, "+build") { |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 570 | // skip first line |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 571 | action = rest |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 572 | } |
Patrice Arruda | 7f4776e | 2020-06-25 11:55:41 -0700 | [diff] [blame] | 573 | action = strings.TrimPrefix(action, "//") |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 574 | |
Dan Willemsen | 6ff2325 | 2015-09-15 13:49:18 -0700 | [diff] [blame] | 575 | // Check for build constraints only up to the actual code. |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 576 | header, _, ok := strings.Cut(t.src, "\npackage") |
| 577 | if !ok { |
| 578 | header = action // some files are intentionally malformed |
Dan Willemsen | 6ff2325 | 2015-09-15 13:49:18 -0700 | [diff] [blame] | 579 | } |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 580 | if ok, why := shouldTest(header, goos, goarch); !ok { |
Dan Willemsen | 6ff2325 | 2015-09-15 13:49:18 -0700 | [diff] [blame] | 581 | if *showSkips { |
Dan Willemsen | bbdf664 | 2017-01-13 22:57:23 -0800 | [diff] [blame] | 582 | fmt.Printf("%-20s %-20s: %s\n", "skip", t.goFileName(), why) |
Dan Willemsen | 6ff2325 | 2015-09-15 13:49:18 -0700 | [diff] [blame] | 583 | } |
| 584 | return |
| 585 | } |
| 586 | |
Dan Willemsen | 31b9b84 | 2021-08-31 12:51:40 -0700 | [diff] [blame] | 587 | var args, flags, runenv []string |
Dan Willemsen | bbdf664 | 2017-01-13 22:57:23 -0800 | [diff] [blame] | 588 | var tim int |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 589 | wantError := false |
Dan Willemsen | bbdf664 | 2017-01-13 22:57:23 -0800 | [diff] [blame] | 590 | wantAuto := false |
Dan Willemsen | 0c15709 | 2016-07-08 13:57:52 -0700 | [diff] [blame] | 591 | singlefilepkgs := false |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 592 | f, err := splitQuoted(action) |
| 593 | if err != nil { |
| 594 | t.err = fmt.Errorf("invalid test recipe: %v", err) |
| 595 | return |
| 596 | } |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 597 | if len(f) > 0 { |
| 598 | action = f[0] |
| 599 | args = f[1:] |
| 600 | } |
| 601 | |
Dan Willemsen | 0c15709 | 2016-07-08 13:57:52 -0700 | [diff] [blame] | 602 | // TODO: Clean up/simplify this switch statement. |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 603 | switch action { |
Colin Cross | efed634 | 2019-09-07 08:34:44 -0700 | [diff] [blame] | 604 | case "compile", "compiledir", "build", "builddir", "buildrundir", "run", "buildrun", "runoutput", "rundir", "runindir", "asmcheck": |
Dan Willemsen | bbdf664 | 2017-01-13 22:57:23 -0800 | [diff] [blame] | 605 | // nothing to do |
Dan Willemsen | 0c15709 | 2016-07-08 13:57:52 -0700 | [diff] [blame] | 606 | case "errorcheckandrundir": |
| 607 | wantError = false // should be no error if also will run |
Dan Willemsen | bbdf664 | 2017-01-13 22:57:23 -0800 | [diff] [blame] | 608 | case "errorcheckwithauto": |
| 609 | action = "errorcheck" |
| 610 | wantAuto = true |
| 611 | wantError = true |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 612 | case "errorcheck", "errorcheckdir", "errorcheckoutput": |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 613 | wantError = true |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 614 | case "skip": |
Dan Willemsen | 0c15709 | 2016-07-08 13:57:52 -0700 | [diff] [blame] | 615 | if *runSkips { |
| 616 | break |
| 617 | } |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 618 | return |
| 619 | default: |
| 620 | t.err = skipError("skipped; unknown pattern: " + action) |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 621 | return |
| 622 | } |
| 623 | |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 624 | goexp := env.GOEXPERIMENT |
| 625 | |
Dan Willemsen | 0c15709 | 2016-07-08 13:57:52 -0700 | [diff] [blame] | 626 | // collect flags |
| 627 | for len(args) > 0 && strings.HasPrefix(args[0], "-") { |
| 628 | switch args[0] { |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 629 | case "-1": |
| 630 | wantError = true |
Dan Willemsen | 0c15709 | 2016-07-08 13:57:52 -0700 | [diff] [blame] | 631 | case "-0": |
| 632 | wantError = false |
| 633 | case "-s": |
| 634 | singlefilepkgs = true |
Dan Willemsen | bbdf664 | 2017-01-13 22:57:23 -0800 | [diff] [blame] | 635 | case "-t": // timeout in seconds |
| 636 | args = args[1:] |
| 637 | var err error |
| 638 | tim, err = strconv.Atoi(args[0]) |
| 639 | if err != nil { |
| 640 | t.err = fmt.Errorf("need number of seconds for -t timeout, got %s instead", args[0]) |
| 641 | } |
Dan Willemsen | 14b5f99 | 2022-03-10 14:27:21 -0800 | [diff] [blame] | 642 | if s := os.Getenv("GO_TEST_TIMEOUT_SCALE"); s != "" { |
| 643 | timeoutScale, err := strconv.Atoi(s) |
| 644 | if err != nil { |
| 645 | log.Fatalf("failed to parse $GO_TEST_TIMEOUT_SCALE = %q as integer: %v", s, err) |
| 646 | } |
| 647 | tim *= timeoutScale |
| 648 | } |
Dan Willemsen | 31b9b84 | 2021-08-31 12:51:40 -0700 | [diff] [blame] | 649 | case "-goexperiment": // set GOEXPERIMENT environment |
| 650 | args = args[1:] |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 651 | if goexp != "" { |
| 652 | goexp += "," |
| 653 | } |
| 654 | goexp += args[0] |
| 655 | runenv = append(runenv, "GOEXPERIMENT="+goexp) |
Dan Willemsen | bbdf664 | 2017-01-13 22:57:23 -0800 | [diff] [blame] | 656 | |
Dan Willemsen | 0c15709 | 2016-07-08 13:57:52 -0700 | [diff] [blame] | 657 | default: |
| 658 | flags = append(flags, args[0]) |
| 659 | } |
| 660 | args = args[1:] |
| 661 | } |
Colin Cross | 1371fe4 | 2019-03-19 21:08:48 -0700 | [diff] [blame] | 662 | if action == "errorcheck" { |
| 663 | found := false |
| 664 | for i, f := range flags { |
| 665 | if strings.HasPrefix(f, "-d=") { |
| 666 | flags[i] = f + ",ssa/check/on" |
| 667 | found = true |
| 668 | break |
| 669 | } |
| 670 | } |
| 671 | if !found { |
| 672 | flags = append(flags, "-d=ssa/check/on") |
| 673 | } |
| 674 | } |
Dan Willemsen | 0c15709 | 2016-07-08 13:57:52 -0700 | [diff] [blame] | 675 | |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 676 | t.initExpectFail() |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 677 | t.makeTempDir() |
Dan Willemsen | 0c15709 | 2016-07-08 13:57:52 -0700 | [diff] [blame] | 678 | if !*keep { |
| 679 | defer os.RemoveAll(t.tempDir) |
| 680 | } |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 681 | |
| 682 | err = ioutil.WriteFile(filepath.Join(t.tempDir, t.gofile), srcBytes, 0644) |
Patrice Arruda | 7f4776e | 2020-06-25 11:55:41 -0700 | [diff] [blame] | 683 | if err != nil { |
| 684 | log.Fatal(err) |
| 685 | } |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 686 | |
| 687 | // A few tests (of things like the environment) require these to be set. |
| 688 | if os.Getenv("GOOS") == "" { |
| 689 | os.Setenv("GOOS", runtime.GOOS) |
| 690 | } |
| 691 | if os.Getenv("GOARCH") == "" { |
| 692 | os.Setenv("GOARCH", runtime.GOARCH) |
| 693 | } |
| 694 | |
Patrice Arruda | 7f4776e | 2020-06-25 11:55:41 -0700 | [diff] [blame] | 695 | var ( |
| 696 | runInDir = t.tempDir |
| 697 | tempDirIsGOPATH = false |
| 698 | ) |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 699 | runcmd := func(args ...string) ([]byte, error) { |
| 700 | cmd := exec.Command(args[0], args[1:]...) |
| 701 | var buf bytes.Buffer |
| 702 | cmd.Stdout = &buf |
| 703 | cmd.Stderr = &buf |
Patrice Arruda | 7f4776e | 2020-06-25 11:55:41 -0700 | [diff] [blame] | 704 | cmd.Env = append(os.Environ(), "GOENV=off", "GOFLAGS=") |
| 705 | if runInDir != "" { |
| 706 | cmd.Dir = runInDir |
| 707 | // Set PWD to match Dir to speed up os.Getwd in the child process. |
| 708 | cmd.Env = append(cmd.Env, "PWD="+cmd.Dir) |
Colin Cross | efed634 | 2019-09-07 08:34:44 -0700 | [diff] [blame] | 709 | } |
Patrice Arruda | 7f4776e | 2020-06-25 11:55:41 -0700 | [diff] [blame] | 710 | if tempDirIsGOPATH { |
| 711 | cmd.Env = append(cmd.Env, "GOPATH="+t.tempDir) |
Dan Willemsen | 0c15709 | 2016-07-08 13:57:52 -0700 | [diff] [blame] | 712 | } |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 713 | // Put the bin directory of the GOROOT that built this program |
| 714 | // first in the path. This ensures that tests that use the "go" |
| 715 | // tool use the same one that built this program. This ensures |
| 716 | // that if you do "../bin/go run run.go" in this directory, all |
| 717 | // the tests that start subprocesses that "go tool compile" or |
| 718 | // whatever, use ../bin/go as their go tool, not whatever happens |
| 719 | // to be first in the user's path. |
| 720 | path := os.Getenv("PATH") |
| 721 | newdir := filepath.Join(runtime.GOROOT(), "bin") |
| 722 | if path != "" { |
| 723 | path = newdir + string(filepath.ListSeparator) + path |
| 724 | } else { |
| 725 | path = newdir |
| 726 | } |
| 727 | cmd.Env = append(cmd.Env, "PATH="+path) |
| 728 | |
Dan Willemsen | 31b9b84 | 2021-08-31 12:51:40 -0700 | [diff] [blame] | 729 | cmd.Env = append(cmd.Env, runenv...) |
Dan Willemsen | bbdf664 | 2017-01-13 22:57:23 -0800 | [diff] [blame] | 730 | |
| 731 | var err error |
| 732 | |
| 733 | if tim != 0 { |
| 734 | err = cmd.Start() |
| 735 | // This command-timeout code adapted from cmd/go/test.go |
Dan Willemsen | 14b5f99 | 2022-03-10 14:27:21 -0800 | [diff] [blame] | 736 | // Note: the Go command uses a more sophisticated timeout |
| 737 | // strategy, first sending SIGQUIT (if appropriate for the |
| 738 | // OS in question) to try to trigger a stack trace, then |
| 739 | // finally much later SIGKILL. If timeouts prove to be a |
| 740 | // common problem here, it would be worth porting over |
| 741 | // that code as well. See https://do.dev/issue/50973 |
| 742 | // for more discussion. |
Dan Willemsen | bbdf664 | 2017-01-13 22:57:23 -0800 | [diff] [blame] | 743 | if err == nil { |
| 744 | tick := time.NewTimer(time.Duration(tim) * time.Second) |
| 745 | done := make(chan error) |
| 746 | go func() { |
| 747 | done <- cmd.Wait() |
| 748 | }() |
| 749 | select { |
| 750 | case err = <-done: |
| 751 | // ok |
| 752 | case <-tick.C: |
Colin Cross | 846c316 | 2021-05-14 11:11:40 -0700 | [diff] [blame] | 753 | cmd.Process.Signal(os.Interrupt) |
| 754 | time.Sleep(1 * time.Second) |
Dan Willemsen | bbdf664 | 2017-01-13 22:57:23 -0800 | [diff] [blame] | 755 | cmd.Process.Kill() |
Colin Cross | 846c316 | 2021-05-14 11:11:40 -0700 | [diff] [blame] | 756 | <-done |
| 757 | err = errTimeout |
Dan Willemsen | bbdf664 | 2017-01-13 22:57:23 -0800 | [diff] [blame] | 758 | } |
| 759 | tick.Stop() |
| 760 | } |
| 761 | } else { |
| 762 | err = cmd.Run() |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 763 | } |
Colin Cross | 846c316 | 2021-05-14 11:11:40 -0700 | [diff] [blame] | 764 | if err != nil && err != errTimeout { |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 765 | err = fmt.Errorf("%s\n%s", err, buf.Bytes()) |
| 766 | } |
| 767 | return buf.Bytes(), err |
| 768 | } |
| 769 | |
| 770 | long := filepath.Join(cwd, t.goFileName()) |
| 771 | switch action { |
| 772 | default: |
| 773 | t.err = fmt.Errorf("unimplemented action %q", action) |
| 774 | |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 775 | case "asmcheck": |
| 776 | // Compile Go file and match the generated assembly |
| 777 | // against a set of regexps in comments. |
| 778 | ops := t.wantedAsmOpcodes(long) |
Colin Cross | efed634 | 2019-09-07 08:34:44 -0700 | [diff] [blame] | 779 | self := runtime.GOOS + "/" + runtime.GOARCH |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 780 | for _, env := range ops.Envs() { |
Colin Cross | efed634 | 2019-09-07 08:34:44 -0700 | [diff] [blame] | 781 | // Only run checks relevant to the current GOOS/GOARCH, |
| 782 | // to avoid triggering a cross-compile of the runtime. |
| 783 | if string(env) != self && !strings.HasPrefix(string(env), self+"/") && !*allCodegen { |
| 784 | continue |
| 785 | } |
Colin Cross | 1371fe4 | 2019-03-19 21:08:48 -0700 | [diff] [blame] | 786 | // -S=2 forces outermost line numbers when disassembling inlined code. |
| 787 | cmdline := []string{"build", "-gcflags", "-S=2"} |
Patrice Arruda | 7f4776e | 2020-06-25 11:55:41 -0700 | [diff] [blame] | 788 | |
| 789 | // Append flags, but don't override -gcflags=-S=2; add to it instead. |
| 790 | for i := 0; i < len(flags); i++ { |
| 791 | flag := flags[i] |
| 792 | switch { |
| 793 | case strings.HasPrefix(flag, "-gcflags="): |
| 794 | cmdline[2] += " " + strings.TrimPrefix(flag, "-gcflags=") |
| 795 | case strings.HasPrefix(flag, "--gcflags="): |
| 796 | cmdline[2] += " " + strings.TrimPrefix(flag, "--gcflags=") |
| 797 | case flag == "-gcflags", flag == "--gcflags": |
| 798 | i++ |
| 799 | if i < len(flags) { |
| 800 | cmdline[2] += " " + flags[i] |
| 801 | } |
| 802 | default: |
| 803 | cmdline = append(cmdline, flag) |
| 804 | } |
| 805 | } |
| 806 | |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 807 | cmdline = append(cmdline, long) |
| 808 | cmd := exec.Command(goTool(), cmdline...) |
| 809 | cmd.Env = append(os.Environ(), env.Environ()...) |
Colin Cross | efed634 | 2019-09-07 08:34:44 -0700 | [diff] [blame] | 810 | if len(flags) > 0 && flags[0] == "-race" { |
| 811 | cmd.Env = append(cmd.Env, "CGO_ENABLED=1") |
| 812 | } |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 813 | |
| 814 | var buf bytes.Buffer |
| 815 | cmd.Stdout, cmd.Stderr = &buf, &buf |
| 816 | if err := cmd.Run(); err != nil { |
| 817 | fmt.Println(env, "\n", cmd.Stderr) |
| 818 | t.err = err |
| 819 | return |
| 820 | } |
| 821 | |
| 822 | t.err = t.asmCheck(buf.String(), long, env, ops[env]) |
| 823 | if t.err != nil { |
| 824 | return |
| 825 | } |
| 826 | } |
| 827 | return |
| 828 | |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 829 | case "errorcheck": |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 830 | // Compile Go file. |
| 831 | // Fail if wantError is true and compilation was successful and vice versa. |
| 832 | // Match errors produced by gc against errors in comments. |
Dan Willemsen | c78f714 | 2017-07-26 13:08:14 -0700 | [diff] [blame] | 833 | // TODO(gri) remove need for -C (disable printing of columns in error messages) |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 834 | cmdline := []string{goTool(), "tool", "compile", "-p=p", "-d=panic", "-C", "-e", "-o", "a.o"} |
Dan Willemsen | 0c15709 | 2016-07-08 13:57:52 -0700 | [diff] [blame] | 835 | // No need to add -dynlink even if linkshared if we're just checking for errors... |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 836 | cmdline = append(cmdline, flags...) |
| 837 | cmdline = append(cmdline, long) |
| 838 | out, err := runcmd(cmdline...) |
| 839 | if wantError { |
| 840 | if err == nil { |
| 841 | t.err = fmt.Errorf("compilation succeeded unexpectedly\n%s", out) |
| 842 | return |
| 843 | } |
Colin Cross | 846c316 | 2021-05-14 11:11:40 -0700 | [diff] [blame] | 844 | if err == errTimeout { |
| 845 | t.err = fmt.Errorf("compilation timed out") |
| 846 | return |
| 847 | } |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 848 | } else { |
| 849 | if err != nil { |
| 850 | t.err = err |
| 851 | return |
| 852 | } |
| 853 | } |
Dan Willemsen | 6ff2325 | 2015-09-15 13:49:18 -0700 | [diff] [blame] | 854 | if *updateErrors { |
| 855 | t.updateErrors(string(out), long) |
| 856 | } |
Dan Willemsen | bbdf664 | 2017-01-13 22:57:23 -0800 | [diff] [blame] | 857 | t.err = t.errorCheck(string(out), wantAuto, long, t.gofile) |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 858 | |
| 859 | case "compile": |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 860 | // Compile Go file. |
Dan Willemsen | c78f714 | 2017-07-26 13:08:14 -0700 | [diff] [blame] | 861 | _, t.err = compileFile(runcmd, long, flags) |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 862 | |
| 863 | case "compiledir": |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 864 | // Compile all files in the directory as packages in lexicographic order. |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 865 | longdir := filepath.Join(cwd, t.goDirName()) |
Dan Willemsen | 0c15709 | 2016-07-08 13:57:52 -0700 | [diff] [blame] | 866 | pkgs, err := goDirPackages(longdir, singlefilepkgs) |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 867 | if err != nil { |
| 868 | t.err = err |
| 869 | return |
| 870 | } |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 871 | for _, pkg := range pkgs { |
| 872 | _, t.err = compileInDir(runcmd, longdir, flags, pkg.name, pkg.files...) |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 873 | if t.err != nil { |
| 874 | return |
| 875 | } |
| 876 | } |
| 877 | |
Dan Willemsen | 0c15709 | 2016-07-08 13:57:52 -0700 | [diff] [blame] | 878 | case "errorcheckdir", "errorcheckandrundir": |
Dan Willemsen | 31b9b84 | 2021-08-31 12:51:40 -0700 | [diff] [blame] | 879 | flags = append(flags, "-d=panic") |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 880 | // Compile and errorCheck all files in the directory as packages in lexicographic order. |
| 881 | // If errorcheckdir and wantError, compilation of the last package must fail. |
| 882 | // If errorcheckandrundir and wantError, compilation of the package prior the last must fail. |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 883 | longdir := filepath.Join(cwd, t.goDirName()) |
Dan Willemsen | 0c15709 | 2016-07-08 13:57:52 -0700 | [diff] [blame] | 884 | pkgs, err := goDirPackages(longdir, singlefilepkgs) |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 885 | if err != nil { |
| 886 | t.err = err |
| 887 | return |
| 888 | } |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 889 | errPkg := len(pkgs) - 1 |
| 890 | if wantError && action == "errorcheckandrundir" { |
| 891 | // The last pkg should compiled successfully and will be run in next case. |
| 892 | // Preceding pkg must return an error from compileInDir. |
| 893 | errPkg-- |
| 894 | } |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 895 | for i, pkg := range pkgs { |
| 896 | out, err := compileInDir(runcmd, longdir, flags, pkg.name, pkg.files...) |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 897 | if i == errPkg { |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 898 | if wantError && err == nil { |
| 899 | t.err = fmt.Errorf("compilation succeeded unexpectedly\n%s", out) |
| 900 | return |
| 901 | } else if !wantError && err != nil { |
| 902 | t.err = err |
| 903 | return |
| 904 | } |
| 905 | } else if err != nil { |
| 906 | t.err = err |
| 907 | return |
| 908 | } |
| 909 | var fullshort []string |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 910 | for _, name := range pkg.files { |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 911 | fullshort = append(fullshort, filepath.Join(longdir, name), name) |
| 912 | } |
Dan Willemsen | bbdf664 | 2017-01-13 22:57:23 -0800 | [diff] [blame] | 913 | t.err = t.errorCheck(string(out), wantAuto, fullshort...) |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 914 | if t.err != nil { |
| 915 | break |
| 916 | } |
| 917 | } |
Dan Willemsen | 0c15709 | 2016-07-08 13:57:52 -0700 | [diff] [blame] | 918 | if action == "errorcheckdir" { |
| 919 | return |
| 920 | } |
| 921 | fallthrough |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 922 | |
| 923 | case "rundir": |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 924 | // Compile all files in the directory as packages in lexicographic order. |
| 925 | // In case of errorcheckandrundir, ignore failed compilation of the package before the last. |
| 926 | // Link as if the last file is the main package, run it. |
| 927 | // Verify the expected output. |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 928 | longdir := filepath.Join(cwd, t.goDirName()) |
Dan Willemsen | 0c15709 | 2016-07-08 13:57:52 -0700 | [diff] [blame] | 929 | pkgs, err := goDirPackages(longdir, singlefilepkgs) |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 930 | if err != nil { |
| 931 | t.err = err |
| 932 | return |
| 933 | } |
Colin Cross | efed634 | 2019-09-07 08:34:44 -0700 | [diff] [blame] | 934 | // Split flags into gcflags and ldflags |
| 935 | ldflags := []string{} |
| 936 | for i, fl := range flags { |
| 937 | if fl == "-ldflags" { |
| 938 | ldflags = flags[i+1:] |
| 939 | flags = flags[0:i] |
| 940 | break |
| 941 | } |
| 942 | } |
| 943 | |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 944 | for i, pkg := range pkgs { |
| 945 | _, err := compileInDir(runcmd, longdir, flags, pkg.name, pkg.files...) |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 946 | // Allow this package compilation fail based on conditions below; |
| 947 | // its errors were checked in previous case. |
| 948 | if err != nil && !(wantError && action == "errorcheckandrundir" && i == len(pkgs)-2) { |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 949 | t.err = err |
| 950 | return |
| 951 | } |
| 952 | if i == len(pkgs)-1 { |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 953 | err = linkFile(runcmd, pkg.files[0], ldflags) |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 954 | if err != nil { |
| 955 | t.err = err |
| 956 | return |
| 957 | } |
| 958 | var cmd []string |
| 959 | cmd = append(cmd, findExecCmd()...) |
| 960 | cmd = append(cmd, filepath.Join(t.tempDir, "a.exe")) |
| 961 | cmd = append(cmd, args...) |
| 962 | out, err := runcmd(cmd...) |
| 963 | if err != nil { |
| 964 | t.err = err |
| 965 | return |
| 966 | } |
Dan Willemsen | 31b9b84 | 2021-08-31 12:51:40 -0700 | [diff] [blame] | 967 | t.checkExpectedOutput(out) |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 968 | } |
| 969 | } |
| 970 | |
Colin Cross | efed634 | 2019-09-07 08:34:44 -0700 | [diff] [blame] | 971 | case "runindir": |
Patrice Arruda | 7f4776e | 2020-06-25 11:55:41 -0700 | [diff] [blame] | 972 | // Make a shallow copy of t.goDirName() in its own module and GOPATH, and |
| 973 | // run "go run ." in it. The module path (and hence import path prefix) of |
| 974 | // the copy is equal to the basename of the source directory. |
| 975 | // |
| 976 | // It's used when test a requires a full 'go build' in order to compile |
| 977 | // the sources, such as when importing multiple packages (issue29612.dir) |
| 978 | // or compiling a package containing assembly files (see issue15609.dir), |
| 979 | // but still needs to be run to verify the expected output. |
| 980 | tempDirIsGOPATH = true |
| 981 | srcDir := t.goDirName() |
| 982 | modName := filepath.Base(srcDir) |
| 983 | gopathSrcDir := filepath.Join(t.tempDir, "src", modName) |
| 984 | runInDir = gopathSrcDir |
| 985 | |
| 986 | if err := overlayDir(gopathSrcDir, srcDir); err != nil { |
| 987 | t.err = err |
| 988 | return |
| 989 | } |
| 990 | |
| 991 | modFile := fmt.Sprintf("module %s\ngo 1.14\n", modName) |
| 992 | if err := ioutil.WriteFile(filepath.Join(gopathSrcDir, "go.mod"), []byte(modFile), 0666); err != nil { |
| 993 | t.err = err |
| 994 | return |
| 995 | } |
| 996 | |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 997 | cmd := []string{goTool(), "run", t.goGcflags()} |
Colin Cross | efed634 | 2019-09-07 08:34:44 -0700 | [diff] [blame] | 998 | if *linkshared { |
| 999 | cmd = append(cmd, "-linkshared") |
| 1000 | } |
Dan Willemsen | 31b9b84 | 2021-08-31 12:51:40 -0700 | [diff] [blame] | 1001 | cmd = append(cmd, flags...) |
Colin Cross | efed634 | 2019-09-07 08:34:44 -0700 | [diff] [blame] | 1002 | cmd = append(cmd, ".") |
| 1003 | out, err := runcmd(cmd...) |
| 1004 | if err != nil { |
| 1005 | t.err = err |
| 1006 | return |
| 1007 | } |
Dan Willemsen | 31b9b84 | 2021-08-31 12:51:40 -0700 | [diff] [blame] | 1008 | t.checkExpectedOutput(out) |
Colin Cross | efed634 | 2019-09-07 08:34:44 -0700 | [diff] [blame] | 1009 | |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 1010 | case "build": |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 1011 | // Build Go file. |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 1012 | cmd := []string{goTool(), "build", t.goGcflags()} |
| 1013 | cmd = append(cmd, flags...) |
| 1014 | cmd = append(cmd, "-o", "a.exe", long) |
| 1015 | _, err := runcmd(cmd...) |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 1016 | if err != nil { |
| 1017 | t.err = err |
| 1018 | } |
| 1019 | |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 1020 | case "builddir", "buildrundir": |
Dan Willemsen | c78f714 | 2017-07-26 13:08:14 -0700 | [diff] [blame] | 1021 | // Build an executable from all the .go and .s files in a subdirectory. |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 1022 | // Run it and verify its output in the buildrundir case. |
Dan Willemsen | c78f714 | 2017-07-26 13:08:14 -0700 | [diff] [blame] | 1023 | longdir := filepath.Join(cwd, t.goDirName()) |
| 1024 | files, dirErr := ioutil.ReadDir(longdir) |
| 1025 | if dirErr != nil { |
| 1026 | t.err = dirErr |
| 1027 | break |
| 1028 | } |
Colin Cross | 1371fe4 | 2019-03-19 21:08:48 -0700 | [diff] [blame] | 1029 | var gos []string |
| 1030 | var asms []string |
Dan Willemsen | c78f714 | 2017-07-26 13:08:14 -0700 | [diff] [blame] | 1031 | for _, file := range files { |
| 1032 | switch filepath.Ext(file.Name()) { |
| 1033 | case ".go": |
Colin Cross | 1371fe4 | 2019-03-19 21:08:48 -0700 | [diff] [blame] | 1034 | gos = append(gos, filepath.Join(longdir, file.Name())) |
Dan Willemsen | c78f714 | 2017-07-26 13:08:14 -0700 | [diff] [blame] | 1035 | case ".s": |
Colin Cross | 1371fe4 | 2019-03-19 21:08:48 -0700 | [diff] [blame] | 1036 | asms = append(asms, filepath.Join(longdir, file.Name())) |
Dan Willemsen | c78f714 | 2017-07-26 13:08:14 -0700 | [diff] [blame] | 1037 | } |
| 1038 | |
| 1039 | } |
Colin Cross | 1371fe4 | 2019-03-19 21:08:48 -0700 | [diff] [blame] | 1040 | if len(asms) > 0 { |
| 1041 | emptyHdrFile := filepath.Join(t.tempDir, "go_asm.h") |
| 1042 | if err := ioutil.WriteFile(emptyHdrFile, nil, 0666); err != nil { |
| 1043 | t.err = fmt.Errorf("write empty go_asm.h: %s", err) |
| 1044 | return |
| 1045 | } |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 1046 | cmd := []string{goTool(), "tool", "asm", "-p=main", "-gensymabis", "-o", "symabis"} |
Colin Cross | 1371fe4 | 2019-03-19 21:08:48 -0700 | [diff] [blame] | 1047 | cmd = append(cmd, asms...) |
| 1048 | _, err = runcmd(cmd...) |
| 1049 | if err != nil { |
| 1050 | t.err = err |
| 1051 | break |
| 1052 | } |
| 1053 | } |
Dan Willemsen | c78f714 | 2017-07-26 13:08:14 -0700 | [diff] [blame] | 1054 | var objs []string |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 1055 | cmd := []string{goTool(), "tool", "compile", "-p=main", "-e", "-D", ".", "-I", ".", "-o", "go.o"} |
Dan Willemsen | e1b3b18 | 2018-02-27 19:36:27 -0800 | [diff] [blame] | 1056 | if len(asms) > 0 { |
Colin Cross | 1371fe4 | 2019-03-19 21:08:48 -0700 | [diff] [blame] | 1057 | cmd = append(cmd, "-asmhdr", "go_asm.h", "-symabis", "symabis") |
Dan Willemsen | e1b3b18 | 2018-02-27 19:36:27 -0800 | [diff] [blame] | 1058 | } |
Colin Cross | 1371fe4 | 2019-03-19 21:08:48 -0700 | [diff] [blame] | 1059 | cmd = append(cmd, gos...) |
Dan Willemsen | c78f714 | 2017-07-26 13:08:14 -0700 | [diff] [blame] | 1060 | _, err := runcmd(cmd...) |
| 1061 | if err != nil { |
| 1062 | t.err = err |
| 1063 | break |
| 1064 | } |
| 1065 | objs = append(objs, "go.o") |
| 1066 | if len(asms) > 0 { |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 1067 | cmd = []string{goTool(), "tool", "asm", "-p=main", "-e", "-I", ".", "-o", "asm.o"} |
Colin Cross | 1371fe4 | 2019-03-19 21:08:48 -0700 | [diff] [blame] | 1068 | cmd = append(cmd, asms...) |
Dan Willemsen | c78f714 | 2017-07-26 13:08:14 -0700 | [diff] [blame] | 1069 | _, err = runcmd(cmd...) |
| 1070 | if err != nil { |
| 1071 | t.err = err |
| 1072 | break |
| 1073 | } |
| 1074 | objs = append(objs, "asm.o") |
| 1075 | } |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 1076 | cmd = []string{goTool(), "tool", "pack", "c", "all.a"} |
Dan Willemsen | c78f714 | 2017-07-26 13:08:14 -0700 | [diff] [blame] | 1077 | cmd = append(cmd, objs...) |
| 1078 | _, err = runcmd(cmd...) |
| 1079 | if err != nil { |
| 1080 | t.err = err |
| 1081 | break |
| 1082 | } |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 1083 | cmd = []string{goTool(), "tool", "link", "-o", "a.exe", "all.a"} |
Dan Willemsen | c78f714 | 2017-07-26 13:08:14 -0700 | [diff] [blame] | 1084 | _, err = runcmd(cmd...) |
| 1085 | if err != nil { |
| 1086 | t.err = err |
| 1087 | break |
| 1088 | } |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 1089 | if action == "buildrundir" { |
| 1090 | cmd = append(findExecCmd(), filepath.Join(t.tempDir, "a.exe")) |
| 1091 | out, err := runcmd(cmd...) |
| 1092 | if err != nil { |
| 1093 | t.err = err |
| 1094 | break |
| 1095 | } |
Dan Willemsen | 31b9b84 | 2021-08-31 12:51:40 -0700 | [diff] [blame] | 1096 | t.checkExpectedOutput(out) |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 1097 | } |
Dan Willemsen | c78f714 | 2017-07-26 13:08:14 -0700 | [diff] [blame] | 1098 | |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 1099 | case "buildrun": |
| 1100 | // Build an executable from Go file, then run it, verify its output. |
| 1101 | // Useful for timeout tests where failure mode is infinite loop. |
Dan Willemsen | bbdf664 | 2017-01-13 22:57:23 -0800 | [diff] [blame] | 1102 | // TODO: not supported on NaCl |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 1103 | cmd := []string{goTool(), "build", t.goGcflags(), "-o", "a.exe"} |
Dan Willemsen | bbdf664 | 2017-01-13 22:57:23 -0800 | [diff] [blame] | 1104 | if *linkshared { |
| 1105 | cmd = append(cmd, "-linkshared") |
| 1106 | } |
| 1107 | longdirgofile := filepath.Join(filepath.Join(cwd, t.dir), t.gofile) |
| 1108 | cmd = append(cmd, flags...) |
| 1109 | cmd = append(cmd, longdirgofile) |
Patrice Arruda | 7f4776e | 2020-06-25 11:55:41 -0700 | [diff] [blame] | 1110 | _, err := runcmd(cmd...) |
Dan Willemsen | bbdf664 | 2017-01-13 22:57:23 -0800 | [diff] [blame] | 1111 | if err != nil { |
| 1112 | t.err = err |
| 1113 | return |
| 1114 | } |
| 1115 | cmd = []string{"./a.exe"} |
Patrice Arruda | 7f4776e | 2020-06-25 11:55:41 -0700 | [diff] [blame] | 1116 | out, err := runcmd(append(cmd, args...)...) |
Dan Willemsen | bbdf664 | 2017-01-13 22:57:23 -0800 | [diff] [blame] | 1117 | if err != nil { |
| 1118 | t.err = err |
| 1119 | return |
| 1120 | } |
| 1121 | |
Dan Willemsen | 31b9b84 | 2021-08-31 12:51:40 -0700 | [diff] [blame] | 1122 | t.checkExpectedOutput(out) |
Dan Willemsen | bbdf664 | 2017-01-13 22:57:23 -0800 | [diff] [blame] | 1123 | |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 1124 | case "run": |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 1125 | // Run Go file if no special go command flags are provided; |
| 1126 | // otherwise build an executable and run it. |
| 1127 | // Verify the output. |
Patrice Arruda | 7f4776e | 2020-06-25 11:55:41 -0700 | [diff] [blame] | 1128 | runInDir = "" |
Dan Willemsen | e1b3b18 | 2018-02-27 19:36:27 -0800 | [diff] [blame] | 1129 | var out []byte |
| 1130 | var err error |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 1131 | if len(flags)+len(args) == 0 && t.goGcflagsIsEmpty() && !*linkshared && goarch == runtime.GOARCH && goos == runtime.GOOS && goexp == env.GOEXPERIMENT { |
Dan Willemsen | e1b3b18 | 2018-02-27 19:36:27 -0800 | [diff] [blame] | 1132 | // If we're not using special go command flags, |
| 1133 | // skip all the go command machinery. |
| 1134 | // This avoids any time the go command would |
| 1135 | // spend checking whether, for example, the installed |
| 1136 | // package runtime is up to date. |
| 1137 | // Because we run lots of trivial test programs, |
| 1138 | // the time adds up. |
| 1139 | pkg := filepath.Join(t.tempDir, "pkg.a") |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 1140 | if _, err := runcmd(goTool(), "tool", "compile", "-p=main", "-o", pkg, t.goFileName()); err != nil { |
Dan Willemsen | e1b3b18 | 2018-02-27 19:36:27 -0800 | [diff] [blame] | 1141 | t.err = err |
| 1142 | return |
| 1143 | } |
| 1144 | exe := filepath.Join(t.tempDir, "test.exe") |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 1145 | cmd := []string{goTool(), "tool", "link", "-s", "-w"} |
Dan Willemsen | e1b3b18 | 2018-02-27 19:36:27 -0800 | [diff] [blame] | 1146 | cmd = append(cmd, "-o", exe, pkg) |
| 1147 | if _, err := runcmd(cmd...); err != nil { |
| 1148 | t.err = err |
| 1149 | return |
| 1150 | } |
| 1151 | out, err = runcmd(append([]string{exe}, args...)...) |
| 1152 | } else { |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 1153 | cmd := []string{goTool(), "run", t.goGcflags()} |
Dan Willemsen | e1b3b18 | 2018-02-27 19:36:27 -0800 | [diff] [blame] | 1154 | if *linkshared { |
| 1155 | cmd = append(cmd, "-linkshared") |
| 1156 | } |
| 1157 | cmd = append(cmd, flags...) |
| 1158 | cmd = append(cmd, t.goFileName()) |
| 1159 | out, err = runcmd(append(cmd, args...)...) |
Dan Willemsen | 0c15709 | 2016-07-08 13:57:52 -0700 | [diff] [blame] | 1160 | } |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 1161 | if err != nil { |
| 1162 | t.err = err |
| 1163 | return |
| 1164 | } |
Dan Willemsen | 31b9b84 | 2021-08-31 12:51:40 -0700 | [diff] [blame] | 1165 | t.checkExpectedOutput(out) |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 1166 | |
| 1167 | case "runoutput": |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 1168 | // Run Go file and write its output into temporary Go file. |
| 1169 | // Run generated Go file and verify its output. |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 1170 | rungatec <- true |
| 1171 | defer func() { |
| 1172 | <-rungatec |
| 1173 | }() |
Patrice Arruda | 7f4776e | 2020-06-25 11:55:41 -0700 | [diff] [blame] | 1174 | runInDir = "" |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 1175 | cmd := []string{goTool(), "run", t.goGcflags()} |
Dan Willemsen | 0c15709 | 2016-07-08 13:57:52 -0700 | [diff] [blame] | 1176 | if *linkshared { |
| 1177 | cmd = append(cmd, "-linkshared") |
| 1178 | } |
| 1179 | cmd = append(cmd, t.goFileName()) |
| 1180 | out, err := runcmd(append(cmd, args...)...) |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 1181 | if err != nil { |
| 1182 | t.err = err |
| 1183 | return |
| 1184 | } |
| 1185 | tfile := filepath.Join(t.tempDir, "tmp__.go") |
| 1186 | if err := ioutil.WriteFile(tfile, out, 0666); err != nil { |
| 1187 | t.err = fmt.Errorf("write tempfile:%s", err) |
| 1188 | return |
| 1189 | } |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 1190 | cmd = []string{goTool(), "run", t.goGcflags()} |
Dan Willemsen | 0c15709 | 2016-07-08 13:57:52 -0700 | [diff] [blame] | 1191 | if *linkshared { |
| 1192 | cmd = append(cmd, "-linkshared") |
| 1193 | } |
| 1194 | cmd = append(cmd, tfile) |
| 1195 | out, err = runcmd(cmd...) |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 1196 | if err != nil { |
| 1197 | t.err = err |
| 1198 | return |
| 1199 | } |
Dan Willemsen | 31b9b84 | 2021-08-31 12:51:40 -0700 | [diff] [blame] | 1200 | t.checkExpectedOutput(out) |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 1201 | |
| 1202 | case "errorcheckoutput": |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 1203 | // Run Go file and write its output into temporary Go file. |
| 1204 | // Compile and errorCheck generated Go file. |
Patrice Arruda | 7f4776e | 2020-06-25 11:55:41 -0700 | [diff] [blame] | 1205 | runInDir = "" |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 1206 | cmd := []string{goTool(), "run", t.goGcflags()} |
Dan Willemsen | 0c15709 | 2016-07-08 13:57:52 -0700 | [diff] [blame] | 1207 | if *linkshared { |
| 1208 | cmd = append(cmd, "-linkshared") |
| 1209 | } |
| 1210 | cmd = append(cmd, t.goFileName()) |
| 1211 | out, err := runcmd(append(cmd, args...)...) |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 1212 | if err != nil { |
| 1213 | t.err = err |
| 1214 | return |
| 1215 | } |
| 1216 | tfile := filepath.Join(t.tempDir, "tmp__.go") |
| 1217 | err = ioutil.WriteFile(tfile, out, 0666) |
| 1218 | if err != nil { |
| 1219 | t.err = fmt.Errorf("write tempfile:%s", err) |
| 1220 | return |
| 1221 | } |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 1222 | cmdline := []string{goTool(), "tool", "compile", "-p=p", "-d=panic", "-e", "-o", "a.o"} |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 1223 | cmdline = append(cmdline, flags...) |
| 1224 | cmdline = append(cmdline, tfile) |
| 1225 | out, err = runcmd(cmdline...) |
| 1226 | if wantError { |
| 1227 | if err == nil { |
| 1228 | t.err = fmt.Errorf("compilation succeeded unexpectedly\n%s", out) |
| 1229 | return |
| 1230 | } |
| 1231 | } else { |
| 1232 | if err != nil { |
| 1233 | t.err = err |
| 1234 | return |
| 1235 | } |
| 1236 | } |
Dan Willemsen | bbdf664 | 2017-01-13 22:57:23 -0800 | [diff] [blame] | 1237 | t.err = t.errorCheck(string(out), false, tfile, "tmp__.go") |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 1238 | return |
| 1239 | } |
| 1240 | } |
| 1241 | |
| 1242 | var execCmd []string |
| 1243 | |
| 1244 | func findExecCmd() []string { |
| 1245 | if execCmd != nil { |
| 1246 | return execCmd |
| 1247 | } |
| 1248 | execCmd = []string{} // avoid work the second time |
| 1249 | if goos == runtime.GOOS && goarch == runtime.GOARCH { |
| 1250 | return execCmd |
| 1251 | } |
| 1252 | path, err := exec.LookPath(fmt.Sprintf("go_%s_%s_exec", goos, goarch)) |
| 1253 | if err == nil { |
| 1254 | execCmd = []string{path} |
| 1255 | } |
| 1256 | return execCmd |
| 1257 | } |
| 1258 | |
| 1259 | func (t *test) String() string { |
| 1260 | return filepath.Join(t.dir, t.gofile) |
| 1261 | } |
| 1262 | |
| 1263 | func (t *test) makeTempDir() { |
| 1264 | var err error |
| 1265 | t.tempDir, err = ioutil.TempDir("", "") |
Patrice Arruda | 7f4776e | 2020-06-25 11:55:41 -0700 | [diff] [blame] | 1266 | if err != nil { |
| 1267 | log.Fatal(err) |
| 1268 | } |
Dan Willemsen | 0c15709 | 2016-07-08 13:57:52 -0700 | [diff] [blame] | 1269 | if *keep { |
| 1270 | log.Printf("Temporary directory is %s", t.tempDir) |
| 1271 | } |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 1272 | err = os.Mkdir(filepath.Join(t.tempDir, "test"), 0o755) |
| 1273 | if err != nil { |
| 1274 | log.Fatal(err) |
| 1275 | } |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 1276 | } |
| 1277 | |
Dan Willemsen | 31b9b84 | 2021-08-31 12:51:40 -0700 | [diff] [blame] | 1278 | // checkExpectedOutput compares the output from compiling and/or running with the contents |
| 1279 | // of the corresponding reference output file, if any (replace ".go" with ".out"). |
| 1280 | // If they don't match, fail with an informative message. |
| 1281 | func (t *test) checkExpectedOutput(gotBytes []byte) { |
| 1282 | got := string(gotBytes) |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 1283 | filename := filepath.Join(t.dir, t.gofile) |
| 1284 | filename = filename[:len(filename)-len(".go")] |
| 1285 | filename += ".out" |
Dan Willemsen | 31b9b84 | 2021-08-31 12:51:40 -0700 | [diff] [blame] | 1286 | b, err := ioutil.ReadFile(filename) |
| 1287 | // File is allowed to be missing (err != nil) in which case output should be empty. |
| 1288 | got = strings.Replace(got, "\r\n", "\n", -1) |
| 1289 | if got != string(b) { |
| 1290 | if err == nil { |
| 1291 | t.err = fmt.Errorf("output does not match expected in %s. Instead saw\n%s", filename, got) |
| 1292 | } else { |
| 1293 | t.err = fmt.Errorf("output should be empty when (optional) expected-output file %s is not present. Instead saw\n%s", filename, got) |
| 1294 | } |
| 1295 | } |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 1296 | } |
| 1297 | |
Dan Willemsen | bbdf664 | 2017-01-13 22:57:23 -0800 | [diff] [blame] | 1298 | func splitOutput(out string, wantAuto bool) []string { |
Dan Willemsen | 6ff2325 | 2015-09-15 13:49:18 -0700 | [diff] [blame] | 1299 | // gc error messages continue onto additional lines with leading tabs. |
| 1300 | // Split the output at the beginning of each line that doesn't begin with a tab. |
| 1301 | // <autogenerated> lines are impossible to match so those are filtered out. |
| 1302 | var res []string |
| 1303 | for _, line := range strings.Split(out, "\n") { |
| 1304 | if strings.HasSuffix(line, "\r") { // remove '\r', output by compiler on windows |
| 1305 | line = line[:len(line)-1] |
| 1306 | } |
| 1307 | if strings.HasPrefix(line, "\t") { |
| 1308 | res[len(res)-1] += "\n" + line |
Dan Willemsen | bbdf664 | 2017-01-13 22:57:23 -0800 | [diff] [blame] | 1309 | } else if strings.HasPrefix(line, "go tool") || strings.HasPrefix(line, "#") || !wantAuto && strings.HasPrefix(line, "<autogenerated>") { |
Dan Willemsen | 6ff2325 | 2015-09-15 13:49:18 -0700 | [diff] [blame] | 1310 | continue |
| 1311 | } else if strings.TrimSpace(line) != "" { |
| 1312 | res = append(res, line) |
| 1313 | } |
| 1314 | } |
| 1315 | return res |
| 1316 | } |
| 1317 | |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 1318 | // errorCheck matches errors in outStr against comments in source files. |
| 1319 | // For each line of the source files which should generate an error, |
| 1320 | // there should be a comment of the form // ERROR "regexp". |
| 1321 | // If outStr has an error for a line which has no such comment, |
| 1322 | // this function will report an error. |
| 1323 | // Likewise if outStr does not have an error for a line which has a comment, |
| 1324 | // or if the error message does not match the <regexp>. |
Colin Cross | 1371fe4 | 2019-03-19 21:08:48 -0700 | [diff] [blame] | 1325 | // The <regexp> syntax is Perl but it's best to stick to egrep. |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 1326 | // |
| 1327 | // Sources files are supplied as fullshort slice. |
Colin Cross | 1371fe4 | 2019-03-19 21:08:48 -0700 | [diff] [blame] | 1328 | // It consists of pairs: full path to source file and its base name. |
Dan Willemsen | bbdf664 | 2017-01-13 22:57:23 -0800 | [diff] [blame] | 1329 | func (t *test) errorCheck(outStr string, wantAuto bool, fullshort ...string) (err error) { |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 1330 | defer func() { |
| 1331 | if *verbose && err != nil { |
| 1332 | log.Printf("%s gc output:\n%s", t, outStr) |
| 1333 | } |
| 1334 | }() |
| 1335 | var errs []error |
Dan Willemsen | bbdf664 | 2017-01-13 22:57:23 -0800 | [diff] [blame] | 1336 | out := splitOutput(outStr, wantAuto) |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 1337 | |
| 1338 | // Cut directory name. |
| 1339 | for i := range out { |
| 1340 | for j := 0; j < len(fullshort); j += 2 { |
| 1341 | full, short := fullshort[j], fullshort[j+1] |
| 1342 | out[i] = strings.Replace(out[i], full, short, -1) |
| 1343 | } |
| 1344 | } |
| 1345 | |
| 1346 | var want []wantedError |
| 1347 | for j := 0; j < len(fullshort); j += 2 { |
| 1348 | full, short := fullshort[j], fullshort[j+1] |
| 1349 | want = append(want, t.wantedErrors(full, short)...) |
| 1350 | } |
| 1351 | |
| 1352 | for _, we := range want { |
| 1353 | var errmsgs []string |
Dan Willemsen | bbdf664 | 2017-01-13 22:57:23 -0800 | [diff] [blame] | 1354 | if we.auto { |
| 1355 | errmsgs, out = partitionStrings("<autogenerated>", out) |
| 1356 | } else { |
| 1357 | errmsgs, out = partitionStrings(we.prefix, out) |
| 1358 | } |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 1359 | if len(errmsgs) == 0 { |
| 1360 | errs = append(errs, fmt.Errorf("%s:%d: missing error %q", we.file, we.lineNum, we.reStr)) |
| 1361 | continue |
| 1362 | } |
| 1363 | matched := false |
| 1364 | n := len(out) |
| 1365 | for _, errmsg := range errmsgs { |
Dan Willemsen | bbdf664 | 2017-01-13 22:57:23 -0800 | [diff] [blame] | 1366 | // Assume errmsg says "file:line: foo". |
| 1367 | // Cut leading "file:line: " to avoid accidental matching of file name instead of message. |
| 1368 | text := errmsg |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 1369 | if _, suffix, ok := strings.Cut(text, " "); ok { |
| 1370 | text = suffix |
Dan Willemsen | bbdf664 | 2017-01-13 22:57:23 -0800 | [diff] [blame] | 1371 | } |
| 1372 | if we.re.MatchString(text) { |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 1373 | matched = true |
| 1374 | } else { |
| 1375 | out = append(out, errmsg) |
| 1376 | } |
| 1377 | } |
| 1378 | if !matched { |
| 1379 | errs = append(errs, fmt.Errorf("%s:%d: no match for %#q in:\n\t%s", we.file, we.lineNum, we.reStr, strings.Join(out[n:], "\n\t"))) |
| 1380 | continue |
| 1381 | } |
| 1382 | } |
| 1383 | |
| 1384 | if len(out) > 0 { |
| 1385 | errs = append(errs, fmt.Errorf("Unmatched Errors:")) |
| 1386 | for _, errLine := range out { |
| 1387 | errs = append(errs, fmt.Errorf("%s", errLine)) |
| 1388 | } |
| 1389 | } |
| 1390 | |
| 1391 | if len(errs) == 0 { |
| 1392 | return nil |
| 1393 | } |
| 1394 | if len(errs) == 1 { |
| 1395 | return errs[0] |
| 1396 | } |
| 1397 | var buf bytes.Buffer |
| 1398 | fmt.Fprintf(&buf, "\n") |
| 1399 | for _, err := range errs { |
| 1400 | fmt.Fprintf(&buf, "%s\n", err.Error()) |
| 1401 | } |
| 1402 | return errors.New(buf.String()) |
Dan Willemsen | 6ff2325 | 2015-09-15 13:49:18 -0700 | [diff] [blame] | 1403 | } |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 1404 | |
Dan Willemsen | 0c15709 | 2016-07-08 13:57:52 -0700 | [diff] [blame] | 1405 | func (t *test) updateErrors(out, file string) { |
| 1406 | base := path.Base(file) |
Dan Willemsen | 6ff2325 | 2015-09-15 13:49:18 -0700 | [diff] [blame] | 1407 | // Read in source file. |
| 1408 | src, err := ioutil.ReadFile(file) |
| 1409 | if err != nil { |
| 1410 | fmt.Fprintln(os.Stderr, err) |
| 1411 | return |
| 1412 | } |
| 1413 | lines := strings.Split(string(src), "\n") |
| 1414 | // Remove old errors. |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 1415 | for i := range lines { |
| 1416 | lines[i], _, _ = strings.Cut(lines[i], " // ERROR ") |
Dan Willemsen | 6ff2325 | 2015-09-15 13:49:18 -0700 | [diff] [blame] | 1417 | } |
| 1418 | // Parse new errors. |
| 1419 | errors := make(map[int]map[string]bool) |
| 1420 | tmpRe := regexp.MustCompile(`autotmp_[0-9]+`) |
Dan Willemsen | bbdf664 | 2017-01-13 22:57:23 -0800 | [diff] [blame] | 1421 | for _, errStr := range splitOutput(out, false) { |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 1422 | errFile, rest, ok := strings.Cut(errStr, ":") |
| 1423 | if !ok || errFile != file { |
Dan Willemsen | 6ff2325 | 2015-09-15 13:49:18 -0700 | [diff] [blame] | 1424 | continue |
| 1425 | } |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 1426 | lineStr, msg, ok := strings.Cut(rest, ":") |
| 1427 | if !ok { |
Dan Willemsen | 6ff2325 | 2015-09-15 13:49:18 -0700 | [diff] [blame] | 1428 | continue |
| 1429 | } |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 1430 | line, err := strconv.Atoi(lineStr) |
Dan Willemsen | 6ff2325 | 2015-09-15 13:49:18 -0700 | [diff] [blame] | 1431 | line-- |
| 1432 | if err != nil || line < 0 || line >= len(lines) { |
| 1433 | continue |
| 1434 | } |
Dan Willemsen | 0c15709 | 2016-07-08 13:57:52 -0700 | [diff] [blame] | 1435 | msg = strings.Replace(msg, file, base, -1) // normalize file mentions in error itself |
| 1436 | msg = strings.TrimLeft(msg, " \t") |
Colin Cross | 1371fe4 | 2019-03-19 21:08:48 -0700 | [diff] [blame] | 1437 | for _, r := range []string{`\`, `*`, `+`, `?`, `[`, `]`, `(`, `)`} { |
Dan Willemsen | 6ff2325 | 2015-09-15 13:49:18 -0700 | [diff] [blame] | 1438 | msg = strings.Replace(msg, r, `\`+r, -1) |
| 1439 | } |
| 1440 | msg = strings.Replace(msg, `"`, `.`, -1) |
| 1441 | msg = tmpRe.ReplaceAllLiteralString(msg, `autotmp_[0-9]+`) |
| 1442 | if errors[line] == nil { |
| 1443 | errors[line] = make(map[string]bool) |
| 1444 | } |
| 1445 | errors[line][msg] = true |
| 1446 | } |
| 1447 | // Add new errors. |
| 1448 | for line, errs := range errors { |
| 1449 | var sorted []string |
| 1450 | for e := range errs { |
| 1451 | sorted = append(sorted, e) |
| 1452 | } |
| 1453 | sort.Strings(sorted) |
| 1454 | lines[line] += " // ERROR" |
| 1455 | for _, e := range sorted { |
| 1456 | lines[line] += fmt.Sprintf(` "%s$"`, e) |
| 1457 | } |
| 1458 | } |
| 1459 | // Write new file. |
| 1460 | err = ioutil.WriteFile(file, []byte(strings.Join(lines, "\n")), 0640) |
| 1461 | if err != nil { |
| 1462 | fmt.Fprintln(os.Stderr, err) |
| 1463 | return |
| 1464 | } |
| 1465 | // Polish. |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 1466 | exec.Command(goTool(), "fmt", file).CombinedOutput() |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 1467 | } |
| 1468 | |
| 1469 | // matchPrefix reports whether s is of the form ^(.*/)?prefix(:|[), |
| 1470 | // That is, it needs the file name prefix followed by a : or a [, |
| 1471 | // and possibly preceded by a directory name. |
| 1472 | func matchPrefix(s, prefix string) bool { |
| 1473 | i := strings.Index(s, ":") |
| 1474 | if i < 0 { |
| 1475 | return false |
| 1476 | } |
| 1477 | j := strings.LastIndex(s[:i], "/") |
| 1478 | s = s[j+1:] |
| 1479 | if len(s) <= len(prefix) || s[:len(prefix)] != prefix { |
| 1480 | return false |
| 1481 | } |
| 1482 | switch s[len(prefix)] { |
| 1483 | case '[', ':': |
| 1484 | return true |
| 1485 | } |
| 1486 | return false |
| 1487 | } |
| 1488 | |
| 1489 | func partitionStrings(prefix string, strs []string) (matched, unmatched []string) { |
| 1490 | for _, s := range strs { |
| 1491 | if matchPrefix(s, prefix) { |
| 1492 | matched = append(matched, s) |
| 1493 | } else { |
| 1494 | unmatched = append(unmatched, s) |
| 1495 | } |
| 1496 | } |
| 1497 | return |
| 1498 | } |
| 1499 | |
| 1500 | type wantedError struct { |
| 1501 | reStr string |
| 1502 | re *regexp.Regexp |
| 1503 | lineNum int |
Dan Willemsen | bbdf664 | 2017-01-13 22:57:23 -0800 | [diff] [blame] | 1504 | auto bool // match <autogenerated> line |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 1505 | file string |
| 1506 | prefix string |
| 1507 | } |
| 1508 | |
| 1509 | var ( |
| 1510 | errRx = regexp.MustCompile(`// (?:GC_)?ERROR (.*)`) |
Dan Willemsen | bbdf664 | 2017-01-13 22:57:23 -0800 | [diff] [blame] | 1511 | errAutoRx = regexp.MustCompile(`// (?:GC_)?ERRORAUTO (.*)`) |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 1512 | errQuotesRx = regexp.MustCompile(`"([^"]*)"`) |
| 1513 | lineRx = regexp.MustCompile(`LINE(([+-])([0-9]+))?`) |
| 1514 | ) |
| 1515 | |
| 1516 | func (t *test) wantedErrors(file, short string) (errs []wantedError) { |
| 1517 | cache := make(map[string]*regexp.Regexp) |
| 1518 | |
| 1519 | src, _ := ioutil.ReadFile(file) |
| 1520 | for i, line := range strings.Split(string(src), "\n") { |
| 1521 | lineNum := i + 1 |
| 1522 | if strings.Contains(line, "////") { |
| 1523 | // double comment disables ERROR |
| 1524 | continue |
| 1525 | } |
Dan Willemsen | bbdf664 | 2017-01-13 22:57:23 -0800 | [diff] [blame] | 1526 | var auto bool |
| 1527 | m := errAutoRx.FindStringSubmatch(line) |
| 1528 | if m != nil { |
| 1529 | auto = true |
| 1530 | } else { |
| 1531 | m = errRx.FindStringSubmatch(line) |
| 1532 | } |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 1533 | if m == nil { |
| 1534 | continue |
| 1535 | } |
| 1536 | all := m[1] |
| 1537 | mm := errQuotesRx.FindAllStringSubmatch(all, -1) |
| 1538 | if mm == nil { |
| 1539 | log.Fatalf("%s:%d: invalid errchk line: %s", t.goFileName(), lineNum, line) |
| 1540 | } |
| 1541 | for _, m := range mm { |
| 1542 | rx := lineRx.ReplaceAllStringFunc(m[1], func(m string) string { |
| 1543 | n := lineNum |
| 1544 | if strings.HasPrefix(m, "LINE+") { |
| 1545 | delta, _ := strconv.Atoi(m[5:]) |
| 1546 | n += delta |
| 1547 | } else if strings.HasPrefix(m, "LINE-") { |
| 1548 | delta, _ := strconv.Atoi(m[5:]) |
| 1549 | n -= delta |
| 1550 | } |
| 1551 | return fmt.Sprintf("%s:%d", short, n) |
| 1552 | }) |
| 1553 | re := cache[rx] |
| 1554 | if re == nil { |
| 1555 | var err error |
| 1556 | re, err = regexp.Compile(rx) |
| 1557 | if err != nil { |
Dan Willemsen | 6ff2325 | 2015-09-15 13:49:18 -0700 | [diff] [blame] | 1558 | log.Fatalf("%s:%d: invalid regexp \"%s\" in ERROR line: %v", t.goFileName(), lineNum, rx, err) |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 1559 | } |
| 1560 | cache[rx] = re |
| 1561 | } |
| 1562 | prefix := fmt.Sprintf("%s:%d", short, lineNum) |
| 1563 | errs = append(errs, wantedError{ |
| 1564 | reStr: rx, |
| 1565 | re: re, |
| 1566 | prefix: prefix, |
Dan Willemsen | bbdf664 | 2017-01-13 22:57:23 -0800 | [diff] [blame] | 1567 | auto: auto, |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 1568 | lineNum: lineNum, |
| 1569 | file: short, |
| 1570 | }) |
| 1571 | } |
| 1572 | } |
| 1573 | |
| 1574 | return |
| 1575 | } |
| 1576 | |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 1577 | const ( |
| 1578 | // Regexp to match a single opcode check: optionally begin with "-" (to indicate |
| 1579 | // a negative check), followed by a string literal enclosed in "" or ``. For "", |
| 1580 | // backslashes must be handled. |
| 1581 | reMatchCheck = `-?(?:\x60[^\x60]*\x60|"(?:[^"\\]|\\.)*")` |
| 1582 | ) |
| 1583 | |
| 1584 | var ( |
| 1585 | // Regexp to split a line in code and comment, trimming spaces |
Colin Cross | 1371fe4 | 2019-03-19 21:08:48 -0700 | [diff] [blame] | 1586 | rxAsmComment = regexp.MustCompile(`^\s*(.*?)\s*(?://\s*(.+)\s*)?$`) |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 1587 | |
Colin Cross | 1371fe4 | 2019-03-19 21:08:48 -0700 | [diff] [blame] | 1588 | // Regexp to extract an architecture check: architecture name (or triplet), |
| 1589 | // followed by semi-colon, followed by a comma-separated list of opcode checks. |
| 1590 | // Extraneous spaces are ignored. |
| 1591 | rxAsmPlatform = regexp.MustCompile(`(\w+)(/\w+)?(/\w*)?\s*:\s*(` + reMatchCheck + `(?:\s*,\s*` + reMatchCheck + `)*)`) |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 1592 | |
| 1593 | // Regexp to extract a single opcoded check |
| 1594 | rxAsmCheck = regexp.MustCompile(reMatchCheck) |
| 1595 | |
| 1596 | // List of all architecture variants. Key is the GOARCH architecture, |
| 1597 | // value[0] is the variant-changing environment variable, and values[1:] |
| 1598 | // are the supported variants. |
| 1599 | archVariants = map[string][]string{ |
Colin Cross | 846c316 | 2021-05-14 11:11:40 -0700 | [diff] [blame] | 1600 | "386": {"GO386", "sse2", "softfloat"}, |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 1601 | "amd64": {"GOAMD64", "v1", "v2", "v3", "v4"}, |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 1602 | "arm": {"GOARM", "5", "6", "7"}, |
| 1603 | "arm64": {}, |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 1604 | "loong64": {}, |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 1605 | "mips": {"GOMIPS", "hardfloat", "softfloat"}, |
| 1606 | "mips64": {"GOMIPS64", "hardfloat", "softfloat"}, |
Colin Cross | efed634 | 2019-09-07 08:34:44 -0700 | [diff] [blame] | 1607 | "ppc64": {"GOPPC64", "power8", "power9"}, |
| 1608 | "ppc64le": {"GOPPC64", "power8", "power9"}, |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 1609 | "s390x": {}, |
Colin Cross | efed634 | 2019-09-07 08:34:44 -0700 | [diff] [blame] | 1610 | "wasm": {}, |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 1611 | "riscv64": {}, |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 1612 | } |
| 1613 | ) |
| 1614 | |
| 1615 | // wantedAsmOpcode is a single asmcheck check |
| 1616 | type wantedAsmOpcode struct { |
| 1617 | fileline string // original source file/line (eg: "/path/foo.go:45") |
| 1618 | line int // original source line |
| 1619 | opcode *regexp.Regexp // opcode check to be performed on assembly output |
| 1620 | negative bool // true if the check is supposed to fail rather than pass |
| 1621 | found bool // true if the opcode check matched at least one in the output |
| 1622 | } |
| 1623 | |
| 1624 | // A build environment triplet separated by slashes (eg: linux/386/sse2). |
| 1625 | // The third field can be empty if the arch does not support variants (eg: "plan9/amd64/") |
| 1626 | type buildEnv string |
| 1627 | |
| 1628 | // Environ returns the environment it represents in cmd.Environ() "key=val" format |
| 1629 | // For instance, "linux/386/sse2".Environ() returns {"GOOS=linux", "GOARCH=386", "GO386=sse2"} |
| 1630 | func (b buildEnv) Environ() []string { |
| 1631 | fields := strings.Split(string(b), "/") |
| 1632 | if len(fields) != 3 { |
| 1633 | panic("invalid buildEnv string: " + string(b)) |
| 1634 | } |
| 1635 | env := []string{"GOOS=" + fields[0], "GOARCH=" + fields[1]} |
| 1636 | if fields[2] != "" { |
| 1637 | env = append(env, archVariants[fields[1]][0]+"="+fields[2]) |
| 1638 | } |
| 1639 | return env |
| 1640 | } |
| 1641 | |
| 1642 | // asmChecks represents all the asmcheck checks present in a test file |
| 1643 | // The outer map key is the build triplet in which the checks must be performed. |
| 1644 | // The inner map key represent the source file line ("filename.go:1234") at which the |
| 1645 | // checks must be performed. |
| 1646 | type asmChecks map[buildEnv]map[string][]wantedAsmOpcode |
| 1647 | |
| 1648 | // Envs returns all the buildEnv in which at least one check is present |
| 1649 | func (a asmChecks) Envs() []buildEnv { |
| 1650 | var envs []buildEnv |
| 1651 | for e := range a { |
| 1652 | envs = append(envs, e) |
| 1653 | } |
| 1654 | sort.Slice(envs, func(i, j int) bool { |
| 1655 | return string(envs[i]) < string(envs[j]) |
| 1656 | }) |
| 1657 | return envs |
| 1658 | } |
| 1659 | |
| 1660 | func (t *test) wantedAsmOpcodes(fn string) asmChecks { |
| 1661 | ops := make(asmChecks) |
| 1662 | |
| 1663 | comment := "" |
| 1664 | src, _ := ioutil.ReadFile(fn) |
| 1665 | for i, line := range strings.Split(string(src), "\n") { |
| 1666 | matches := rxAsmComment.FindStringSubmatch(line) |
| 1667 | code, cmt := matches[1], matches[2] |
| 1668 | |
| 1669 | // Keep comments pending in the comment variable until |
| 1670 | // we find a line that contains some code. |
| 1671 | comment += " " + cmt |
| 1672 | if code == "" { |
| 1673 | continue |
| 1674 | } |
| 1675 | |
| 1676 | // Parse and extract any architecture check from comments, |
| 1677 | // made by one architecture name and multiple checks. |
| 1678 | lnum := fn + ":" + strconv.Itoa(i+1) |
| 1679 | for _, ac := range rxAsmPlatform.FindAllStringSubmatch(comment, -1) { |
| 1680 | archspec, allchecks := ac[1:4], ac[4] |
| 1681 | |
| 1682 | var arch, subarch, os string |
| 1683 | switch { |
| 1684 | case archspec[2] != "": // 3 components: "linux/386/sse2" |
| 1685 | os, arch, subarch = archspec[0], archspec[1][1:], archspec[2][1:] |
| 1686 | case archspec[1] != "": // 2 components: "386/sse2" |
| 1687 | os, arch, subarch = "linux", archspec[0], archspec[1][1:] |
| 1688 | default: // 1 component: "386" |
| 1689 | os, arch, subarch = "linux", archspec[0], "" |
Colin Cross | efed634 | 2019-09-07 08:34:44 -0700 | [diff] [blame] | 1690 | if arch == "wasm" { |
| 1691 | os = "js" |
| 1692 | } |
Dan Willemsen | f3f2eb6 | 2018-08-28 11:28:58 -0700 | [diff] [blame] | 1693 | } |
| 1694 | |
| 1695 | if _, ok := archVariants[arch]; !ok { |
| 1696 | log.Fatalf("%s:%d: unsupported architecture: %v", t.goFileName(), i+1, arch) |
| 1697 | } |
| 1698 | |
| 1699 | // Create the build environments corresponding the above specifiers |
| 1700 | envs := make([]buildEnv, 0, 4) |
| 1701 | if subarch != "" { |
| 1702 | envs = append(envs, buildEnv(os+"/"+arch+"/"+subarch)) |
| 1703 | } else { |
| 1704 | subarchs := archVariants[arch] |
| 1705 | if len(subarchs) == 0 { |
| 1706 | envs = append(envs, buildEnv(os+"/"+arch+"/")) |
| 1707 | } else { |
| 1708 | for _, sa := range archVariants[arch][1:] { |
| 1709 | envs = append(envs, buildEnv(os+"/"+arch+"/"+sa)) |
| 1710 | } |
| 1711 | } |
| 1712 | } |
| 1713 | |
| 1714 | for _, m := range rxAsmCheck.FindAllString(allchecks, -1) { |
| 1715 | negative := false |
| 1716 | if m[0] == '-' { |
| 1717 | negative = true |
| 1718 | m = m[1:] |
| 1719 | } |
| 1720 | |
| 1721 | rxsrc, err := strconv.Unquote(m) |
| 1722 | if err != nil { |
| 1723 | log.Fatalf("%s:%d: error unquoting string: %v", t.goFileName(), i+1, err) |
| 1724 | } |
| 1725 | |
| 1726 | // Compile the checks as regular expressions. Notice that we |
| 1727 | // consider checks as matching from the beginning of the actual |
| 1728 | // assembler source (that is, what is left on each line of the |
| 1729 | // compile -S output after we strip file/line info) to avoid |
| 1730 | // trivial bugs such as "ADD" matching "FADD". This |
| 1731 | // doesn't remove genericity: it's still possible to write |
| 1732 | // something like "F?ADD", but we make common cases simpler |
| 1733 | // to get right. |
| 1734 | oprx, err := regexp.Compile("^" + rxsrc) |
| 1735 | if err != nil { |
| 1736 | log.Fatalf("%s:%d: %v", t.goFileName(), i+1, err) |
| 1737 | } |
| 1738 | |
| 1739 | for _, env := range envs { |
| 1740 | if ops[env] == nil { |
| 1741 | ops[env] = make(map[string][]wantedAsmOpcode) |
| 1742 | } |
| 1743 | ops[env][lnum] = append(ops[env][lnum], wantedAsmOpcode{ |
| 1744 | negative: negative, |
| 1745 | fileline: lnum, |
| 1746 | line: i + 1, |
| 1747 | opcode: oprx, |
| 1748 | }) |
| 1749 | } |
| 1750 | } |
| 1751 | } |
| 1752 | comment = "" |
| 1753 | } |
| 1754 | |
| 1755 | return ops |
| 1756 | } |
| 1757 | |
| 1758 | func (t *test) asmCheck(outStr string, fn string, env buildEnv, fullops map[string][]wantedAsmOpcode) (err error) { |
| 1759 | // The assembly output contains the concatenated dump of multiple functions. |
| 1760 | // the first line of each function begins at column 0, while the rest is |
| 1761 | // indented by a tabulation. These data structures help us index the |
| 1762 | // output by function. |
| 1763 | functionMarkers := make([]int, 1) |
| 1764 | lineFuncMap := make(map[string]int) |
| 1765 | |
| 1766 | lines := strings.Split(outStr, "\n") |
| 1767 | rxLine := regexp.MustCompile(fmt.Sprintf(`\((%s:\d+)\)\s+(.*)`, regexp.QuoteMeta(fn))) |
| 1768 | |
| 1769 | for nl, line := range lines { |
| 1770 | // Check if this line begins a function |
| 1771 | if len(line) > 0 && line[0] != '\t' { |
| 1772 | functionMarkers = append(functionMarkers, nl) |
| 1773 | } |
| 1774 | |
| 1775 | // Search if this line contains a assembly opcode (which is prefixed by the |
| 1776 | // original source file/line in parenthesis) |
| 1777 | matches := rxLine.FindStringSubmatch(line) |
| 1778 | if len(matches) == 0 { |
| 1779 | continue |
| 1780 | } |
| 1781 | srcFileLine, asm := matches[1], matches[2] |
| 1782 | |
| 1783 | // Associate the original file/line information to the current |
| 1784 | // function in the output; it will be useful to dump it in case |
| 1785 | // of error. |
| 1786 | lineFuncMap[srcFileLine] = len(functionMarkers) - 1 |
| 1787 | |
| 1788 | // If there are opcode checks associated to this source file/line, |
| 1789 | // run the checks. |
| 1790 | if ops, found := fullops[srcFileLine]; found { |
| 1791 | for i := range ops { |
| 1792 | if !ops[i].found && ops[i].opcode.FindString(asm) != "" { |
| 1793 | ops[i].found = true |
| 1794 | } |
| 1795 | } |
| 1796 | } |
| 1797 | } |
| 1798 | functionMarkers = append(functionMarkers, len(lines)) |
| 1799 | |
| 1800 | var failed []wantedAsmOpcode |
| 1801 | for _, ops := range fullops { |
| 1802 | for _, o := range ops { |
| 1803 | // There's a failure if a negative match was found, |
| 1804 | // or a positive match was not found. |
| 1805 | if o.negative == o.found { |
| 1806 | failed = append(failed, o) |
| 1807 | } |
| 1808 | } |
| 1809 | } |
| 1810 | if len(failed) == 0 { |
| 1811 | return |
| 1812 | } |
| 1813 | |
| 1814 | // At least one asmcheck failed; report them |
| 1815 | sort.Slice(failed, func(i, j int) bool { |
| 1816 | return failed[i].line < failed[j].line |
| 1817 | }) |
| 1818 | |
| 1819 | lastFunction := -1 |
| 1820 | var errbuf bytes.Buffer |
| 1821 | fmt.Fprintln(&errbuf) |
| 1822 | for _, o := range failed { |
| 1823 | // Dump the function in which this opcode check was supposed to |
| 1824 | // pass but failed. |
| 1825 | funcIdx := lineFuncMap[o.fileline] |
| 1826 | if funcIdx != 0 && funcIdx != lastFunction { |
| 1827 | funcLines := lines[functionMarkers[funcIdx]:functionMarkers[funcIdx+1]] |
| 1828 | log.Println(strings.Join(funcLines, "\n")) |
| 1829 | lastFunction = funcIdx // avoid printing same function twice |
| 1830 | } |
| 1831 | |
| 1832 | if o.negative { |
| 1833 | fmt.Fprintf(&errbuf, "%s:%d: %s: wrong opcode found: %q\n", t.goFileName(), o.line, env, o.opcode.String()) |
| 1834 | } else { |
| 1835 | fmt.Fprintf(&errbuf, "%s:%d: %s: opcode not found: %q\n", t.goFileName(), o.line, env, o.opcode.String()) |
| 1836 | } |
| 1837 | } |
| 1838 | err = errors.New(errbuf.String()) |
| 1839 | return |
| 1840 | } |
| 1841 | |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 1842 | // defaultRunOutputLimit returns the number of runoutput tests that |
| 1843 | // can be executed in parallel. |
| 1844 | func defaultRunOutputLimit() int { |
| 1845 | const maxArmCPU = 2 |
| 1846 | |
| 1847 | cpu := runtime.NumCPU() |
| 1848 | if runtime.GOARCH == "arm" && cpu > maxArmCPU { |
| 1849 | cpu = maxArmCPU |
| 1850 | } |
| 1851 | return cpu |
| 1852 | } |
| 1853 | |
| 1854 | // checkShouldTest runs sanity checks on the shouldTest function. |
| 1855 | func checkShouldTest() { |
| 1856 | assert := func(ok bool, _ string) { |
| 1857 | if !ok { |
| 1858 | panic("fail") |
| 1859 | } |
| 1860 | } |
| 1861 | assertNot := func(ok bool, _ string) { assert(!ok, "") } |
| 1862 | |
| 1863 | // Simple tests. |
| 1864 | assert(shouldTest("// +build linux", "linux", "arm")) |
| 1865 | assert(shouldTest("// +build !windows", "linux", "arm")) |
| 1866 | assertNot(shouldTest("// +build !windows", "windows", "amd64")) |
| 1867 | |
| 1868 | // A file with no build tags will always be tested. |
| 1869 | assert(shouldTest("// This is a test.", "os", "arch")) |
| 1870 | |
| 1871 | // Build tags separated by a space are OR-ed together. |
| 1872 | assertNot(shouldTest("// +build arm 386", "linux", "amd64")) |
| 1873 | |
| 1874 | // Build tags separated by a comma are AND-ed together. |
| 1875 | assertNot(shouldTest("// +build !windows,!plan9", "windows", "amd64")) |
| 1876 | assertNot(shouldTest("// +build !windows,!plan9", "plan9", "386")) |
| 1877 | |
| 1878 | // Build tags on multiple lines are AND-ed together. |
| 1879 | assert(shouldTest("// +build !windows\n// +build amd64", "linux", "amd64")) |
| 1880 | assertNot(shouldTest("// +build !windows\n// +build amd64", "windows", "amd64")) |
| 1881 | |
| 1882 | // Test that (!a OR !b) matches anything. |
| 1883 | assert(shouldTest("// +build !windows !plan9", "windows", "amd64")) |
| 1884 | } |
| 1885 | |
Colin Cross | 7bb052a | 2015-02-03 12:59:37 -0800 | [diff] [blame] | 1886 | func getenv(key, def string) string { |
| 1887 | value := os.Getenv(key) |
| 1888 | if value != "" { |
| 1889 | return value |
| 1890 | } |
| 1891 | return def |
| 1892 | } |
Patrice Arruda | 7f4776e | 2020-06-25 11:55:41 -0700 | [diff] [blame] | 1893 | |
| 1894 | // overlayDir makes a minimal-overhead copy of srcRoot in which new files may be added. |
| 1895 | func overlayDir(dstRoot, srcRoot string) error { |
| 1896 | dstRoot = filepath.Clean(dstRoot) |
| 1897 | if err := os.MkdirAll(dstRoot, 0777); err != nil { |
| 1898 | return err |
| 1899 | } |
| 1900 | |
| 1901 | srcRoot, err := filepath.Abs(srcRoot) |
| 1902 | if err != nil { |
| 1903 | return err |
| 1904 | } |
| 1905 | |
Colin Cross | 846c316 | 2021-05-14 11:11:40 -0700 | [diff] [blame] | 1906 | return filepath.WalkDir(srcRoot, func(srcPath string, d fs.DirEntry, err error) error { |
Patrice Arruda | 7f4776e | 2020-06-25 11:55:41 -0700 | [diff] [blame] | 1907 | if err != nil || srcPath == srcRoot { |
| 1908 | return err |
| 1909 | } |
| 1910 | |
| 1911 | suffix := strings.TrimPrefix(srcPath, srcRoot) |
| 1912 | for len(suffix) > 0 && suffix[0] == filepath.Separator { |
| 1913 | suffix = suffix[1:] |
| 1914 | } |
| 1915 | dstPath := filepath.Join(dstRoot, suffix) |
| 1916 | |
Colin Cross | 846c316 | 2021-05-14 11:11:40 -0700 | [diff] [blame] | 1917 | var info fs.FileInfo |
| 1918 | if d.Type()&os.ModeSymlink != 0 { |
Patrice Arruda | 7f4776e | 2020-06-25 11:55:41 -0700 | [diff] [blame] | 1919 | info, err = os.Stat(srcPath) |
Colin Cross | 846c316 | 2021-05-14 11:11:40 -0700 | [diff] [blame] | 1920 | } else { |
| 1921 | info, err = d.Info() |
Patrice Arruda | 7f4776e | 2020-06-25 11:55:41 -0700 | [diff] [blame] | 1922 | } |
Colin Cross | 846c316 | 2021-05-14 11:11:40 -0700 | [diff] [blame] | 1923 | if err != nil { |
| 1924 | return err |
| 1925 | } |
| 1926 | perm := info.Mode() & os.ModePerm |
Patrice Arruda | 7f4776e | 2020-06-25 11:55:41 -0700 | [diff] [blame] | 1927 | |
| 1928 | // Always copy directories (don't symlink them). |
| 1929 | // If we add a file in the overlay, we don't want to add it in the original. |
| 1930 | if info.IsDir() { |
| 1931 | return os.MkdirAll(dstPath, perm|0200) |
| 1932 | } |
| 1933 | |
| 1934 | // If the OS supports symlinks, use them instead of copying bytes. |
| 1935 | if err := os.Symlink(srcPath, dstPath); err == nil { |
| 1936 | return nil |
| 1937 | } |
| 1938 | |
| 1939 | // Otherwise, copy the bytes. |
| 1940 | src, err := os.Open(srcPath) |
| 1941 | if err != nil { |
| 1942 | return err |
| 1943 | } |
| 1944 | defer src.Close() |
| 1945 | |
| 1946 | dst, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm) |
| 1947 | if err != nil { |
| 1948 | return err |
| 1949 | } |
| 1950 | |
| 1951 | _, err = io.Copy(dst, src) |
| 1952 | if closeErr := dst.Close(); err == nil { |
| 1953 | err = closeErr |
| 1954 | } |
| 1955 | return err |
| 1956 | }) |
| 1957 | } |
Dan Willemsen | 31b9b84 | 2021-08-31 12:51:40 -0700 | [diff] [blame] | 1958 | |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 1959 | // The following sets of files are excluded from testing depending on configuration. |
| 1960 | // The types2Failures(32Bit) files pass with the 1.17 compiler but don't pass with |
| 1961 | // the 1.18 compiler using the new types2 type checker, or pass with sub-optimal |
| 1962 | // error(s). |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 1963 | |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 1964 | // List of files that the compiler cannot errorcheck with the new typechecker (types2). |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 1965 | var types2Failures = setOf( |
Dan Willemsen | 14b5f99 | 2022-03-10 14:27:21 -0800 | [diff] [blame] | 1966 | "notinheap.go", // types2 doesn't report errors about conversions that are invalid due to //go:notinheap |
| 1967 | "shift1.go", // types2 reports two new errors which are probably not right |
| 1968 | "fixedbugs/issue10700.go", // types2 should give hint about ptr to interface |
| 1969 | "fixedbugs/issue18331.go", // missing error about misuse of //go:noescape (irgen needs code from noder) |
| 1970 | "fixedbugs/issue18419.go", // types2 reports no field or method member, but should say unexported |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 1971 | "fixedbugs/issue20233.go", // types2 reports two instead of one error (preference: 1.17 compiler) |
| 1972 | "fixedbugs/issue20245.go", // types2 reports two instead of one error (preference: 1.17 compiler) |
Dan Willemsen | 14b5f99 | 2022-03-10 14:27:21 -0800 | [diff] [blame] | 1973 | "fixedbugs/issue31053.go", // types2 reports "unknown field" instead of "cannot refer to unexported field" |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 1974 | ) |
| 1975 | |
| 1976 | var types2Failures32Bit = setOf( |
| 1977 | "printbig.go", // large untyped int passed to print (32-bit) |
| 1978 | "fixedbugs/bug114.go", // large untyped int passed to println (32-bit) |
| 1979 | "fixedbugs/issue23305.go", // large untyped int passed to println (32-bit) |
| 1980 | ) |
| 1981 | |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 1982 | var go118Failures = setOf( |
| 1983 | "typeparam/nested.go", // 1.18 compiler doesn't support function-local types with generics |
| 1984 | "typeparam/issue51521.go", // 1.18 compiler produces bad panic message and link error |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 1985 | ) |
| 1986 | |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 1987 | // In all of these cases, the 1.17 compiler reports reasonable errors, but either the |
| 1988 | // 1.17 or 1.18 compiler report extra errors, so we can't match correctly on both. We |
| 1989 | // now set the patterns to match correctly on all the 1.18 errors. |
| 1990 | // This list remains here just as a reference and for comparison - these files all pass. |
| 1991 | var _ = setOf( |
Dan Willemsen | 14b5f99 | 2022-03-10 14:27:21 -0800 | [diff] [blame] | 1992 | "import1.go", // types2 reports extra errors |
| 1993 | "initializerr.go", // types2 reports extra error |
| 1994 | "typecheck.go", // types2 reports extra error at function call |
| 1995 | |
| 1996 | "fixedbugs/bug176.go", // types2 reports all errors (pref: types2) |
| 1997 | "fixedbugs/bug195.go", // types2 reports slight different errors, and an extra error |
| 1998 | "fixedbugs/bug412.go", // types2 produces a follow-on error |
| 1999 | |
| 2000 | "fixedbugs/issue11614.go", // types2 reports an extra error |
| 2001 | "fixedbugs/issue17038.go", // types2 doesn't report a follow-on error (pref: types2) |
| 2002 | "fixedbugs/issue23732.go", // types2 reports different (but ok) line numbers |
| 2003 | "fixedbugs/issue4510.go", // types2 reports different (but ok) line numbers |
| 2004 | "fixedbugs/issue7525b.go", // types2 reports init cycle error on different line - ok otherwise |
| 2005 | "fixedbugs/issue7525c.go", // types2 reports init cycle error on different line - ok otherwise |
| 2006 | "fixedbugs/issue7525d.go", // types2 reports init cycle error on different line - ok otherwise |
| 2007 | "fixedbugs/issue7525e.go", // types2 reports init cycle error on different line - ok otherwise |
| 2008 | "fixedbugs/issue7525.go", // types2 reports init cycle error on different line - ok otherwise |
| 2009 | ) |
| 2010 | |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 2011 | var unifiedFailures = setOf( |
| 2012 | "closure3.go", // unified IR numbers closures differently than -d=inlfuncswithclosures |
| 2013 | "escape4.go", // unified IR can inline f5 and f6; test doesn't expect this |
| 2014 | "inline.go", // unified IR reports function literal diagnostics on different lines than -d=inlfuncswithclosures |
| 2015 | "linkname3.go", // unified IR is missing some linkname errors |
| 2016 | |
| 2017 | "fixedbugs/issue42284.go", // prints "T(0) does not escape", but test expects "a.I(a.T(0)) does not escape" |
| 2018 | "fixedbugs/issue7921.go", // prints "… escapes to heap", but test expects "string(…) escapes to heap" |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 2019 | "typeparam/issue47631.go", // unified IR can handle local type declarations |
| 2020 | "fixedbugs/issue42058a.go", // unified IR doesn't report channel element too large |
| 2021 | "fixedbugs/issue42058b.go", // unified IR doesn't report channel element too large |
| 2022 | "fixedbugs/issue49767.go", // unified IR doesn't report channel element too large |
| 2023 | "fixedbugs/issue49814.go", // unified IR doesn't report array type too large |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 2024 | ) |
| 2025 | |
| 2026 | func setOf(keys ...string) map[string]bool { |
| 2027 | m := make(map[string]bool, len(keys)) |
| 2028 | for _, key := range keys { |
| 2029 | m[key] = true |
| 2030 | } |
| 2031 | return m |
| 2032 | } |
| 2033 | |
| 2034 | // splitQuoted splits the string s around each instance of one or more consecutive |
| 2035 | // white space characters while taking into account quotes and escaping, and |
| 2036 | // returns an array of substrings of s or an empty list if s contains only white space. |
| 2037 | // Single quotes and double quotes are recognized to prevent splitting within the |
| 2038 | // quoted region, and are removed from the resulting substrings. If a quote in s |
| 2039 | // isn't closed err will be set and r will have the unclosed argument as the |
| 2040 | // last element. The backslash is used for escaping. |
| 2041 | // |
| 2042 | // For example, the following string: |
| 2043 | // |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 2044 | // a b:"c d" 'e''f' "g\"" |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 2045 | // |
| 2046 | // Would be parsed as: |
| 2047 | // |
Dan Willemsen | 1cbb82a | 2022-08-08 23:13:50 -0700 | [diff] [blame] | 2048 | // []string{"a", "b:c d", "ef", `g"`} |
Dan Willemsen | 59ee780 | 2021-12-15 01:08:25 -0800 | [diff] [blame] | 2049 | // |
| 2050 | // [copied from src/go/build/build.go] |
| 2051 | func splitQuoted(s string) (r []string, err error) { |
| 2052 | var args []string |
| 2053 | arg := make([]rune, len(s)) |
| 2054 | escaped := false |
| 2055 | quoted := false |
| 2056 | quote := '\x00' |
| 2057 | i := 0 |
| 2058 | for _, rune := range s { |
| 2059 | switch { |
| 2060 | case escaped: |
| 2061 | escaped = false |
| 2062 | case rune == '\\': |
| 2063 | escaped = true |
| 2064 | continue |
| 2065 | case quote != '\x00': |
| 2066 | if rune == quote { |
| 2067 | quote = '\x00' |
| 2068 | continue |
| 2069 | } |
| 2070 | case rune == '"' || rune == '\'': |
| 2071 | quoted = true |
| 2072 | quote = rune |
| 2073 | continue |
| 2074 | case unicode.IsSpace(rune): |
| 2075 | if quoted || i > 0 { |
| 2076 | quoted = false |
| 2077 | args = append(args, string(arg[:i])) |
| 2078 | i = 0 |
| 2079 | } |
| 2080 | continue |
| 2081 | } |
| 2082 | arg[i] = rune |
| 2083 | i++ |
| 2084 | } |
| 2085 | if quoted || i > 0 { |
| 2086 | args = append(args, string(arg[:i])) |
| 2087 | } |
| 2088 | if quote != 0 { |
| 2089 | err = errors.New("unclosed quote") |
| 2090 | } else if escaped { |
| 2091 | err = errors.New("unfinished escaping") |
| 2092 | } |
| 2093 | return args, err |
Dan Willemsen | 31b9b84 | 2021-08-31 12:51:40 -0700 | [diff] [blame] | 2094 | } |