consul/lib/semaphore/semaphore_test.go

127 lines
2.8 KiB
Go
Raw Permalink Normal View History

// Copyright (c) HashiCorp, Inc.
[COMPLIANCE] License changes (#18443) * Adding explicit MPL license for sub-package This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Adding explicit MPL license for sub-package This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Updating the license from MPL to Business Source License Going forward, this project will be licensed under the Business Source License v1.1. Please see our blog post for more details at <Blog URL>, FAQ at www.hashicorp.com/licensing-faq, and details of the license at www.hashicorp.com/bsl. * add missing license headers * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 --------- Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com>
2023-08-11 13:12:13 +00:00
// SPDX-License-Identifier: BUSL-1.1
package semaphore
// Based on https://github.com/golang/sync/blob/master/semaphore/semaphore_test.go
import (
"context"
"math/rand"
"runtime"
"sync"
"testing"
"time"
"github.com/stretchr/testify/require"
)
const maxSleep = 1 * time.Millisecond
func HammerDynamic(sem *Dynamic, loops int) {
for i := 0; i < loops; i++ {
sem.Acquire(context.Background())
time.Sleep(time.Duration(rand.Int63n(int64(maxSleep/time.Nanosecond))) * time.Nanosecond)
sem.Release()
}
}
// TestDynamic hammers the semaphore from all available cores to ensure we don't
// hit a panic or race detector notice something wonky.
func TestDynamic(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
t.Parallel()
n := runtime.GOMAXPROCS(0)
loops := 10000 / n
sem := NewDynamic(int64(n))
var wg sync.WaitGroup
wg.Add(n)
for i := 0; i < n; i++ {
go func() {
defer wg.Done()
HammerDynamic(sem, loops)
}()
}
wg.Wait()
}
func TestDynamicPanic(t *testing.T) {
t.Parallel()
defer func() {
if recover() == nil {
t.Fatal("release of an unacquired dynamic semaphore did not panic")
}
}()
w := NewDynamic(1)
w.Release()
}
func checkAcquire(t *testing.T, sem *Dynamic, wantAcquire bool) {
t.Helper()
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)
defer cancel()
err := sem.Acquire(ctx)
if wantAcquire {
require.NoErrorf(t, err, "failed to acquire when we should have")
} else {
require.Error(t, err, "failed to block when should be full")
}
}
func TestDynamicAcquire(t *testing.T) {
t.Parallel()
ctx := context.Background()
sem := NewDynamic(2)
// Consume one slot [free: 1]
sem.Acquire(ctx)
// Should be able to consume another [free: 0]
checkAcquire(t, sem, true)
// Should fail to consume another [free: 0]
checkAcquire(t, sem, false)
// Release 2
sem.Release()
sem.Release()
// Should be able to consume another [free: 1]
checkAcquire(t, sem, true)
// Should be able to consume another [free: 0]
checkAcquire(t, sem, true)
// Should fail to consume another [free: 0]
checkAcquire(t, sem, false)
// Now expand the semaphore and we should be able to acquire again [free: 2]
sem.SetSize(4)
// Should be able to consume another [free: 1]
checkAcquire(t, sem, true)
// Should be able to consume another [free: 0]
checkAcquire(t, sem, true)
// Should fail to consume another [free: 0]
checkAcquire(t, sem, false)
// Shrinking it should work [free: 0]
sem.SetSize(3)
// Should fail to consume another [free: 0]
checkAcquire(t, sem, false)
// Release one [free: 0] (3 slots used are release, size only 3)
sem.Release()
// Should fail to consume another [free: 0]
checkAcquire(t, sem, false)
sem.Release()
// Should be able to consume another [free: 1]
checkAcquire(t, sem, true)
}