Source file src/testing/loop_test.go

     1  // Copyright 2024 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  	"strings"
    10  )
    11  
    12  // See also TestBenchmarkBLoop* in other files.
    13  
    14  func TestBenchmarkBLoop(t *T) {
    15  	var initialStart highPrecisionTime
    16  	var firstStart highPrecisionTime
    17  	var scaledStart highPrecisionTime
    18  	var runningEnd bool
    19  	runs := 0
    20  	iters := 0
    21  	firstBN := 0
    22  	restBN := 0
    23  	finalBN := 0
    24  	bRet := Benchmark(func(b *B) {
    25  		initialStart = b.start
    26  		runs++
    27  		for b.Loop() {
    28  			if iters == 0 {
    29  				firstStart = b.start
    30  				firstBN = b.N
    31  			} else {
    32  				restBN = max(restBN, b.N)
    33  			}
    34  			if iters == 1 {
    35  				scaledStart = b.start
    36  			}
    37  			iters++
    38  		}
    39  		finalBN = b.N
    40  		runningEnd = b.timerOn
    41  	})
    42  	// Verify that a b.Loop benchmark is invoked just once.
    43  	if runs != 1 {
    44  		t.Errorf("want runs == 1, got %d", runs)
    45  	}
    46  	// Verify that at least one iteration ran.
    47  	if iters == 0 {
    48  		t.Fatalf("no iterations ran")
    49  	}
    50  	// Verify that b.N, bRet.N, and the b.Loop() iteration count match.
    51  	if finalBN != iters || bRet.N != iters {
    52  		t.Errorf("benchmark iterations mismatch: %d loop iterations, final b.N=%d, bRet.N=%d", iters, finalBN, bRet.N)
    53  	}
    54  	// Verify that b.N was 0 inside the loop
    55  	if firstBN != 0 {
    56  		t.Errorf("want b.N == 0 on first iteration, got %d", firstBN)
    57  	}
    58  	if restBN != 0 {
    59  		t.Errorf("want b.N == 0 on subsequent iterations, got %d", restBN)
    60  	}
    61  	// Make sure the benchmark ran for an appropriate amount of time.
    62  	if bRet.T < benchTime.d {
    63  		t.Fatalf("benchmark ran for %s, want >= %s", bRet.T, benchTime.d)
    64  	}
    65  	// Verify that the timer is reset on the first loop, and then left alone.
    66  	if firstStart == initialStart {
    67  		t.Errorf("b.Loop did not reset the timer")
    68  	}
    69  	if scaledStart != firstStart {
    70  		t.Errorf("b.Loop stops and restarts the timer during iteration")
    71  	}
    72  	// Verify that it stopped the timer after the last loop.
    73  	if runningEnd {
    74  		t.Errorf("timer was still running after last iteration")
    75  	}
    76  }
    77  
    78  func TestBenchmarkBLoopBreak(t *T) {
    79  	var bState *B
    80  	var bLog bytes.Buffer
    81  	bRet := Benchmark(func(b *B) {
    82  		// The Benchmark function provides no access to the failure state and
    83  		// discards the log, so capture the B and save its log.
    84  		bState = b
    85  		b.common.w = &bLog
    86  
    87  		for i := 0; b.Loop(); i++ {
    88  			if i == 2 {
    89  				break
    90  			}
    91  		}
    92  	})
    93  	if !bState.failed {
    94  		t.Errorf("benchmark should have failed")
    95  	}
    96  	const wantLog = "benchmark function returned without B.Loop"
    97  	if log := bLog.String(); !strings.Contains(log, wantLog) {
    98  		t.Errorf("missing error %q in output:\n%s", wantLog, log)
    99  	}
   100  	// A benchmark that exits early should not report its target iteration count
   101  	// because it's not meaningful.
   102  	if bRet.N != 0 {
   103  		t.Errorf("want N == 0, got %d", bRet.N)
   104  	}
   105  }
   106  
   107  func TestBenchmarkBLoopError(t *T) {
   108  	// Test that a benchmark that exits early because of an error doesn't *also*
   109  	// complain that the benchmark exited early.
   110  	var bState *B
   111  	var bLog bytes.Buffer
   112  	bRet := Benchmark(func(b *B) {
   113  		bState = b
   114  		b.common.w = &bLog
   115  
   116  		for i := 0; b.Loop(); i++ {
   117  			b.Error("error")
   118  			return
   119  		}
   120  	})
   121  	if !bState.failed {
   122  		t.Errorf("benchmark should have failed")
   123  	}
   124  	const noWantLog = "benchmark function returned without B.Loop"
   125  	if log := bLog.String(); strings.Contains(log, noWantLog) {
   126  		t.Errorf("unexpected error %q in output:\n%s", noWantLog, log)
   127  	}
   128  	if bRet.N != 0 {
   129  		t.Errorf("want N == 0, got %d", bRet.N)
   130  	}
   131  }
   132  
   133  func TestBenchmarkBLoopStop(t *T) {
   134  	var bState *B
   135  	var bLog bytes.Buffer
   136  	bRet := Benchmark(func(b *B) {
   137  		bState = b
   138  		b.common.w = &bLog
   139  
   140  		for i := 0; b.Loop(); i++ {
   141  			b.StopTimer()
   142  		}
   143  	})
   144  	if !bState.failed {
   145  		t.Errorf("benchmark should have failed")
   146  	}
   147  	const wantLog = "B.Loop called with timer stopped"
   148  	if log := bLog.String(); !strings.Contains(log, wantLog) {
   149  		t.Errorf("missing error %q in output:\n%s", wantLog, log)
   150  	}
   151  	if bRet.N != 0 {
   152  		t.Errorf("want N == 0, got %d", bRet.N)
   153  	}
   154  }
   155  

View as plain text