This commit is contained in:
@@ -1,27 +0,0 @@
|
||||
package syncx
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAtomicBool(t *testing.T) {
|
||||
val := ForAtomicBool(true)
|
||||
assert.True(t, val.True())
|
||||
val.Set(false)
|
||||
assert.False(t, val.True())
|
||||
val.Set(true)
|
||||
assert.True(t, val.True())
|
||||
val.Set(false)
|
||||
assert.False(t, val.True())
|
||||
ok := val.CompareAndSwap(false, true)
|
||||
assert.True(t, ok)
|
||||
assert.True(t, val.True())
|
||||
ok = val.CompareAndSwap(true, false)
|
||||
assert.True(t, ok)
|
||||
assert.False(t, val.True())
|
||||
ok = val.CompareAndSwap(true, false)
|
||||
assert.False(t, ok)
|
||||
assert.False(t, val.True())
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package syncx
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAtomicDuration(t *testing.T) {
|
||||
d := ForAtomicDuration(time.Duration(100))
|
||||
assert.Equal(t, time.Duration(100), d.Load())
|
||||
d.Set(time.Duration(200))
|
||||
assert.Equal(t, time.Duration(200), d.Load())
|
||||
assert.True(t, d.CompareAndSwap(time.Duration(200), time.Duration(300)))
|
||||
assert.Equal(t, time.Duration(300), d.Load())
|
||||
assert.False(t, d.CompareAndSwap(time.Duration(200), time.Duration(400)))
|
||||
assert.Equal(t, time.Duration(300), d.Load())
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package syncx
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAtomicFloat64(t *testing.T) {
|
||||
f := ForAtomicFloat64(100)
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < 5; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
for i := 0; i < 100; i++ {
|
||||
f.Add(1)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
assert.Equal(t, float64(600), f.Load())
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
package syncx
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestBarrier_Guard(t *testing.T) {
|
||||
const total = 10000
|
||||
var barrier Barrier
|
||||
var count int
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(total)
|
||||
for i := 0; i < total; i++ {
|
||||
go barrier.Guard(func() {
|
||||
count++
|
||||
wg.Done()
|
||||
})
|
||||
}
|
||||
wg.Wait()
|
||||
assert.Equal(t, total, count)
|
||||
}
|
||||
|
||||
func TestBarrierPtr_Guard(t *testing.T) {
|
||||
const total = 10000
|
||||
barrier := new(Barrier)
|
||||
var count int
|
||||
wg := new(sync.WaitGroup)
|
||||
wg.Add(total)
|
||||
for i := 0; i < total; i++ {
|
||||
go barrier.Guard(func() {
|
||||
count++
|
||||
wg.Done()
|
||||
})
|
||||
}
|
||||
wg.Wait()
|
||||
assert.Equal(t, total, count)
|
||||
}
|
||||
|
||||
func TestGuard(t *testing.T) {
|
||||
const total = 10000
|
||||
var count int
|
||||
var lock sync.Mutex
|
||||
wg := new(sync.WaitGroup)
|
||||
wg.Add(total)
|
||||
for i := 0; i < total; i++ {
|
||||
go Guard(&lock, func() {
|
||||
count++
|
||||
wg.Done()
|
||||
})
|
||||
}
|
||||
wg.Wait()
|
||||
assert.Equal(t, total, count)
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
package syncx
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestTimeoutCondWait(t *testing.T) {
|
||||
var wait sync.WaitGroup
|
||||
cond := NewCond()
|
||||
wait.Add(2)
|
||||
go func() {
|
||||
cond.Wait()
|
||||
wait.Done()
|
||||
}()
|
||||
time.Sleep(time.Duration(50) * time.Millisecond)
|
||||
go func() {
|
||||
cond.Signal()
|
||||
wait.Done()
|
||||
}()
|
||||
wait.Wait()
|
||||
}
|
||||
|
||||
func TestTimeoutCondWaitTimeout(t *testing.T) {
|
||||
var wait sync.WaitGroup
|
||||
cond := NewCond()
|
||||
wait.Add(1)
|
||||
go func() {
|
||||
cond.WaitWithTimeout(time.Duration(500) * time.Millisecond)
|
||||
wait.Done()
|
||||
}()
|
||||
wait.Wait()
|
||||
}
|
||||
|
||||
func TestTimeoutCondWaitTimeoutRemain(t *testing.T) {
|
||||
var wait sync.WaitGroup
|
||||
cond := NewCond()
|
||||
wait.Add(2)
|
||||
ch := make(chan time.Duration, 1)
|
||||
defer close(ch)
|
||||
timeout := time.Duration(2000) * time.Millisecond
|
||||
go func() {
|
||||
remainTimeout, _ := cond.WaitWithTimeout(timeout)
|
||||
ch <- remainTimeout
|
||||
wait.Done()
|
||||
}()
|
||||
sleep(200)
|
||||
go func() {
|
||||
cond.Signal()
|
||||
wait.Done()
|
||||
}()
|
||||
wait.Wait()
|
||||
remainTimeout := <-ch
|
||||
assert.True(t, remainTimeout < timeout, "expect remainTimeout %v < %v", remainTimeout, timeout)
|
||||
assert.True(t, remainTimeout >= time.Duration(200)*time.Millisecond,
|
||||
"expect remainTimeout %v >= 200 millisecond", remainTimeout)
|
||||
}
|
||||
|
||||
func TestSignalNoWait(t *testing.T) {
|
||||
cond := NewCond()
|
||||
cond.Signal()
|
||||
}
|
||||
|
||||
func sleep(millisecond int) {
|
||||
time.Sleep(time.Duration(millisecond) * time.Millisecond)
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package syncx
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDoneChanClose(t *testing.T) {
|
||||
doneChan := NewDoneChan()
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
doneChan.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func TestDoneChanDone(t *testing.T) {
|
||||
var waitGroup sync.WaitGroup
|
||||
doneChan := NewDoneChan()
|
||||
|
||||
waitGroup.Add(1)
|
||||
go func() {
|
||||
<-doneChan.Done()
|
||||
waitGroup.Done()
|
||||
}()
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
doneChan.Close()
|
||||
}
|
||||
|
||||
waitGroup.Wait()
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
package syncx
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestImmutableResource(t *testing.T) {
|
||||
var count int
|
||||
r := NewImmutableResource(func() (any, error) {
|
||||
count++
|
||||
return "hello", nil
|
||||
})
|
||||
|
||||
res, err := r.Get()
|
||||
assert.Equal(t, "hello", res)
|
||||
assert.Equal(t, 1, count)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// again
|
||||
res, err = r.Get()
|
||||
assert.Equal(t, "hello", res)
|
||||
assert.Equal(t, 1, count)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestImmutableResourceError(t *testing.T) {
|
||||
var count int
|
||||
r := NewImmutableResource(func() (any, error) {
|
||||
count++
|
||||
return nil, errors.New("any")
|
||||
})
|
||||
|
||||
res, err := r.Get()
|
||||
assert.Nil(t, res)
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, "any", err.Error())
|
||||
assert.Equal(t, 1, count)
|
||||
|
||||
// again
|
||||
res, err = r.Get()
|
||||
assert.Nil(t, res)
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, "any", err.Error())
|
||||
assert.Equal(t, 1, count)
|
||||
|
||||
r.refreshInterval = 0
|
||||
time.Sleep(time.Millisecond)
|
||||
res, err = r.Get()
|
||||
assert.Nil(t, res)
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, "any", err.Error())
|
||||
assert.Equal(t, 2, count)
|
||||
}
|
||||
|
||||
func TestImmutableResourceErrorRefreshAlways(t *testing.T) {
|
||||
var count int
|
||||
r := NewImmutableResource(func() (any, error) {
|
||||
count++
|
||||
return nil, errors.New("any")
|
||||
}, WithRefreshIntervalOnFailure(0))
|
||||
|
||||
res, err := r.Get()
|
||||
assert.Nil(t, res)
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, "any", err.Error())
|
||||
assert.Equal(t, 1, count)
|
||||
|
||||
// again
|
||||
res, err = r.Get()
|
||||
assert.Nil(t, res)
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, "any", err.Error())
|
||||
assert.Equal(t, 2, count)
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package syncx
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestLimit(t *testing.T) {
|
||||
limit := NewLimit(2)
|
||||
limit.Borrow()
|
||||
assert.True(t, limit.TryBorrow())
|
||||
assert.False(t, limit.TryBorrow())
|
||||
assert.Nil(t, limit.Return())
|
||||
assert.Nil(t, limit.Return())
|
||||
assert.Equal(t, ErrLimitReturn, limit.Return())
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
package syncx
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestLockedCallDo(t *testing.T) {
|
||||
g := NewLockedCalls()
|
||||
v, err := g.Do("key", func() (any, error) {
|
||||
return "bar", nil
|
||||
})
|
||||
if got, want := fmt.Sprintf("%v (%T)", v, v), "bar (string)"; got != want {
|
||||
t.Errorf("Do = %v; want %v", got, want)
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("Do error = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLockedCallDoErr(t *testing.T) {
|
||||
g := NewLockedCalls()
|
||||
someErr := errors.New("some error")
|
||||
v, err := g.Do("key", func() (any, error) {
|
||||
return nil, someErr
|
||||
})
|
||||
if !errors.Is(err, someErr) {
|
||||
t.Errorf("Do error = %v; want someErr", err)
|
||||
}
|
||||
if v != nil {
|
||||
t.Errorf("unexpected non-nil value %#v", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLockedCallDoDupSuppress(t *testing.T) {
|
||||
g := NewLockedCalls()
|
||||
c := make(chan string)
|
||||
var calls int
|
||||
fn := func() (any, error) {
|
||||
calls++
|
||||
ret := calls
|
||||
<-c
|
||||
calls--
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
const n = 10
|
||||
var results []int
|
||||
var lock sync.Mutex
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < n; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
v, err := g.Do("key", fn)
|
||||
if err != nil {
|
||||
t.Errorf("Do error: %v", err)
|
||||
}
|
||||
|
||||
lock.Lock()
|
||||
results = append(results, v.(int))
|
||||
lock.Unlock()
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond) // let goroutines above block
|
||||
for i := 0; i < n; i++ {
|
||||
c <- "bar"
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
for _, item := range results {
|
||||
if item != 1 {
|
||||
t.Errorf("number of calls = %d; want 1", item)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package syncx
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestManagedResource(t *testing.T) {
|
||||
var count int32
|
||||
resource := NewManagedResource(func() any {
|
||||
return atomic.AddInt32(&count, 1)
|
||||
}, func(a, b any) bool {
|
||||
return a == b
|
||||
})
|
||||
|
||||
assert.Equal(t, resource.Take(), resource.Take())
|
||||
old := resource.Take()
|
||||
resource.MarkBroken(old)
|
||||
assert.NotEqual(t, old, resource.Take())
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package syncx
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestOnce(t *testing.T) {
|
||||
var v int
|
||||
add := Once(func() {
|
||||
v++
|
||||
})
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
add()
|
||||
}
|
||||
|
||||
assert.Equal(t, 1, v)
|
||||
}
|
||||
|
||||
func BenchmarkOnce(b *testing.B) {
|
||||
var v int
|
||||
add := Once(func() {
|
||||
v++
|
||||
})
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
add()
|
||||
}
|
||||
assert.Equal(b, 1, v)
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package syncx
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestOnceGuard(t *testing.T) {
|
||||
var guard OnceGuard
|
||||
|
||||
assert.False(t, guard.Taken())
|
||||
assert.True(t, guard.Take())
|
||||
assert.True(t, guard.Taken())
|
||||
assert.False(t, guard.Take())
|
||||
assert.True(t, guard.Taken())
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
package syncx
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/perfect-panel/server/pkg/lang"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const limit = 10
|
||||
|
||||
func TestPoolGet(t *testing.T) {
|
||||
stack := NewPool(limit, create, destroy)
|
||||
ch := make(chan lang.PlaceholderType)
|
||||
|
||||
for i := 0; i < limit; i++ {
|
||||
var fail AtomicBool
|
||||
go func() {
|
||||
v := stack.Get()
|
||||
if v.(int) != 1 {
|
||||
fail.Set(true)
|
||||
}
|
||||
ch <- lang.Placeholder
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ch:
|
||||
case <-time.After(time.Second):
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if fail.True() {
|
||||
t.Fatal("unmatch value")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPoolPopTooMany(t *testing.T) {
|
||||
stack := NewPool(limit, create, destroy)
|
||||
ch := make(chan lang.PlaceholderType, 1)
|
||||
|
||||
for i := 0; i < limit; i++ {
|
||||
var wait sync.WaitGroup
|
||||
wait.Add(1)
|
||||
go func() {
|
||||
stack.Get()
|
||||
ch <- lang.Placeholder
|
||||
wait.Done()
|
||||
}()
|
||||
|
||||
wait.Wait()
|
||||
select {
|
||||
case <-ch:
|
||||
default:
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
var waitGroup, pushWait sync.WaitGroup
|
||||
waitGroup.Add(1)
|
||||
pushWait.Add(1)
|
||||
go func() {
|
||||
pushWait.Done()
|
||||
stack.Get()
|
||||
waitGroup.Done()
|
||||
}()
|
||||
|
||||
pushWait.Wait()
|
||||
stack.Put(1)
|
||||
waitGroup.Wait()
|
||||
}
|
||||
|
||||
func TestPoolPopFirst(t *testing.T) {
|
||||
var value int32
|
||||
stack := NewPool(limit, func() any {
|
||||
return atomic.AddInt32(&value, 1)
|
||||
}, destroy)
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
v := stack.Get().(int32)
|
||||
assert.Equal(t, 1, int(v))
|
||||
stack.Put(v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPoolWithMaxAge(t *testing.T) {
|
||||
var value int32
|
||||
stack := NewPool(limit, func() any {
|
||||
return atomic.AddInt32(&value, 1)
|
||||
}, destroy, WithMaxAge(time.Millisecond))
|
||||
|
||||
v1 := stack.Get().(int32)
|
||||
// put nil should not matter
|
||||
stack.Put(nil)
|
||||
stack.Put(v1)
|
||||
time.Sleep(time.Millisecond * 10)
|
||||
v2 := stack.Get().(int32)
|
||||
assert.NotEqual(t, v1, v2)
|
||||
}
|
||||
|
||||
func TestNewPoolPanics(t *testing.T) {
|
||||
assert.Panics(t, func() {
|
||||
NewPool(0, create, destroy)
|
||||
})
|
||||
}
|
||||
|
||||
func create() any {
|
||||
return 1
|
||||
}
|
||||
|
||||
func destroy(_ any) {
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package syncx
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRefCleaner(t *testing.T) {
|
||||
var count int
|
||||
clean := func() {
|
||||
count += 1
|
||||
}
|
||||
|
||||
cleaner := NewRefResource(clean)
|
||||
err := cleaner.Use()
|
||||
assert.Nil(t, err)
|
||||
err = cleaner.Use()
|
||||
assert.Nil(t, err)
|
||||
cleaner.Clean()
|
||||
cleaner.Clean()
|
||||
assert.Equal(t, 1, count)
|
||||
cleaner.Clean()
|
||||
cleaner.Clean()
|
||||
assert.Equal(t, 1, count)
|
||||
assert.Equal(t, ErrUseOfCleaned, cleaner.Use())
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
package syncx
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type dummyResource struct {
|
||||
age int
|
||||
}
|
||||
|
||||
func (dr *dummyResource) Close() error {
|
||||
return errors.New("close")
|
||||
}
|
||||
|
||||
func TestResourceManager_GetResource(t *testing.T) {
|
||||
manager := NewResourceManager()
|
||||
defer manager.Close()
|
||||
|
||||
var age int
|
||||
for i := 0; i < 10; i++ {
|
||||
val, err := manager.GetResource("key", func() (io.Closer, error) {
|
||||
age++
|
||||
return &dummyResource{
|
||||
age: age,
|
||||
}, nil
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, val.(*dummyResource).age)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceManager_GetResourceError(t *testing.T) {
|
||||
manager := NewResourceManager()
|
||||
defer manager.Close()
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
_, err := manager.GetResource("key", func() (io.Closer, error) {
|
||||
return nil, errors.New("fail")
|
||||
})
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceManager_Close(t *testing.T) {
|
||||
manager := NewResourceManager()
|
||||
defer manager.Close()
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
_, err := manager.GetResource("key", func() (io.Closer, error) {
|
||||
return nil, errors.New("fail")
|
||||
})
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
if assert.NoError(t, manager.Close()) {
|
||||
assert.Equal(t, 0, len(manager.resources))
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceManager_UseAfterClose(t *testing.T) {
|
||||
manager := NewResourceManager()
|
||||
defer manager.Close()
|
||||
|
||||
_, err := manager.GetResource("key", func() (io.Closer, error) {
|
||||
return nil, errors.New("fail")
|
||||
})
|
||||
assert.NotNil(t, err)
|
||||
if assert.NoError(t, manager.Close()) {
|
||||
_, err = manager.GetResource("key", func() (io.Closer, error) {
|
||||
return nil, errors.New("fail")
|
||||
})
|
||||
assert.NotNil(t, err)
|
||||
|
||||
assert.Panics(t, func() {
|
||||
_, err = manager.GetResource("key", func() (io.Closer, error) {
|
||||
return &dummyResource{age: 123}, nil
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceManager_Inject(t *testing.T) {
|
||||
manager := NewResourceManager()
|
||||
defer manager.Close()
|
||||
|
||||
manager.Inject("key", &dummyResource{
|
||||
age: 10,
|
||||
})
|
||||
|
||||
val, err := manager.GetResource("key", func() (io.Closer, error) {
|
||||
return nil, nil
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 10, val.(*dummyResource).age)
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
package syncx
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestExclusiveCallDo(t *testing.T) {
|
||||
g := NewSingleFlight()
|
||||
v, err := g.Do("key", func() (any, error) {
|
||||
return "bar", nil
|
||||
})
|
||||
if got, want := fmt.Sprintf("%v (%T)", v, v), "bar (string)"; got != want {
|
||||
t.Errorf("Do = %v; want %v", got, want)
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("Do error = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExclusiveCallDoErr(t *testing.T) {
|
||||
g := NewSingleFlight()
|
||||
someErr := errors.New("some error")
|
||||
v, err := g.Do("key", func() (any, error) {
|
||||
return nil, someErr
|
||||
})
|
||||
if !errors.Is(err, someErr) {
|
||||
t.Errorf("Do error = %v; want someErr", err)
|
||||
}
|
||||
if v != nil {
|
||||
t.Errorf("unexpected non-nil value %#v", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExclusiveCallDoDupSuppress(t *testing.T) {
|
||||
g := NewSingleFlight()
|
||||
c := make(chan string)
|
||||
var calls int32
|
||||
fn := func() (any, error) {
|
||||
atomic.AddInt32(&calls, 1)
|
||||
return <-c, nil
|
||||
}
|
||||
|
||||
const n = 10
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < n; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
v, err := g.Do("key", fn)
|
||||
if err != nil {
|
||||
t.Errorf("Do error: %v", err)
|
||||
}
|
||||
if v.(string) != "bar" {
|
||||
t.Errorf("got %q; want %q", v, "bar")
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond) // let goroutines above block
|
||||
c <- "bar"
|
||||
wg.Wait()
|
||||
if got := atomic.LoadInt32(&calls); got != 1 {
|
||||
t.Errorf("number of calls = %d; want 1", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExclusiveCallDoDiffDupSuppress(t *testing.T) {
|
||||
g := NewSingleFlight()
|
||||
broadcast := make(chan struct{})
|
||||
var calls int32
|
||||
tests := []string{"e", "a", "e", "a", "b", "c", "b", "a", "c", "d", "b", "c", "d"}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for _, key := range tests {
|
||||
wg.Add(1)
|
||||
go func(k string) {
|
||||
<-broadcast // get all goroutines ready
|
||||
_, err := g.Do(k, func() (any, error) {
|
||||
atomic.AddInt32(&calls, 1)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
return nil, nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("Do error: %v", err)
|
||||
}
|
||||
wg.Done()
|
||||
}(key)
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond) // let goroutines above block
|
||||
close(broadcast)
|
||||
wg.Wait()
|
||||
|
||||
if got := atomic.LoadInt32(&calls); got != 5 {
|
||||
// five letters
|
||||
t.Errorf("number of calls = %d; want 5", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExclusiveCallDoExDupSuppress(t *testing.T) {
|
||||
g := NewSingleFlight()
|
||||
c := make(chan string)
|
||||
var calls int32
|
||||
fn := func() (any, error) {
|
||||
atomic.AddInt32(&calls, 1)
|
||||
return <-c, nil
|
||||
}
|
||||
|
||||
const n = 10
|
||||
var wg sync.WaitGroup
|
||||
var freshes int32
|
||||
for i := 0; i < n; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
v, fresh, err := g.DoEx("key", fn)
|
||||
if err != nil {
|
||||
t.Errorf("Do error: %v", err)
|
||||
}
|
||||
if fresh {
|
||||
atomic.AddInt32(&freshes, 1)
|
||||
}
|
||||
if v.(string) != "bar" {
|
||||
t.Errorf("got %q; want %q", v, "bar")
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond) // let goroutines above block
|
||||
c <- "bar"
|
||||
wg.Wait()
|
||||
if got := atomic.LoadInt32(&calls); got != 1 {
|
||||
t.Errorf("number of calls = %d; want 1", got)
|
||||
}
|
||||
if got := atomic.LoadInt32(&freshes); got != 1 {
|
||||
t.Errorf("freshes = %d; want 1", got)
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package syncx
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/perfect-panel/server/pkg/lang"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestTryLock(t *testing.T) {
|
||||
var lock SpinLock
|
||||
assert.True(t, lock.TryLock())
|
||||
assert.False(t, lock.TryLock())
|
||||
lock.Unlock()
|
||||
assert.True(t, lock.TryLock())
|
||||
}
|
||||
|
||||
func TestSpinLock(t *testing.T) {
|
||||
var lock SpinLock
|
||||
lock.Lock()
|
||||
assert.False(t, lock.TryLock())
|
||||
lock.Unlock()
|
||||
assert.True(t, lock.TryLock())
|
||||
}
|
||||
|
||||
func TestSpinLockRace(t *testing.T) {
|
||||
var lock SpinLock
|
||||
lock.Lock()
|
||||
var wait sync.WaitGroup
|
||||
wait.Add(1)
|
||||
go func() {
|
||||
wait.Done()
|
||||
}()
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
lock.Unlock()
|
||||
wait.Wait()
|
||||
assert.True(t, lock.TryLock())
|
||||
}
|
||||
|
||||
func TestSpinLock_TryLock(t *testing.T) {
|
||||
var lock SpinLock
|
||||
var count int32
|
||||
var wait sync.WaitGroup
|
||||
wait.Add(2)
|
||||
sig := make(chan lang.PlaceholderType)
|
||||
|
||||
go func() {
|
||||
lock.TryLock()
|
||||
sig <- lang.Placeholder
|
||||
atomic.AddInt32(&count, 1)
|
||||
runtime.Gosched()
|
||||
lock.Unlock()
|
||||
wait.Done()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
<-sig
|
||||
lock.Lock()
|
||||
atomic.AddInt32(&count, 1)
|
||||
lock.Unlock()
|
||||
wait.Done()
|
||||
}()
|
||||
|
||||
wait.Wait()
|
||||
assert.Equal(t, int32(2), atomic.LoadInt32(&count))
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package syncx
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestTimeoutLimit(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
interval time.Duration
|
||||
}{
|
||||
{
|
||||
name: "no wait",
|
||||
},
|
||||
{
|
||||
name: "wait",
|
||||
interval: time.Millisecond * 100,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
limit := NewTimeoutLimit(2)
|
||||
assert.Nil(t, limit.Borrow(time.Millisecond*200))
|
||||
assert.Nil(t, limit.Borrow(time.Millisecond*200))
|
||||
var wait1, wait2, wait3 sync.WaitGroup
|
||||
wait1.Add(1)
|
||||
wait2.Add(1)
|
||||
wait3.Add(1)
|
||||
go func() {
|
||||
wait1.Wait()
|
||||
wait2.Done()
|
||||
time.Sleep(test.interval)
|
||||
assert.Nil(t, limit.Return())
|
||||
wait3.Done()
|
||||
}()
|
||||
wait1.Done()
|
||||
wait2.Wait()
|
||||
assert.Nil(t, limit.Borrow(time.Second))
|
||||
wait3.Wait()
|
||||
assert.Equal(t, ErrTimeout, limit.Borrow(time.Millisecond*100))
|
||||
assert.Nil(t, limit.Return())
|
||||
assert.Nil(t, limit.Return())
|
||||
assert.Equal(t, ErrLimitReturn, limit.Return())
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user