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