github.com/mh-cbon/go@v0.0.0-20160603070303-9e112a3fe4c0/src/testing/sub_test.go (about) 1 // Copyright 2016 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 package testing 6 7 import ( 8 "bytes" 9 "regexp" 10 "strings" 11 "sync/atomic" 12 "time" 13 ) 14 15 func TestTestContext(t *T) { 16 const ( 17 add1 = 0 18 done = 1 19 ) 20 // After each of the calls are applied to the context, the 21 type call struct { 22 typ int // run or done 23 // result from applying the call 24 running int 25 waiting int 26 started bool 27 } 28 testCases := []struct { 29 max int 30 run []call 31 }{{ 32 max: 1, 33 run: []call{ 34 {typ: add1, running: 1, waiting: 0, started: true}, 35 {typ: done, running: 0, waiting: 0, started: false}, 36 }, 37 }, { 38 max: 1, 39 run: []call{ 40 {typ: add1, running: 1, waiting: 0, started: true}, 41 {typ: add1, running: 1, waiting: 1, started: false}, 42 {typ: done, running: 1, waiting: 0, started: true}, 43 {typ: done, running: 0, waiting: 0, started: false}, 44 {typ: add1, running: 1, waiting: 0, started: true}, 45 }, 46 }, { 47 max: 3, 48 run: []call{ 49 {typ: add1, running: 1, waiting: 0, started: true}, 50 {typ: add1, running: 2, waiting: 0, started: true}, 51 {typ: add1, running: 3, waiting: 0, started: true}, 52 {typ: add1, running: 3, waiting: 1, started: false}, 53 {typ: add1, running: 3, waiting: 2, started: false}, 54 {typ: add1, running: 3, waiting: 3, started: false}, 55 {typ: done, running: 3, waiting: 2, started: true}, 56 {typ: add1, running: 3, waiting: 3, started: false}, 57 {typ: done, running: 3, waiting: 2, started: true}, 58 {typ: done, running: 3, waiting: 1, started: true}, 59 {typ: done, running: 3, waiting: 0, started: true}, 60 {typ: done, running: 2, waiting: 0, started: false}, 61 {typ: done, running: 1, waiting: 0, started: false}, 62 {typ: done, running: 0, waiting: 0, started: false}, 63 }, 64 }} 65 for i, tc := range testCases { 66 ctx := &testContext{ 67 startParallel: make(chan bool), 68 maxParallel: tc.max, 69 } 70 for j, call := range tc.run { 71 doCall := func(f func()) chan bool { 72 done := make(chan bool) 73 go func() { 74 f() 75 done <- true 76 }() 77 return done 78 } 79 started := false 80 switch call.typ { 81 case add1: 82 signal := doCall(ctx.waitParallel) 83 select { 84 case <-signal: 85 started = true 86 case ctx.startParallel <- true: 87 <-signal 88 } 89 case done: 90 signal := doCall(ctx.release) 91 select { 92 case <-signal: 93 case <-ctx.startParallel: 94 started = true 95 <-signal 96 } 97 } 98 if started != call.started { 99 t.Errorf("%d:%d:started: got %v; want %v", i, j, started, call.started) 100 } 101 if ctx.running != call.running { 102 t.Errorf("%d:%d:running: got %v; want %v", i, j, ctx.running, call.running) 103 } 104 if ctx.numWaiting != call.waiting { 105 t.Errorf("%d:%d:waiting: got %v; want %v", i, j, ctx.numWaiting, call.waiting) 106 } 107 } 108 } 109 } 110 111 func TestTRun(t *T) { 112 realTest := t 113 testCases := []struct { 114 desc string 115 ok bool 116 maxPar int 117 chatty bool 118 output string 119 f func(*T) 120 }{{ 121 desc: "failnow skips future sequential and parallel tests at same level", 122 ok: false, 123 maxPar: 1, 124 output: ` 125 --- FAIL: failnow skips future sequential and parallel tests at same level (N.NNs) 126 --- FAIL: failnow skips future sequential and parallel tests at same level/#00 (N.NNs) 127 `, 128 f: func(t *T) { 129 ranSeq := false 130 ranPar := false 131 t.Run("", func(t *T) { 132 t.Run("par", func(t *T) { 133 t.Parallel() 134 ranPar = true 135 }) 136 t.Run("seq", func(t *T) { 137 ranSeq = true 138 }) 139 t.FailNow() 140 t.Run("seq", func(t *T) { 141 realTest.Error("test must be skipped") 142 }) 143 t.Run("par", func(t *T) { 144 t.Parallel() 145 realTest.Error("test must be skipped.") 146 }) 147 }) 148 if !ranPar { 149 realTest.Error("parallel test was not run") 150 } 151 if !ranSeq { 152 realTest.Error("sequential test was not run") 153 } 154 }, 155 }, { 156 desc: "failure in parallel test propagates upwards", 157 ok: false, 158 maxPar: 1, 159 output: ` 160 --- FAIL: failure in parallel test propagates upwards (N.NNs) 161 --- FAIL: failure in parallel test propagates upwards/#00 (N.NNs) 162 --- FAIL: failure in parallel test propagates upwards/#00/par (N.NNs) 163 `, 164 f: func(t *T) { 165 t.Run("", func(t *T) { 166 t.Parallel() 167 t.Run("par", func(t *T) { 168 t.Parallel() 169 t.Fail() 170 }) 171 }) 172 }, 173 }, { 174 desc: "skipping without message, chatty", 175 ok: true, 176 chatty: true, 177 output: ` 178 === RUN skipping without message, chatty 179 --- SKIP: skipping without message, chatty (N.NNs)`, 180 f: func(t *T) { t.SkipNow() }, 181 }, { 182 desc: "chatty with recursion", 183 ok: true, 184 chatty: true, 185 output: ` 186 === RUN chatty with recursion 187 === RUN chatty with recursion/#00 188 === RUN chatty with recursion/#00/#00 189 --- PASS: chatty with recursion (N.NNs) 190 --- PASS: chatty with recursion/#00 (N.NNs) 191 --- PASS: chatty with recursion/#00/#00 (N.NNs)`, 192 f: func(t *T) { 193 t.Run("", func(t *T) { 194 t.Run("", func(t *T) {}) 195 }) 196 }, 197 }, { 198 desc: "skipping without message, not chatty", 199 ok: true, 200 f: func(t *T) { t.SkipNow() }, 201 }, { 202 desc: "skipping after error", 203 output: ` 204 --- FAIL: skipping after error (N.NNs) 205 sub_test.go:NNN: an error 206 sub_test.go:NNN: skipped`, 207 f: func(t *T) { 208 t.Error("an error") 209 t.Skip("skipped") 210 }, 211 }, { 212 desc: "use Run to locally synchronize parallelism", 213 ok: true, 214 maxPar: 1, 215 f: func(t *T) { 216 var count uint32 217 t.Run("waitGroup", func(t *T) { 218 for i := 0; i < 4; i++ { 219 t.Run("par", func(t *T) { 220 t.Parallel() 221 atomic.AddUint32(&count, 1) 222 }) 223 } 224 }) 225 if count != 4 { 226 t.Errorf("count was %d; want 4", count) 227 } 228 }, 229 }, { 230 desc: "alternate sequential and parallel", 231 // Sequential tests should partake in the counting of running threads. 232 // Otherwise, if one runs parallel subtests in sequential tests that are 233 // itself subtests of parallel tests, the counts can get askew. 234 ok: true, 235 maxPar: 1, 236 f: func(t *T) { 237 t.Run("a", func(t *T) { 238 t.Parallel() 239 t.Run("b", func(t *T) { 240 // Sequential: ensure running count is decremented. 241 t.Run("c", func(t *T) { 242 t.Parallel() 243 }) 244 245 }) 246 }) 247 }, 248 }, { 249 desc: "alternate sequential and parallel 2", 250 // Sequential tests should partake in the counting of running threads. 251 // Otherwise, if one runs parallel subtests in sequential tests that are 252 // itself subtests of parallel tests, the counts can get askew. 253 ok: true, 254 maxPar: 2, 255 f: func(t *T) { 256 for i := 0; i < 2; i++ { 257 t.Run("a", func(t *T) { 258 t.Parallel() 259 time.Sleep(time.Nanosecond) 260 for i := 0; i < 2; i++ { 261 t.Run("b", func(t *T) { 262 time.Sleep(time.Nanosecond) 263 for i := 0; i < 2; i++ { 264 t.Run("c", func(t *T) { 265 t.Parallel() 266 time.Sleep(time.Nanosecond) 267 }) 268 } 269 270 }) 271 } 272 }) 273 } 274 }, 275 }, { 276 desc: "stress test", 277 ok: true, 278 maxPar: 4, 279 f: func(t *T) { 280 t.Parallel() 281 for i := 0; i < 12; i++ { 282 t.Run("a", func(t *T) { 283 t.Parallel() 284 time.Sleep(time.Nanosecond) 285 for i := 0; i < 12; i++ { 286 t.Run("b", func(t *T) { 287 time.Sleep(time.Nanosecond) 288 for i := 0; i < 12; i++ { 289 t.Run("c", func(t *T) { 290 t.Parallel() 291 time.Sleep(time.Nanosecond) 292 t.Run("d1", func(t *T) {}) 293 t.Run("d2", func(t *T) {}) 294 t.Run("d3", func(t *T) {}) 295 t.Run("d4", func(t *T) {}) 296 }) 297 } 298 }) 299 } 300 }) 301 } 302 }, 303 }, { 304 desc: "skip output", 305 ok: true, 306 maxPar: 4, 307 f: func(t *T) { 308 t.Skip() 309 }, 310 }, { 311 desc: "panic on goroutine fail after test exit", 312 ok: false, 313 maxPar: 4, 314 f: func(t *T) { 315 ch := make(chan bool) 316 t.Run("", func(t *T) { 317 go func() { 318 <-ch 319 defer func() { 320 if r := recover(); r == nil { 321 realTest.Errorf("expected panic") 322 } 323 ch <- true 324 }() 325 t.Errorf("failed after success") 326 }() 327 }) 328 ch <- true 329 <-ch 330 }, 331 }} 332 for _, tc := range testCases { 333 ctx := newTestContext(tc.maxPar, newMatcher(regexp.MatchString, "", "")) 334 buf := &bytes.Buffer{} 335 root := &T{ 336 common: common{ 337 signal: make(chan bool), 338 name: "Test", 339 w: buf, 340 chatty: tc.chatty, 341 }, 342 context: ctx, 343 } 344 ok := root.Run(tc.desc, tc.f) 345 ctx.release() 346 347 if ok != tc.ok { 348 t.Errorf("%s:ok: got %v; want %v", tc.desc, ok, tc.ok) 349 } 350 if ok != !root.Failed() { 351 t.Errorf("%s:root failed: got %v; want %v", tc.desc, !ok, root.Failed()) 352 } 353 if ctx.running != 0 || ctx.numWaiting != 0 { 354 t.Errorf("%s:running and waiting non-zero: got %d and %d", tc.desc, ctx.running, ctx.numWaiting) 355 } 356 got := strings.TrimSpace(buf.String()) 357 want := strings.TrimSpace(tc.output) 358 re := makeRegexp(want) 359 if ok, err := regexp.MatchString(re, got); !ok || err != nil { 360 t.Errorf("%s:ouput:\ngot:\n%s\nwant:\n%s", tc.desc, got, want) 361 } 362 } 363 } 364 365 func TestBRun(t *T) { 366 work := func(b *B) { 367 for i := 0; i < b.N; i++ { 368 time.Sleep(time.Nanosecond) 369 } 370 } 371 testCases := []struct { 372 desc string 373 failed bool 374 chatty bool 375 output string 376 f func(*B) 377 }{{ 378 desc: "simulate sequential run of subbenchmarks.", 379 f: func(b *B) { 380 b.Run("", func(b *B) { work(b) }) 381 time1 := b.result.NsPerOp() 382 b.Run("", func(b *B) { work(b) }) 383 time2 := b.result.NsPerOp() 384 if time1 >= time2 { 385 t.Errorf("no time spent in benchmark t1 >= t2 (%d >= %d)", time1, time2) 386 } 387 }, 388 }, { 389 desc: "bytes set by all benchmarks", 390 f: func(b *B) { 391 b.Run("", func(b *B) { b.SetBytes(10); work(b) }) 392 b.Run("", func(b *B) { b.SetBytes(10); work(b) }) 393 if b.result.Bytes != 20 { 394 t.Errorf("bytes: got: %d; want 20", b.result.Bytes) 395 } 396 }, 397 }, { 398 desc: "bytes set by some benchmarks", 399 // In this case the bytes result is meaningless, so it must be 0. 400 f: func(b *B) { 401 b.Run("", func(b *B) { b.SetBytes(10); work(b) }) 402 b.Run("", func(b *B) { work(b) }) 403 b.Run("", func(b *B) { b.SetBytes(10); work(b) }) 404 if b.result.Bytes != 0 { 405 t.Errorf("bytes: got: %d; want 0", b.result.Bytes) 406 } 407 }, 408 }, { 409 desc: "failure carried over to root", 410 failed: true, 411 output: "--- FAIL: root", 412 f: func(b *B) { b.Fail() }, 413 }, { 414 desc: "skipping without message, chatty", 415 chatty: true, 416 output: "--- SKIP: root", 417 f: func(b *B) { b.SkipNow() }, 418 }, { 419 desc: "skipping with message, chatty", 420 chatty: true, 421 output: ` 422 --- SKIP: root 423 sub_test.go:NNN: skipping`, 424 f: func(b *B) { b.Skip("skipping") }, 425 }, { 426 desc: "chatty with recursion", 427 chatty: true, 428 f: func(b *B) { 429 b.Run("", func(b *B) { 430 b.Run("", func(b *B) {}) 431 }) 432 }, 433 }, { 434 desc: "skipping without message, not chatty", 435 f: func(b *B) { b.SkipNow() }, 436 }, { 437 desc: "skipping after error", 438 failed: true, 439 output: ` 440 --- FAIL: root 441 sub_test.go:NNN: an error 442 sub_test.go:NNN: skipped`, 443 f: func(b *B) { 444 b.Error("an error") 445 b.Skip("skipped") 446 }, 447 }, { 448 desc: "memory allocation", 449 f: func(b *B) { 450 const bufSize = 256 451 alloc := func(b *B) { 452 var buf [bufSize]byte 453 for i := 0; i < b.N; i++ { 454 _ = append([]byte(nil), buf[:]...) 455 } 456 } 457 b.Run("", func(b *B) { alloc(b) }) 458 b.Run("", func(b *B) { alloc(b) }) 459 // runtime.MemStats sometimes reports more allocations than the 460 // benchmark is responsible for. Luckily the point of this test is 461 // to ensure that the results are not underreported, so we can 462 // simply verify the lower bound. 463 if got := b.result.MemAllocs; got < 2 { 464 t.Errorf("MemAllocs was %v; want 2", got) 465 } 466 if got := b.result.MemBytes; got < 2*bufSize { 467 t.Errorf("MemBytes was %v; want %v", got, 2*bufSize) 468 } 469 }, 470 }} 471 for _, tc := range testCases { 472 var ok bool 473 buf := &bytes.Buffer{} 474 // This is almost like the Benchmark function, except that we override 475 // the benchtime and catch the failure result of the subbenchmark. 476 root := &B{ 477 common: common{ 478 signal: make(chan bool), 479 name: "root", 480 w: buf, 481 chatty: tc.chatty, 482 }, 483 benchFunc: func(b *B) { ok = b.Run("test", tc.f) }, // Use Run to catch failure. 484 benchTime: time.Microsecond, 485 } 486 root.runN(1) 487 if ok != !tc.failed { 488 t.Errorf("%s:ok: got %v; want %v", tc.desc, ok, !tc.failed) 489 } 490 if !ok != root.Failed() { 491 t.Errorf("%s:root failed: got %v; want %v", tc.desc, !ok, root.Failed()) 492 } 493 // All tests are run as subtests 494 if root.result.N != 1 { 495 t.Errorf("%s: N for parent benchmark was %d; want 1", tc.desc, root.result.N) 496 } 497 got := strings.TrimSpace(buf.String()) 498 want := strings.TrimSpace(tc.output) 499 re := makeRegexp(want) 500 if ok, err := regexp.MatchString(re, got); !ok || err != nil { 501 t.Errorf("%s:ouput:\ngot:\n%s\nwant:\n%s", tc.desc, got, want) 502 } 503 } 504 } 505 506 func makeRegexp(s string) string { 507 s = strings.Replace(s, ":NNN:", `:\d\d\d:`, -1) 508 s = strings.Replace(s, "(N.NNs)", `\(\d*\.\d*s\)`, -1) 509 return s 510 } 511 512 func TestBenchmarkOutput(t *T) { 513 // Ensure Benchmark initialized common.w by invoking it with an error and 514 // normal case. 515 Benchmark(func(b *B) { b.Error("do not print this output") }) 516 Benchmark(func(b *B) {}) 517 }