Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(30)

Delta Between Two Patch Sets: src/pkg/testing/testing.go

Issue 5071044: code review 5071044: testing: Add support for running tests in parallel (t.P... (Closed)
Left Patch Set: diff -r 722a1d98acc0 https://go.googlecode.com/hg/ Created 13 years, 5 months ago
Right Patch Set: diff -r 3c7f031ee62b https://go.googlecode.com/hg/ Created 13 years, 5 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « src/cmd/gotest/flag.go ('k') | no next file » | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 // Copyright 2009 The Go Authors. All rights reserved. 1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style 2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file. 3 // license that can be found in the LICENSE file.
4 4
5 // Package testing provides support for automated testing of Go packages. 5 // Package testing provides support for automated testing of Go packages.
6 // It is intended to be used in concert with the ``gotest'' utility, which autom ates 6 // It is intended to be used in concert with the ``gotest'' utility, which autom ates
7 // execution of any function of the form 7 // execution of any function of the form
8 // func TestXxx(*testing.T) 8 // func TestXxx(*testing.T)
9 // where Xxx can be any alphanumeric string (but the first letter must not be in 9 // where Xxx can be any alphanumeric string (but the first letter must not be in
10 // [a-z]) and serves to identify the test routine. 10 // [a-z]) and serves to identify the test routine.
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
58 short = flag.Bool("test.short", false, "run smaller test suite to save t ime") 58 short = flag.Bool("test.short", false, "run smaller test suite to save t ime")
59 59
60 // Report as tests are run; default is silent for success. 60 // Report as tests are run; default is silent for success.
61 chatty = flag.Bool("test.v", false, "verbose: print additional o utput") 61 chatty = flag.Bool("test.v", false, "verbose: print additional o utput")
62 match = flag.String("test.run", "", "regular expression to sele ct tests to run") 62 match = flag.String("test.run", "", "regular expression to sele ct tests to run")
63 memProfile = flag.String("test.memprofile", "", "write a memory prof ile to the named file after execution") 63 memProfile = flag.String("test.memprofile", "", "write a memory prof ile to the named file after execution")
64 memProfileRate = flag.Int("test.memprofilerate", 0, "if >=0, sets runtim e.MemProfileRate") 64 memProfileRate = flag.Int("test.memprofilerate", 0, "if >=0, sets runtim e.MemProfileRate")
65 cpuProfile = flag.String("test.cpuprofile", "", "write a cpu profile to the named file during execution") 65 cpuProfile = flag.String("test.cpuprofile", "", "write a cpu profile to the named file during execution")
66 timeout = flag.Int64("test.timeout", 0, "if > 0, sets time limit for tests in seconds") 66 timeout = flag.Int64("test.timeout", 0, "if > 0, sets time limit for tests in seconds")
67 cpuListStr = flag.String("test.cpu", "", "comma-separated list of nu mber of CPUs to use for each test") 67 cpuListStr = flag.String("test.cpu", "", "comma-separated list of nu mber of CPUs to use for each test")
68 parallel = flag.Int("test.parallel", runtime.GOMAXPROCS(0), "maxim um test parallelism")
68 69
69 cpuList []int 70 cpuList []int
70 ) 71 )
71 72
72 // Short reports whether the -test.short flag is set. 73 // Short reports whether the -test.short flag is set.
73 func Short() bool { 74 func Short() bool {
74 return *short 75 return *short
75 } 76 }
76 77
77 // Insert final newline if needed and tabs after internal newlines. 78 // Insert final newline if needed and tabs after internal newlines.
78 func tabify(s string) string { 79 func tabify(s string) string {
79 n := len(s) 80 n := len(s)
80 if n > 0 && s[n-1] != '\n' { 81 if n > 0 && s[n-1] != '\n' {
81 s += "\n" 82 s += "\n"
82 n++ 83 n++
83 } 84 }
84 for i := 0; i < n-1; i++ { // -1 to avoid final newline 85 for i := 0; i < n-1; i++ { // -1 to avoid final newline
85 if s[i] == '\n' { 86 if s[i] == '\n' {
86 return s[0:i+1] + "\t" + tabify(s[i+1:n]) 87 return s[0:i+1] + "\t" + tabify(s[i+1:n])
87 } 88 }
88 } 89 }
89 return s 90 return s
90 } 91 }
91 92
92 // T is a type passed to Test functions to manage test state and support formatt ed test logs. 93 // T is a type passed to Test functions to manage test state and support formatt ed test logs.
93 // Logs are accumulated during execution and dumped to standard error when done. 94 // Logs are accumulated during execution and dumped to standard error when done.
94 type T struct { 95 type T struct {
95 » errors string 96 » name string // Name of test.
96 » failed bool 97 » errors string // Error string from test.
97 » ch chan *T // Output for serial tests 98 » failed bool // Test has failed.
98 » pch chan *T // Output for parallel tests 99 » ch chan *T // Output for serial tests.
rog 2011/09/21 20:22:53 d
99 » wch chan bool // Sync channel for parallel tests 100 » startParallel chan bool // Parallel tests will wait on this.
100 » parallel bool 101 » ns int64 // Duration of test in nanoseconds.
101 » name string
102 } 102 }
103 103
104 // Fail marks the Test function as having failed but continues execution. 104 // Fail marks the Test function as having failed but continues execution.
105 func (t *T) Fail() { t.failed = true } 105 func (t *T) Fail() { t.failed = true }
106 106
107 // Failed returns whether the Test function has failed. 107 // Failed returns whether the Test function has failed.
108 func (t *T) Failed() bool { return t.failed } 108 func (t *T) Failed() bool { return t.failed }
109 109
110 // FailNow marks the Test function as having failed and stops its execution. 110 // FailNow marks the Test function as having failed and stops its execution.
111 // Execution will continue at the next Test. 111 // Execution will continue at the next Test.
(...skipping 30 matching lines...) Expand all
142 t.Log(args...) 142 t.Log(args...)
143 t.FailNow() 143 t.FailNow()
144 } 144 }
145 145
146 // Fatalf is equivalent to Logf() followed by FailNow(). 146 // Fatalf is equivalent to Logf() followed by FailNow().
147 func (t *T) Fatalf(format string, args ...interface{}) { 147 func (t *T) Fatalf(format string, args ...interface{}) {
148 t.Logf(format, args...) 148 t.Logf(format, args...)
149 t.FailNow() 149 t.FailNow()
150 } 150 }
151 151
152 // Parallel signal that this test can be run in parallel to other parallel tests . 152 // Parallel signals that this test is to be run in parallel with (and only with) ·
153 // This will block until all serial tests in this cpu group are done. 153 // other parallel tests in this CPU group.
154 func (t *T) Parallel() { 154 func (t *T) Parallel() {
155 » t.parallel = true 155 » t.ch <- nil // Release main testing loop
rog 2011/09/21 20:22:53 this doesn't appear to be used. perhaps it should
156 » t.ch <- nil // Release main testing loop 156 » <-t.startParallel // Wait for serial tests to finish
157 » t.ch = t.pch
rog 2011/09/21 20:22:53 d
158
159 » // Wait for serial tests to finish
160 » <-t.wch
161 } 157 }
162 158
163 // An internal type but exported because it is cross-package; part of the implem entation 159 // An internal type but exported because it is cross-package; part of the implem entation
164 // of gotest. 160 // of gotest.
165 type InternalTest struct { 161 type InternalTest struct {
166 Name string 162 Name string
167 F func(*T) 163 F func(*T)
168 } 164 }
169 165
170 func tRunner(t *T, test *InternalTest, report chan string) { 166 func tRunner(t *T, test *InternalTest) {
171 » if *chatty { 167 » t.ns = time.Nanoseconds()
172 » » report <- fmt.Sprintf("=== RUN %s\n", t.name)
173 » }
174 » ns := time.Nanoseconds()
175 test.F(t) 168 test.F(t)
176 » ns = time.Nanoseconds() - ns 169 » t.ns = time.Nanoseconds() - t.ns
177
178 » tstr := fmt.Sprintf("(%.2f seconds)", float64(ns)/1e9)
179 » format := "--- %s: %s %s\n%s"
180 » if t.failed {
181 » » report <- fmt.Sprintf(format, "FAIL", t.name, tstr, t.errors)
182 » } else if *chatty {
183 » » report <- fmt.Sprintf(format, "PASS", t.name, tstr, t.errors)
184 » }
185 t.ch <- t 170 t.ch <- t
186 } 171 }
187 172
188 // An internal function but exported because it is cross-package; part of the im plementation 173 // An internal function but exported because it is cross-package; part of the im plementation
189 // of gotest. 174 // of gotest.
190 func Main(matchString func(pat, str string) (bool, os.Error), tests []InternalTe st, benchmarks []InternalBenchmark) { 175 func Main(matchString func(pat, str string) (bool, os.Error), tests []InternalTe st, benchmarks []InternalBenchmark) {
191 flag.Parse() 176 flag.Parse()
192 parseCpuList() 177 parseCpuList()
193 178
194 before() 179 before()
195 startAlarm() 180 startAlarm()
196 RunTests(matchString, tests) 181 RunTests(matchString, tests)
197 stopAlarm() 182 stopAlarm()
198 RunBenchmarks(matchString, benchmarks) 183 RunBenchmarks(matchString, benchmarks)
199 after() 184 after()
200 } 185 }
201 186
187 func report(t *T) {
188 tstr := fmt.Sprintf("(%.2f seconds)", float64(t.ns)/1e9)
189 format := "--- %s: %s %s\n%s"
190 if t.failed {
191 fmt.Fprintf(os.Stderr, format, "FAIL", t.name, tstr, t.errors)
192 } else if *chatty {
193 fmt.Fprintf(os.Stderr, format, "PASS", t.name, tstr, t.errors)
194 }
195 }
196
202 func RunTests(matchString func(pat, str string) (bool, os.Error), tests []Intern alTest) { 197 func RunTests(matchString func(pat, str string) (bool, os.Error), tests []Intern alTest) {
203 if len(tests) == 0 { 198 if len(tests) == 0 {
204 » » println("testing: warning: no tests to run") 199 » » fmt.Fprintln(os.Stderr, "testing: warning: no tests to run")
205 return 200 return
206 } 201 }
207 202
208 ok := true 203 ok := true
209 » pch := make(chan *T) 204 » ch := make(chan *T)
rog 2011/09/21 20:22:53 s/pch/ch/
210 205
211 » // One reporter, to avoid print collision
212 » report := make(chan string)
213 » go func() {
214 » » for msg := range report {
215 » » » print(msg)
216 » » }
217 » }()
218
219 PROCS:
220 for _, procs := range cpuList { 206 for _, procs := range cpuList {
221 runtime.GOMAXPROCS(procs) 207 runtime.GOMAXPROCS(procs)
222 if p := runtime.GOMAXPROCS(-1); p != procs {
223 fmt.Printf("error: can't restore GOMAXPROCS to %d (curre ntly %d)\n", procs, p)
224 ok = false
225 break PROCS
226 }
227 208
228 numParallel := 0 209 numParallel := 0
229 » » wch := make(chan bool) 210 » » startParallel := make(chan bool)
rog 2011/09/21 20:22:53 i'm not sure wch is a good name here. startParalle
211
230 for i := 0; i < len(tests); i++ { 212 for i := 0; i < len(tests); i++ {
231 matched, err := matchString(*match, tests[i].Name) 213 matched, err := matchString(*match, tests[i].Name)
232 if err != nil { 214 if err != nil {
233 println("invalid regexp for -test.run:", err.Str ing()) 215 println("invalid regexp for -test.run:", err.Str ing())
234 os.Exit(1) 216 os.Exit(1)
235 } 217 }
236 if !matched { 218 if !matched {
237 continue 219 continue
238 } 220 }
239 testName := tests[i].Name 221 testName := tests[i].Name
240 if procs != 1 { 222 if procs != 1 {
241 testName = fmt.Sprintf("%s-%d", tests[i].Name, p rocs) 223 testName = fmt.Sprintf("%s-%d", tests[i].Name, p rocs)
242 } 224 }
243 » » » t := &T{ch: make(chan *T), pch: pch, name: testName, wch : wch} 225 » » » t := &T{ch: ch, name: testName, startParallel: startPara llel}
rog 2011/09/21 20:22:53 t := &T{ch: ch, name: testName, wch: wch}
244 » » » go tRunner(t, &tests[i], report) 226 » » » if *chatty {
227 » » » » println("=== RUN", t.name)
228 » » » }
229 » » » go tRunner(t, &tests[i])
245 out := <-t.ch 230 out := <-t.ch
246 if out == nil { // Parallel run. 231 if out == nil { // Parallel run.
247 numParallel++ 232 numParallel++
248 continue 233 continue
249 } 234 }
235 report(t)
250 ok = ok && !out.failed 236 ok = ok && !out.failed
251 } 237 }
252 238
253 » » close(wch) // Release parallel tests 239 » » running := 0
254 » » for i := 0; i < numParallel; i++ { 240 » » for numParallel+running > 0 {
255 » » » out := <-pch 241 » » » if running < *parallel && numParallel > 0 {
rog 2011/09/21 20:22:53 s/pch/ch/
256 » » » ok = ok && !out.failed 242 » » » » startParallel <- true
243 » » » » running++
244 » » » » numParallel--
245 » » » » continue
246 » » » }
247 » » » t := <-ch
248 » » » report(t)
249 » » » ok = ok && !t.failed
250 » » » running--
257 } 251 }
258 } 252 }
259 253
260 if !ok { 254 if !ok {
261 println("FAIL") 255 println("FAIL")
262 os.Exit(1) 256 os.Exit(1)
263 } 257 }
264 println("PASS") 258 println("PASS")
265 } 259 }
266 260
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
331 for _, val := range strings.Split(*cpuListStr, ",") { 325 for _, val := range strings.Split(*cpuListStr, ",") {
332 cpu, err := strconv.Atoi(val) 326 cpu, err := strconv.Atoi(val)
333 if err != nil || cpu <= 0 { 327 if err != nil || cpu <= 0 {
334 println("invalid value for -test.cpu") 328 println("invalid value for -test.cpu")
335 os.Exit(1) 329 os.Exit(1)
336 } 330 }
337 cpuList = append(cpuList, cpu) 331 cpuList = append(cpuList, cpu)
338 } 332 }
339 } 333 }
340 } 334 }
LEFTRIGHT

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b