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  }