| // 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" |
| ) |
| |
| 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() { |
| 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) |
| 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) |
| 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.Replace(out, []byte{'\n'}, []byte{' '}, -1) |
| |
| // 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") |
| } |
| } |