// Copyright 2015 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // run runs the docs tests found in this directory. package main import ( "bytes" "flag" "fmt" "io/ioutil" "os" "os/exec" "path/filepath" "regexp" "runtime" "strings" "time" ) const usage = `go run run.go [tests] run.go runs the docs tests in this directory. If no tests are provided, it runs all tests. Tests may be specified without their .go suffix. ` func main() { start := time.Now() flag.Usage = func() { fmt.Fprintf(os.Stderr, usage) flag.PrintDefaults() os.Exit(2) } flag.Parse() if flag.NArg() == 0 { // run all tests fixcgo() } else { // run specified tests onlyTest(flag.Args()...) } tmpdir, err := ioutil.TempDir("", "go-progs") if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } // ratec limits the number of tests running concurrently. // None of the tests are intensive, so don't bother // trying to manually adjust for slow builders. ratec := make(chan bool, runtime.NumCPU()) errc := make(chan error, len(tests)) for _, tt := range tests { tt := tt ratec <- true go func() { errc <- test(tmpdir, tt.file, tt.want) <-ratec }() } var rc int for range tests { if err := <-errc; err != nil { fmt.Fprintln(os.Stderr, err) rc = 1 } } os.Remove(tmpdir) if rc == 0 { fmt.Printf("ok\t%s\t%s\n", filepath.Base(os.Args[0]), time.Since(start).Round(time.Millisecond)) } os.Exit(rc) } // test builds the test in the given file. // If want is non-empty, test also runs the test // and checks that the output matches the regexp want. func test(tmpdir, file, want string) error { // Build the program. prog := filepath.Join(tmpdir, file+".exe") cmd := exec.Command("go", "build", "-o", prog, file+".go") out, err := cmd.CombinedOutput() if err != nil { return fmt.Errorf("go build %s.go failed: %v\nOutput:\n%s", file, err, out) } defer os.Remove(prog) // Only run the test if we have output to check. if want == "" { return nil } cmd = exec.Command(prog) out, err = cmd.CombinedOutput() if err != nil { return fmt.Errorf("%s failed: %v\nOutput:\n%s", file, err, out) } // Canonicalize output. out = bytes.TrimRight(out, "\n") out = bytes.ReplaceAll(out, []byte{'\n'}, []byte{' '}) // Check the result. match, err := regexp.Match(want, out) if err != nil { return fmt.Errorf("failed to parse regexp %q: %v", want, err) } if !match { return fmt.Errorf("%s.go:\n%q\ndoes not match %s", file, out, want) } return nil } type testcase struct { file string want string } var tests = []testcase{ // defer_panic_recover {"defer", `^0 3210 2$`}, {"defer2", `^Calling g. Printing in g 0 Printing in g 1 Printing in g 2 Printing in g 3 Panicking! Defer in g 3 Defer in g 2 Defer in g 1 Defer in g 0 Recovered in f 4 Returned normally from f.$`}, // effective_go {"eff_bytesize", `^1.00YB 9.09TB$`}, {"eff_qr", ""}, {"eff_sequence", `^\[-1 2 6 16 44\]$`}, {"eff_unused2", ""}, // error_handling {"error", ""}, {"error2", ""}, {"error3", ""}, {"error4", ""}, // law_of_reflection {"interface", ""}, {"interface2", `^type: float64$`}, // c_go_cgo {"cgo1", ""}, {"cgo2", ""}, {"cgo3", ""}, {"cgo4", ""}, // timeout {"timeout1", ""}, {"timeout2", ""}, // gobs {"gobs1", ""}, {"gobs2", ""}, // json {"json1", `^$`}, {"json2", `the reciprocal of i is`}, {"json3", `Age is int 6`}, {"json4", `^$`}, {"json5", ""}, // image_package {"image_package1", `^X is 2 Y is 1$`}, {"image_package2", `^3 4 false$`}, {"image_package3", `^3 4 true$`}, {"image_package4", `^image.Point{X:2, Y:1}$`}, {"image_package5", `^{255 0 0 255}$`}, {"image_package6", `^8 4 true$`}, // other {"go1", `^Christmas is a holiday: true .*go1.go already exists$`}, {"slices", ""}, } func onlyTest(files ...string) { var new []testcase NextFile: for _, file := range files { file = strings.TrimSuffix(file, ".go") for _, tt := range tests { if tt.file == file { new = append(new, tt) continue NextFile } } fmt.Fprintf(os.Stderr, "test %s.go not found\n", file) os.Exit(1) } tests = new } func skipTest(file string) { for i, tt := range tests { if tt.file == file { copy(tests[i:], tests[i+1:]) tests = tests[:len(tests)-1] return } } panic("delete(" + file + "): not found") } func fixcgo() { if os.Getenv("CGO_ENABLED") != "1" { skipTest("cgo1") skipTest("cgo2") skipTest("cgo3") skipTest("cgo4") return } switch runtime.GOOS { case "freebsd": // cgo1 and cgo2 don't run on freebsd, srandom has a different signature skipTest("cgo1") skipTest("cgo2") case "netbsd": // cgo1 and cgo2 don't run on netbsd, srandom has a different signature skipTest("cgo1") skipTest("cgo2") } }