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