mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-22 11:32:09 +00:00
343 lines
6.2 KiB
Go
343 lines
6.2 KiB
Go
|
/*
|
||
|
Copyright 2017 Albert Tedja
|
||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
you may not use this file except in compliance with the License.
|
||
|
You may obtain a copy of the License at
|
||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||
|
Unless required by applicable law or agreed to in writing, software
|
||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
See the License for the specific language governing permissions and
|
||
|
limitations under the License.
|
||
|
*/
|
||
|
package mputil
|
||
|
|
||
|
import (
|
||
|
"sync"
|
||
|
"testing"
|
||
|
"time"
|
||
|
|
||
|
"github.com/stretchr/testify/assert"
|
||
|
)
|
||
|
|
||
|
func TestUnique(t *testing.T) {
|
||
|
var arr []string
|
||
|
assert := assert.New(t)
|
||
|
|
||
|
arr = []string{"a", "b", "c"}
|
||
|
assert.Equal(arr, unique(arr))
|
||
|
|
||
|
arr = []string{"a", "a", "a"}
|
||
|
assert.Equal([]string{"a"}, unique(arr))
|
||
|
|
||
|
arr = []string{"a", "a", "b"}
|
||
|
assert.Equal([]string{"a", "b"}, unique(arr))
|
||
|
|
||
|
arr = []string{"a", "b", "a"}
|
||
|
assert.Equal([]string{"a", "b"}, unique(arr))
|
||
|
|
||
|
arr = []string{"a", "b", "c", "b", "d"}
|
||
|
assert.Equal([]string{"a", "b", "c", "d"}, unique(arr))
|
||
|
}
|
||
|
|
||
|
func TestGetChan(t *testing.T) {
|
||
|
ch1 := getChan("a")
|
||
|
ch2 := getChan("aa")
|
||
|
ch3 := getChan("a")
|
||
|
|
||
|
assert := assert.New(t)
|
||
|
assert.NotEqual(ch1, ch2)
|
||
|
assert.Equal(ch1, ch3)
|
||
|
}
|
||
|
|
||
|
func TestLockUnlock(t *testing.T) {
|
||
|
var wg sync.WaitGroup
|
||
|
|
||
|
wg.Add(5)
|
||
|
|
||
|
go func() {
|
||
|
lock := NewMultilock("dog", "cat", "owl")
|
||
|
lock.Lock()
|
||
|
defer lock.Unlock()
|
||
|
|
||
|
<-time.After(100 * time.Millisecond)
|
||
|
wg.Done()
|
||
|
}()
|
||
|
|
||
|
go func() {
|
||
|
lock := NewMultilock("cat", "dog", "bird")
|
||
|
lock.Lock()
|
||
|
defer lock.Unlock()
|
||
|
|
||
|
<-time.After(100 * time.Millisecond)
|
||
|
wg.Done()
|
||
|
}()
|
||
|
|
||
|
go func() {
|
||
|
lock := NewMultilock("cat", "bird", "owl")
|
||
|
lock.Lock()
|
||
|
defer lock.Unlock()
|
||
|
|
||
|
<-time.After(100 * time.Millisecond)
|
||
|
wg.Done()
|
||
|
}()
|
||
|
|
||
|
go func() {
|
||
|
lock := NewMultilock("bird", "owl", "snake")
|
||
|
lock.Lock()
|
||
|
defer lock.Unlock()
|
||
|
|
||
|
<-time.After(100 * time.Millisecond)
|
||
|
wg.Done()
|
||
|
}()
|
||
|
|
||
|
go func() {
|
||
|
lock := NewMultilock("owl", "snake")
|
||
|
lock.Lock()
|
||
|
defer lock.Unlock()
|
||
|
|
||
|
<-time.After(1 * time.Second)
|
||
|
wg.Done()
|
||
|
}()
|
||
|
|
||
|
wg.Wait()
|
||
|
}
|
||
|
|
||
|
func TestLockUnlock_CleansUnused(t *testing.T) {
|
||
|
var wg sync.WaitGroup
|
||
|
wg.Add(1)
|
||
|
go func() {
|
||
|
lock := NewMultilock("dog", "cat", "owl")
|
||
|
lock.Lock()
|
||
|
assert.Equal(t, 3, len(locks.list))
|
||
|
defer lock.Unlock()
|
||
|
|
||
|
<-time.After(100 * time.Millisecond)
|
||
|
wg.Done()
|
||
|
}()
|
||
|
wg.Wait()
|
||
|
// We expect that unlocking completely cleared the locks list
|
||
|
// given all 3 lock keys were unused at time of unlock.
|
||
|
assert.Equal(t, 0, len(locks.list))
|
||
|
}
|
||
|
|
||
|
func TestLockUnlock_DoesNotCleanIfHeldElsewhere(t *testing.T) {
|
||
|
var wg sync.WaitGroup
|
||
|
wg.Add(2)
|
||
|
go func() {
|
||
|
lock := NewMultilock("cat")
|
||
|
lock.Lock()
|
||
|
// We take 200 milliseconds to release the lock on "cat"
|
||
|
<-time.After(200 * time.Millisecond)
|
||
|
lock.Unlock()
|
||
|
// Assert that at the end of this goroutine, all locks are cleared.
|
||
|
assert.Equal(t, 0, len(locks.list))
|
||
|
wg.Done()
|
||
|
}()
|
||
|
go func() {
|
||
|
lock := NewMultilock("dog", "cat", "owl")
|
||
|
lock.Lock()
|
||
|
// We release the locks after 100 milliseconds, and check that "cat" is not
|
||
|
// cleared as a lock for it is still held by the previous goroutine.
|
||
|
<-time.After(100 * time.Millisecond)
|
||
|
lock.Unlock()
|
||
|
assert.Equal(t, 1, len(locks.list))
|
||
|
_, ok := locks.list["cat"]
|
||
|
assert.Equal(t, true, ok)
|
||
|
wg.Done()
|
||
|
}()
|
||
|
wg.Wait()
|
||
|
// We expect that at the end of this test, all locks are cleared.
|
||
|
assert.Equal(t, 0, len(locks.list))
|
||
|
}
|
||
|
|
||
|
func TestYield(t *testing.T) {
|
||
|
var wg sync.WaitGroup
|
||
|
|
||
|
wg.Add(2)
|
||
|
var resources = map[string]int{}
|
||
|
|
||
|
go func() {
|
||
|
lock := NewMultilock("A", "C")
|
||
|
lock.Lock()
|
||
|
defer lock.Unlock()
|
||
|
|
||
|
for resources["ac"] == 0 {
|
||
|
lock.Yield()
|
||
|
}
|
||
|
resources["dc"] = 10
|
||
|
|
||
|
wg.Done()
|
||
|
}()
|
||
|
|
||
|
go func() {
|
||
|
lock := NewMultilock("D", "C")
|
||
|
lock.Lock()
|
||
|
defer lock.Unlock()
|
||
|
|
||
|
resources["ac"] = 5
|
||
|
for resources["dc"] == 0 {
|
||
|
lock.Yield()
|
||
|
}
|
||
|
|
||
|
wg.Done()
|
||
|
}()
|
||
|
|
||
|
wg.Wait()
|
||
|
|
||
|
assert.Equal(t, 5, resources["ac"])
|
||
|
assert.Equal(t, 10, resources["dc"])
|
||
|
}
|
||
|
|
||
|
func TestClean(t *testing.T) {
|
||
|
var wg sync.WaitGroup
|
||
|
|
||
|
wg.Add(3)
|
||
|
|
||
|
// some goroutine that holds multiple locks
|
||
|
go1done := make(chan bool, 1)
|
||
|
go func() {
|
||
|
Loop:
|
||
|
for {
|
||
|
select {
|
||
|
case <-go1done:
|
||
|
break Loop
|
||
|
default:
|
||
|
lock := NewMultilock("A", "B", "C", "E", "Z")
|
||
|
lock.Lock()
|
||
|
<-time.After(30 * time.Millisecond)
|
||
|
lock.Unlock()
|
||
|
}
|
||
|
}
|
||
|
wg.Done()
|
||
|
}()
|
||
|
|
||
|
// another goroutine
|
||
|
go2done := make(chan bool, 1)
|
||
|
go func() {
|
||
|
Loop:
|
||
|
for {
|
||
|
select {
|
||
|
case <-go2done:
|
||
|
break Loop
|
||
|
default:
|
||
|
lock := NewMultilock("B", "C", "K", "L", "Z")
|
||
|
lock.Lock()
|
||
|
<-time.After(200 * time.Millisecond)
|
||
|
lock.Unlock()
|
||
|
}
|
||
|
}
|
||
|
wg.Done()
|
||
|
}()
|
||
|
|
||
|
// this one cleans up the locks every 100 ms
|
||
|
done := make(chan bool, 1)
|
||
|
go func() {
|
||
|
c := time.Tick(100 * time.Millisecond)
|
||
|
Loop:
|
||
|
for {
|
||
|
select {
|
||
|
case <-done:
|
||
|
break Loop
|
||
|
case <-c:
|
||
|
Clean()
|
||
|
}
|
||
|
}
|
||
|
wg.Done()
|
||
|
}()
|
||
|
|
||
|
<-time.After(2 * time.Second)
|
||
|
go1done <- true
|
||
|
go2done <- true
|
||
|
<-time.After(1 * time.Second)
|
||
|
done <- true
|
||
|
wg.Wait()
|
||
|
assert.Equal(t, []string{}, Clean())
|
||
|
}
|
||
|
|
||
|
func TestBankAccountProblem(t *testing.T) {
|
||
|
var wg sync.WaitGroup
|
||
|
wg.Add(3)
|
||
|
|
||
|
joe := 50.0
|
||
|
susan := 100.0
|
||
|
|
||
|
// withdraw $80 from joe, only if balance is sufficient
|
||
|
go func() {
|
||
|
lock := NewMultilock("joe")
|
||
|
lock.Lock()
|
||
|
defer lock.Unlock()
|
||
|
|
||
|
for joe < 80.0 {
|
||
|
lock.Yield()
|
||
|
}
|
||
|
joe -= 80.0
|
||
|
|
||
|
wg.Done()
|
||
|
}()
|
||
|
|
||
|
// transfer $200 from susan to joe, only if balance is sufficient
|
||
|
go func() {
|
||
|
lock := NewMultilock("joe", "susan")
|
||
|
lock.Lock()
|
||
|
defer lock.Unlock()
|
||
|
|
||
|
for susan < 200.0 {
|
||
|
lock.Yield()
|
||
|
}
|
||
|
|
||
|
susan -= 200.0
|
||
|
joe += 200.0
|
||
|
|
||
|
wg.Done()
|
||
|
}()
|
||
|
|
||
|
// susan deposit $300 to cover balance
|
||
|
go func() {
|
||
|
lock := NewMultilock("susan")
|
||
|
lock.Lock()
|
||
|
defer lock.Unlock()
|
||
|
|
||
|
susan += 300.0
|
||
|
|
||
|
wg.Done()
|
||
|
}()
|
||
|
|
||
|
wg.Wait()
|
||
|
assert.Equal(t, 170.0, joe)
|
||
|
assert.Equal(t, 200.0, susan)
|
||
|
}
|
||
|
|
||
|
func TestSyncCondCompatibility(t *testing.T) {
|
||
|
var wg sync.WaitGroup
|
||
|
wg.Add(2)
|
||
|
cond := sync.NewCond(NewMultilock("A", "C"))
|
||
|
|
||
|
sharedRsc := "foo"
|
||
|
|
||
|
go func() {
|
||
|
cond.L.Lock()
|
||
|
for sharedRsc == "foo" {
|
||
|
cond.Wait()
|
||
|
}
|
||
|
sharedRsc = "fizz!"
|
||
|
cond.Broadcast()
|
||
|
cond.L.Unlock()
|
||
|
wg.Done()
|
||
|
}()
|
||
|
|
||
|
go func() {
|
||
|
cond.L.Lock()
|
||
|
sharedRsc = "bar"
|
||
|
cond.Broadcast()
|
||
|
for sharedRsc == "bar" {
|
||
|
cond.Wait()
|
||
|
}
|
||
|
cond.L.Unlock()
|
||
|
wg.Done()
|
||
|
}()
|
||
|
|
||
|
wg.Wait()
|
||
|
assert.Equal(t, "fizz!", sharedRsc)
|
||
|
}
|