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 }