github.com/huandu/go@v0.0.0-20151114150818-04e615e41150/doc/progs/run.go (about) 1 // Copyright 2015 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // run runs the docs tests found in this directory. 6 package main 7 8 import ( 9 "bytes" 10 "flag" 11 "fmt" 12 "io/ioutil" 13 "os" 14 "os/exec" 15 "path/filepath" 16 "regexp" 17 "runtime" 18 "strings" 19 ) 20 21 const usage = `go run run.go [tests] 22 23 run.go runs the docs tests in this directory. 24 If no tests are provided, it runs all tests. 25 Tests may be specified without their .go suffix. 26 ` 27 28 func main() { 29 flag.Usage = func() { 30 fmt.Fprintf(os.Stderr, usage) 31 flag.PrintDefaults() 32 os.Exit(2) 33 } 34 35 flag.Parse() 36 if flag.NArg() == 0 { 37 // run all tests 38 fixcgo() 39 } else { 40 // run specified tests 41 onlyTest(flag.Args()...) 42 } 43 44 tmpdir, err := ioutil.TempDir("", "go-progs") 45 if err != nil { 46 fmt.Fprintln(os.Stderr, err) 47 os.Exit(1) 48 } 49 50 // ratec limits the number of tests running concurrently. 51 // None of the tests are intensive, so don't bother 52 // trying to manually adjust for slow builders. 53 ratec := make(chan bool, runtime.NumCPU()) 54 errc := make(chan error, len(tests)) 55 56 for _, tt := range tests { 57 tt := tt 58 ratec <- true 59 go func() { 60 errc <- test(tmpdir, tt.file, tt.want) 61 <-ratec 62 }() 63 } 64 65 var rc int 66 for range tests { 67 if err := <-errc; err != nil { 68 fmt.Fprintln(os.Stderr, err) 69 rc = 1 70 } 71 } 72 os.Remove(tmpdir) 73 os.Exit(rc) 74 } 75 76 // test builds the test in the given file. 77 // If want is non-empty, test also runs the test 78 // and checks that the output matches the regexp want. 79 func test(tmpdir, file, want string) error { 80 // Build the program. 81 prog := filepath.Join(tmpdir, file) 82 cmd := exec.Command("go", "build", "-o", prog, file+".go") 83 out, err := cmd.CombinedOutput() 84 if err != nil { 85 return fmt.Errorf("go build %s.go failed: %v\nOutput:\n%s", file, err, out) 86 } 87 defer os.Remove(prog) 88 89 // Only run the test if we have output to check. 90 if want == "" { 91 return nil 92 } 93 94 cmd = exec.Command(prog) 95 out, err = cmd.CombinedOutput() 96 if err != nil { 97 return fmt.Errorf("%s failed: %v\nOutput:\n%s", file, err, out) 98 } 99 100 // Canonicalize output. 101 out = bytes.TrimRight(out, "\n") 102 out = bytes.Replace(out, []byte{'\n'}, []byte{' '}, -1) 103 104 // Check the result. 105 match, err := regexp.Match(want, out) 106 if err != nil { 107 return fmt.Errorf("failed to parse regexp %q: %v", want, err) 108 } 109 if !match { 110 return fmt.Errorf("%s.go:\n%q\ndoes not match %s", file, out, want) 111 } 112 113 return nil 114 } 115 116 type testcase struct { 117 file string 118 want string 119 } 120 121 var tests = []testcase{ 122 // defer_panic_recover 123 {"defer", `^0 3210 2$`}, 124 {"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.$`}, 125 126 // effective_go 127 {"eff_bytesize", `^1.00YB 9.09TB$`}, 128 {"eff_qr", ""}, 129 {"eff_sequence", `^\[-1 2 6 16 44\]$`}, 130 {"eff_unused2", ""}, 131 132 // error_handling 133 {"error", ""}, 134 {"error2", ""}, 135 {"error3", ""}, 136 {"error4", ""}, 137 138 // law_of_reflection 139 {"interface", ""}, 140 {"interface2", `^type: float64$`}, 141 142 // c_go_cgo 143 {"cgo1", ""}, 144 {"cgo2", ""}, 145 {"cgo3", ""}, 146 {"cgo4", ""}, 147 148 // timeout 149 {"timeout1", ""}, 150 {"timeout2", ""}, 151 152 // gobs 153 {"gobs1", ""}, 154 {"gobs2", ""}, 155 156 // json 157 {"json1", `^$`}, 158 {"json2", `the reciprocal of i is`}, 159 {"json3", `Age is int 6`}, 160 {"json4", `^$`}, 161 {"json5", ""}, 162 163 // image_package 164 {"image_package1", `^X is 2 Y is 1$`}, 165 {"image_package2", `^3 4 false$`}, 166 {"image_package3", `^3 4 true$`}, 167 {"image_package4", `^image.Point{X:2, Y:1}$`}, 168 {"image_package5", `^{255 0 0 255}$`}, 169 {"image_package6", `^8 4 true$`}, 170 171 // other 172 {"go1", `^Christmas is a holiday: true .*go1.go already exists$`}, 173 {"slices", ""}, 174 } 175 176 func onlyTest(files ...string) { 177 var new []testcase 178 NextFile: 179 for _, file := range files { 180 file = strings.TrimSuffix(file, ".go") 181 for _, tt := range tests { 182 if tt.file == file { 183 new = append(new, tt) 184 continue NextFile 185 } 186 } 187 fmt.Fprintf(os.Stderr, "test %s.go not found\n", file) 188 os.Exit(1) 189 } 190 tests = new 191 } 192 193 func skipTest(file string) { 194 for i, tt := range tests { 195 if tt.file == file { 196 copy(tests[i:], tests[i+1:]) 197 tests = tests[:len(tests)-1] 198 return 199 } 200 } 201 panic("delete(" + file + "): not found") 202 } 203 204 func fixcgo() { 205 if os.Getenv("CGO_ENABLED") != "1" { 206 skipTest("cgo1") 207 skipTest("cgo2") 208 skipTest("cgo3") 209 skipTest("cgo4") 210 return 211 } 212 213 switch runtime.GOOS { 214 case "freebsd": 215 // cgo1 and cgo2 don't run on freebsd, srandom has a different signature 216 skipTest("cgo1") 217 skipTest("cgo2") 218 case "netbsd": 219 // cgo1 and cgo2 don't run on netbsd, srandom has a different signature 220 skipTest("cgo1") 221 skipTest("cgo2") 222 // cgo3 and cgo4 don't run on netbsd, since cgo cannot handle stdout correctly, see issue #10715. 223 skipTest("cgo3") 224 skipTest("cgo4") 225 case "openbsd", "solaris": 226 // cgo3 and cgo4 don't run on openbsd and solaris, since cgo cannot handle stdout correctly, see issue #10715. 227 skipTest("cgo3") 228 skipTest("cgo4") 229 } 230 }