github.com/kjdelisle/consul@v1.4.5/lib/semaphore/semaphore_test.go (about)

     1  package semaphore
     2  
     3  // Based on https://github.com/golang/sync/blob/master/semaphore/semaphore_test.go
     4  
     5  import (
     6  	"context"
     7  	"math/rand"
     8  	"runtime"
     9  	"sync"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/stretchr/testify/require"
    14  )
    15  
    16  const maxSleep = 1 * time.Millisecond
    17  
    18  func HammerDynamic(sem *Dynamic, loops int) {
    19  	for i := 0; i < loops; i++ {
    20  		sem.Acquire(context.Background())
    21  		time.Sleep(time.Duration(rand.Int63n(int64(maxSleep/time.Nanosecond))) * time.Nanosecond)
    22  		sem.Release()
    23  	}
    24  }
    25  
    26  // TestDynamic hammers the semaphore from all available cores to ensure we don't
    27  // hit a panic or race detector notice something wonky.
    28  func TestDynamic(t *testing.T) {
    29  	t.Parallel()
    30  
    31  	n := runtime.GOMAXPROCS(0)
    32  	loops := 10000 / n
    33  	sem := NewDynamic(int64(n))
    34  	var wg sync.WaitGroup
    35  	wg.Add(n)
    36  	for i := 0; i < n; i++ {
    37  		go func() {
    38  			defer wg.Done()
    39  			HammerDynamic(sem, loops)
    40  		}()
    41  	}
    42  	wg.Wait()
    43  }
    44  
    45  func TestDynamicPanic(t *testing.T) {
    46  	t.Parallel()
    47  
    48  	defer func() {
    49  		if recover() == nil {
    50  			t.Fatal("release of an unacquired dynamic semaphore did not panic")
    51  		}
    52  	}()
    53  	w := NewDynamic(1)
    54  	w.Release()
    55  }
    56  
    57  func checkAcquire(t *testing.T, sem *Dynamic, wantAcquire bool) {
    58  	t.Helper()
    59  	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)
    60  	defer cancel()
    61  	err := sem.Acquire(ctx)
    62  	if wantAcquire {
    63  		require.NoErrorf(t, err, "failed to acquire when we should have")
    64  	} else {
    65  		require.Error(t, err, "failed to block when should be full")
    66  	}
    67  }
    68  
    69  func TestDynamicAcquire(t *testing.T) {
    70  	t.Parallel()
    71  
    72  	ctx := context.Background()
    73  	sem := NewDynamic(2)
    74  
    75  	// Consume one slot [free: 1]
    76  	sem.Acquire(ctx)
    77  	// Should be able to consume another [free: 0]
    78  	checkAcquire(t, sem, true)
    79  	// Should fail to consume another [free: 0]
    80  	checkAcquire(t, sem, false)
    81  
    82  	// Release 2
    83  	sem.Release()
    84  	sem.Release()
    85  
    86  	// Should be able to consume another [free: 1]
    87  	checkAcquire(t, sem, true)
    88  	// Should be able to consume another [free: 0]
    89  	checkAcquire(t, sem, true)
    90  	// Should fail to consume another [free: 0]
    91  	checkAcquire(t, sem, false)
    92  
    93  	// Now expand the semaphore and we should be able to acquire again [free: 2]
    94  	sem.SetSize(4)
    95  
    96  	// Should be able to consume another [free: 1]
    97  	checkAcquire(t, sem, true)
    98  	// Should be able to consume another [free: 0]
    99  	checkAcquire(t, sem, true)
   100  	// Should fail to consume another [free: 0]
   101  	checkAcquire(t, sem, false)
   102  
   103  	// Shrinking it should work [free: 0]
   104  	sem.SetSize(3)
   105  
   106  	// Should fail to consume another [free: 0]
   107  	checkAcquire(t, sem, false)
   108  
   109  	// Release one [free: 0] (3 slots used are release, size only 3)
   110  	sem.Release()
   111  
   112  	// Should fail to consume another [free: 0]
   113  	checkAcquire(t, sem, false)
   114  
   115  	sem.Release()
   116  
   117  	// Should be able to consume another [free: 1]
   118  	checkAcquire(t, sem, true)
   119  }