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