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  }