diff --git a/debounce/simple_debouncer_test.go b/debounce/simple_debouncer_test.go index fe8a0a0da..fb5ee3e0e 100644 --- a/debounce/simple_debouncer_test.go +++ b/debounce/simple_debouncer_test.go @@ -2,16 +2,150 @@ package debounce import ( "runtime" + "sync" "sync/atomic" "testing" + "time" ) +// TestSimpleDebouncerRace tests SimpleDebouncer for the fact that it does not allow concurrent writing, reading. +func TestSimpleDebouncerRace(t *testing.T) { + operations := 1000 + runs := 100 + count := 3 + + d := NewSimpleDebouncer() + for r := 0; r < runs; r++ { + var counter atomic.Int32 + var wg sync.WaitGroup + wg.Add(count) + + results := make([]bool, count) + fails := make([]bool, count) + for c := range results { + result := &results[c] + fail := &fails[c] + + go func() { + *result = d.Debounce(func() { + for i := 0; i < operations; i++ { + if counter.Add(1) != 1 { + *fail = true + } + time.Sleep(time.Microsecond) + counter.Add(-1) + } + }) + wg.Done() + }() + } + wg.Wait() + + // check results + + finished := 0 + for i, done := range results { + if done { + finished++ + } + if fails[i] { + t.Fatalf("Simultaneous execution detected") + } + } + if finished < 2 { + t.Fatalf("In one run should be finished more than 2 `Debounce` method calls, but finished %d", finished) + } + } +} + +// TestDebouncerExtreme tests SimpleDebouncer in the conditions fast multi `Debounce` method calls and fast execution of the `debounced function`. +func TestDebouncerExtreme(t *testing.T) { + type runResult struct { + executedN int32 + done bool + } + + runs := 10000 + count := 20 + + d := NewSimpleDebouncer() + var wg sync.WaitGroup + for r := 0; r < runs; r++ { + var executionsC atomic.Int32 + wg.Add(count) + + results := make([]runResult, count) + + for c := range results { + result := &results[c] + + go func() { + result.done = d.Debounce(func() { + result.executedN = executionsC.Add(1) + }) + wg.Done() + }() + } + wg.Wait() + + // check results + finished := 0 + for _, result := range results { + if result.done { + if result.executedN == 0 { + t.Fatalf("Wrong execution detected: \n%#v", result) + } + finished++ + } + } + if finished < 2 { + t.Fatalf("In one run should be finished more than 2 `Debounce` method calls, but finished %d", finished) + } + } +} + +// TestSimpleDebouncerCount tests SimpleDebouncer for the fact that it pended only one function call. +func TestSimpleDebouncerCount(t *testing.T) { + count := 10 + + d := NewSimpleDebouncer() + var prepared, start, done sync.WaitGroup + prepared.Add(count - 1) + start.Add(1) + done.Add(count - 1) + + finished := 0 + for c := 0; c < count-1; c++ { + go func() { + prepared.Done() + start.Wait() + d.Debounce(func() { + finished++ + }) + done.Done() + }() + } + d.Debounce(func() { + prepared.Wait() + start.Done() + finished++ + time.Sleep(time.Second) + }) + done.Wait() + + // check results + if finished != 2 { + t.Fatalf("Should be finished more than 2 `Debounce` method calls, but finished %d", finished) + } +} + // TestDebouncer tests that the debouncer allows only one function to execute at a time func TestSimpleDebouncer(t *testing.T) { d := NewSimpleDebouncer() var executions int32 startedCh := make(chan struct{}, 1) doneCh := make(chan struct{}, 1) + // Function to increment executions fn := func() { <-startedCh // Simulate work