github.com/safing/portbase@v0.19.5/utils/call_limiter_test.go (about) 1 package utils 2 3 import ( 4 "sync" 5 "sync/atomic" 6 "testing" 7 "time" 8 9 "github.com/tevino/abool" 10 ) 11 12 func TestCallLimiter(t *testing.T) { 13 t.Parallel() 14 15 pause := 10 * time.Millisecond 16 oa := NewCallLimiter(pause) 17 executed := abool.New() 18 var testWg sync.WaitGroup 19 20 // One execution should gobble up the whole batch. 21 // We are doing this without sleep in function, so dummy exec first to trigger first pause. 22 oa.Do(func() {}) 23 // Start 24 for i := 0; i < 10; i++ { 25 testWg.Add(100) 26 for i := 0; i < 100; i++ { 27 go func() { 28 oa.Do(func() { 29 if !executed.SetToIf(false, true) { 30 t.Errorf("concurrent execution!") 31 } 32 }) 33 testWg.Done() 34 }() 35 } 36 testWg.Wait() 37 // Check if function was executed at least once. 38 if executed.IsNotSet() { 39 t.Errorf("no execution!") 40 } 41 executed.UnSet() // reset check 42 } 43 44 // Wait for pause to reset. 45 time.Sleep(pause) 46 47 // Continuous use with re-execution. 48 // Choose values so that about 10 executions are expected 49 var execs uint32 50 testWg.Add(200) 51 for i := 0; i < 200; i++ { 52 go func() { 53 oa.Do(func() { 54 atomic.AddUint32(&execs, 1) 55 time.Sleep(10 * time.Millisecond) 56 }) 57 testWg.Done() 58 }() 59 60 // Start one goroutine every 1ms. 61 time.Sleep(1 * time.Millisecond) 62 } 63 64 testWg.Wait() 65 if execs <= 5 { 66 t.Errorf("unexpected low exec count: %d", execs) 67 } 68 if execs >= 15 { 69 t.Errorf("unexpected high exec count: %d", execs) 70 } 71 72 // Wait for pause to reset. 73 time.Sleep(pause) 74 75 // Check if the limiter correctly handles panics. 76 testWg.Add(100) 77 for i := 0; i < 100; i++ { 78 go func() { 79 defer func() { 80 _ = recover() 81 testWg.Done() 82 }() 83 oa.Do(func() { 84 time.Sleep(1 * time.Millisecond) 85 panic("test") 86 }) 87 }() 88 time.Sleep(100 * time.Microsecond) 89 } 90 testWg.Wait() 91 }