Left: | ||
Right: |
LEFT | RIGHT |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 } |
LEFT | RIGHT |