github.com/ydb-platform/ydb-go-sdk/v3@v3.89.2/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 _, opt := range opts {
    35  		if opt != nil {
    36  			opt(&options)
    37  		}
    38  	}
    39  
    40  	start := time.Now()
    41  	var testMutex sync.Mutex
    42  	for {
    43  		testCounter++
    44  		// run test, then check stopAfter for guarantee run test least once
    45  		runTest(t, test, &testMutex)
    46  
    47  		if testing.Short() {
    48  			return
    49  		}
    50  
    51  		if time.Since(start) > options.stopAfter || t.Failed() {
    52  			return
    53  		}
    54  	}
    55  }
    56  
    57  func TestManyTimesWithName(t *testing.T, name string, test TestFunc) {
    58  	t.Helper()
    59  
    60  	t.Run(name, func(t *testing.T) {
    61  		t.Helper()
    62  		TestManyTimes(t, test)
    63  	})
    64  }
    65  
    66  type TestFunc func(t testing.TB)
    67  
    68  func runTest(t testing.TB, test TestFunc, testMutex *sync.Mutex) {
    69  	t.Helper()
    70  
    71  	tw := &testWrapper{
    72  		TB: t,
    73  		m:  testMutex,
    74  	}
    75  
    76  	defer tw.doCleanup()
    77  
    78  	test(tw)
    79  }
    80  
    81  type testWrapper struct {
    82  	testing.TB
    83  
    84  	m       *sync.Mutex
    85  	logs    []logRecord
    86  	cleanup []func()
    87  }
    88  
    89  func (tw *testWrapper) Cleanup(f func()) {
    90  	tw.Helper()
    91  
    92  	tw.m.Lock()
    93  	defer tw.m.Unlock()
    94  
    95  	tw.cleanup = append(tw.cleanup, f)
    96  }
    97  
    98  func (tw *testWrapper) Error(args ...interface{}) {
    99  	tw.TB.Helper()
   100  
   101  	tw.flushLogs()
   102  	tw.TB.Error(args...)
   103  }
   104  
   105  func (tw *testWrapper) Errorf(format string, args ...interface{}) {
   106  	tw.TB.Helper()
   107  
   108  	tw.flushLogs()
   109  	tw.TB.Errorf(format, args...)
   110  }
   111  
   112  func (tw *testWrapper) Fatal(args ...interface{}) {
   113  	tw.TB.Helper()
   114  
   115  	tw.flushLogs()
   116  	tw.TB.Fatal(args...)
   117  }
   118  
   119  func (tw *testWrapper) Fatalf(format string, args ...interface{}) {
   120  	tw.TB.Helper()
   121  
   122  	tw.flushLogs()
   123  	tw.TB.Fatalf(format, args...)
   124  }
   125  
   126  func (tw *testWrapper) Fail() {
   127  	tw.TB.Helper()
   128  
   129  	tw.flushLogs()
   130  	tw.TB.Fail()
   131  }
   132  
   133  func (tw *testWrapper) FailNow() {
   134  	tw.TB.Helper()
   135  
   136  	tw.flushLogs()
   137  	tw.TB.FailNow()
   138  }
   139  
   140  func (tw *testWrapper) Log(args ...interface{}) {
   141  	tw.TB.Helper()
   142  
   143  	tw.m.Lock()
   144  	defer tw.m.Unlock()
   145  
   146  	if tw.TB.Failed() {
   147  		tw.TB.Log(args...)
   148  	} else {
   149  		tw.logs = append(tw.logs, logRecord{
   150  			format: "",
   151  			args:   args,
   152  		})
   153  	}
   154  }
   155  
   156  func (tw *testWrapper) Logf(format string, args ...interface{}) {
   157  	tw.TB.Helper()
   158  
   159  	tw.m.Lock()
   160  	defer tw.m.Unlock()
   161  
   162  	if tw.TB.Failed() {
   163  		tw.TB.Logf(format, args...)
   164  	} else {
   165  		tw.logs = append(tw.logs, logRecord{
   166  			format: format,
   167  			args:   args,
   168  		})
   169  	}
   170  }
   171  
   172  func (tw *testWrapper) flushLogs() {
   173  	tw.TB.Helper()
   174  
   175  	tw.m.Lock()
   176  	defer tw.m.Unlock()
   177  
   178  	for i := range tw.logs {
   179  		if tw.logs[i].format == "" {
   180  			tw.TB.Log(tw.logs[i].args...)
   181  		} else {
   182  			tw.TB.Logf(tw.logs[i].format, tw.logs[i].args...)
   183  		}
   184  	}
   185  
   186  	tw.logs = nil
   187  }
   188  
   189  func (tw *testWrapper) doCleanup() {
   190  	tw.Helper()
   191  
   192  	for len(tw.cleanup) > 0 {
   193  		last := tw.cleanup[len(tw.cleanup)-1]
   194  		tw.cleanup = tw.cleanup[:len(tw.cleanup)-1]
   195  
   196  		last()
   197  	}
   198  }
   199  
   200  type logRecord struct {
   201  	format string
   202  	args   []interface{}
   203  }