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  }