github.com/ydb-platform/ydb-go-sdk/v3@v3.57.0/internal/xtest/manytimes.go (about)

     1  package xtest
     2  
     3  import (
     4  	"sync"
     5  	"testing"
     6  	"time"
     7  )
     8  
     9  type testManyTimesOptions struct {
    10  	stopAfter time.Duration
    11  }
    12  
    13  type TestManyTimesOption func(opts *testManyTimesOptions)
    14  
    15  func StopAfter(stopAfter time.Duration) TestManyTimesOption {
    16  	return func(opts *testManyTimesOptions) {
    17  		opts.stopAfter = stopAfter
    18  	}
    19  }
    20  
    21  func TestManyTimes(t testing.TB, test TestFunc, opts ...TestManyTimesOption) {
    22  	t.Helper()
    23  
    24  	testCounter := 0
    25  	defer func() {
    26  		t.Helper()
    27  		t.Log("test run counter:", testCounter)
    28  	}()
    29  
    30  	options := testManyTimesOptions{
    31  		stopAfter: time.Second,
    32  	}
    33  
    34  	for _, o := range opts {
    35  		if o != nil {
    36  			o(&options)
    37  		}
    38  	}
    39  
    40  	start := time.Now()
    41  	for {
    42  		testCounter++
    43  		// run test, then check stopAfter for guarantee run test least once
    44  		runTest(t, test)
    45  
    46  		if time.Since(start) > options.stopAfter || t.Failed() {
    47  			return
    48  		}
    49  	}
    50  }
    51  
    52  func TestManyTimesWithName(t *testing.T, name string, test TestFunc) {
    53  	t.Helper()
    54  
    55  	t.Run(name, func(t *testing.T) {
    56  		t.Helper()
    57  		TestManyTimes(t, test)
    58  	})
    59  }
    60  
    61  type TestFunc func(t testing.TB)
    62  
    63  func runTest(t testing.TB, test TestFunc) {
    64  	t.Helper()
    65  
    66  	tw := &testWrapper{
    67  		TB: t,
    68  	}
    69  
    70  	defer tw.doCleanup()
    71  
    72  	test(tw)
    73  }
    74  
    75  type testWrapper struct {
    76  	testing.TB
    77  
    78  	m       sync.Mutex
    79  	logs    []logRecord
    80  	cleanup []func()
    81  }
    82  
    83  func (tw *testWrapper) Cleanup(f func()) {
    84  	tw.Helper()
    85  
    86  	tw.m.Lock()
    87  	defer tw.m.Unlock()
    88  
    89  	tw.cleanup = append(tw.cleanup, f)
    90  }
    91  
    92  func (tw *testWrapper) Error(args ...interface{}) {
    93  	tw.TB.Helper()
    94  
    95  	tw.flushLogs()
    96  	tw.TB.Error(args...)
    97  }
    98  
    99  func (tw *testWrapper) Errorf(format string, args ...interface{}) {
   100  	tw.TB.Helper()
   101  
   102  	tw.flushLogs()
   103  	tw.TB.Errorf(format, args...)
   104  }
   105  
   106  func (tw *testWrapper) Fatal(args ...interface{}) {
   107  	tw.TB.Helper()
   108  
   109  	tw.flushLogs()
   110  	tw.TB.Fatal(args...)
   111  }
   112  
   113  func (tw *testWrapper) Fatalf(format string, args ...interface{}) {
   114  	tw.TB.Helper()
   115  
   116  	tw.flushLogs()
   117  	tw.TB.Fatalf(format, args...)
   118  }
   119  
   120  func (tw *testWrapper) Fail() {
   121  	tw.TB.Helper()
   122  
   123  	tw.flushLogs()
   124  	tw.TB.Fail()
   125  }
   126  
   127  func (tw *testWrapper) FailNow() {
   128  	tw.TB.Helper()
   129  
   130  	tw.flushLogs()
   131  	tw.TB.FailNow()
   132  }
   133  
   134  func (tw *testWrapper) Log(args ...interface{}) {
   135  	tw.TB.Helper()
   136  
   137  	tw.m.Lock()
   138  	defer tw.m.Unlock()
   139  
   140  	if tw.TB.Failed() {
   141  		tw.TB.Log(args...)
   142  	} else {
   143  		tw.logs = append(tw.logs, logRecord{
   144  			format: "",
   145  			args:   args,
   146  		})
   147  	}
   148  }
   149  
   150  func (tw *testWrapper) Logf(format string, args ...interface{}) {
   151  	tw.TB.Helper()
   152  
   153  	tw.m.Lock()
   154  	defer tw.m.Unlock()
   155  
   156  	if tw.TB.Failed() {
   157  		tw.TB.Logf(format, args...)
   158  	} else {
   159  		tw.logs = append(tw.logs, logRecord{
   160  			format: format,
   161  			args:   args,
   162  		})
   163  	}
   164  }
   165  
   166  func (tw *testWrapper) flushLogs() {
   167  	tw.TB.Helper()
   168  
   169  	tw.m.Lock()
   170  	defer tw.m.Unlock()
   171  
   172  	for i := range tw.logs {
   173  		if tw.logs[i].format == "" {
   174  			tw.TB.Log(tw.logs[i].args...)
   175  		} else {
   176  			tw.TB.Logf(tw.logs[i].format, tw.logs[i].args...)
   177  		}
   178  	}
   179  
   180  	tw.logs = nil
   181  }
   182  
   183  func (tw *testWrapper) doCleanup() {
   184  	tw.Helper()
   185  
   186  	for len(tw.cleanup) > 0 {
   187  		last := tw.cleanup[len(tw.cleanup)-1]
   188  		tw.cleanup = tw.cleanup[:len(tw.cleanup)-1]
   189  
   190  		last()
   191  	}
   192  }
   193  
   194  type logRecord struct {
   195  	format string
   196  	args   []interface{}
   197  }