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 }