aboutsummaryrefslogtreecommitdiff
path: root/stm_test.go
diff options
context:
space:
mode:
authorMatt Joiner <anacrolix@gmail.com>2019-11-06 16:14:47 +1100
committerMatt Joiner <anacrolix@gmail.com>2019-11-06 16:14:47 +1100
commite749ba3531cf430b66e1d3f310f53ea2972e3aa3 (patch)
tree328dbb9b2078908a8967483a0829ac4acb54cc9a /stm_test.go
parentAdd WouldBlock (diff)
downloadstm-e749ba3531cf430b66e1d3f310f53ea2972e3aa3.tar.gz
stm-e749ba3531cf430b66e1d3f310f53ea2972e3aa3.tar.xz
Make returns explicit
Diffstat (limited to 'stm_test.go')
-rw-r--r--stm_test.go92
1 files changed, 43 insertions, 49 deletions
diff --git a/stm_test.go b/stm_test.go
index aeb0907..ffb4011 100644
--- a/stm_test.go
+++ b/stm_test.go
@@ -6,21 +6,22 @@ import (
"time"
_ "github.com/anacrolix/envpprof"
+ "github.com/stretchr/testify/assert"
)
func TestDecrement(t *testing.T) {
x := NewVar(1000)
for i := 0; i < 500; i++ {
- go Atomically(func(tx *Tx) {
+ go Atomically(VoidOperation(func(tx *Tx) {
cur := tx.Get(x).(int)
tx.Set(x, cur-1)
- })
+ }))
}
done := make(chan struct{})
go func() {
- Atomically(func(tx *Tx) {
+ Atomically(VoidOperation(func(tx *Tx) {
tx.Assert(tx.Get(x) == 500)
- })
+ }))
close(done)
}()
select {
@@ -47,12 +48,12 @@ func TestReadVerify(t *testing.T) {
// spawn a transaction that reads x, then y. The other tx will modify x in
// between the reads, causing this tx to retry.
var x2, y2 int
- Atomically(func(tx *Tx) {
+ Atomically(VoidOperation(func(tx *Tx) {
x2 = tx.Get(x).(int)
read <- struct{}{}
<-read // wait for other tx to complete
y2 = tx.Get(y).(int)
- })
+ }))
if x2 == 1 && y2 == 2 {
t.Fatal("read was not verified")
}
@@ -65,22 +66,22 @@ func TestRetry(t *testing.T) {
go func() {
for i := 0; i < 10; i++ {
time.Sleep(10 * time.Millisecond)
- Atomically(func(tx *Tx) {
+ Atomically(VoidOperation(func(tx *Tx) {
cur := tx.Get(x).(int)
tx.Set(x, cur-1)
- })
+ }))
}
}()
// Each time we read x before the above loop has finished, we need to
// retry. This should result in no more than 1 retry per transaction.
retry := 0
- Atomically(func(tx *Tx) {
+ Atomically(VoidOperation(func(tx *Tx) {
cur := tx.Get(x).(int)
if cur != 0 {
retry++
tx.Retry()
}
- })
+ }))
if retry > 10 {
t.Fatal("should have retried at most 10 times, got", retry)
}
@@ -96,12 +97,12 @@ func TestVerify(t *testing.T) {
// spawn a transaction that modifies x
go func() {
- Atomically(func(tx *Tx) {
+ Atomically(VoidOperation(func(tx *Tx) {
<-read
rx := tx.Get(x).(*foo)
rx.i = 7
tx.Set(x, rx)
- })
+ }))
read <- struct{}{}
// other tx should retry, so we need to read/send again
read <- <-read
@@ -110,12 +111,12 @@ func TestVerify(t *testing.T) {
// spawn a transaction that reads x, then y. The other tx will modify x in
// between the reads, causing this tx to retry.
var i int
- Atomically(func(tx *Tx) {
+ Atomically(VoidOperation(func(tx *Tx) {
f := tx.Get(x).(*foo)
i = f.i
read <- struct{}{}
<-read // wait for other tx to complete
- })
+ }))
if i == 3 {
t.Fatal("verify did not retry despite modified Var", i)
}
@@ -136,37 +137,33 @@ func TestSelect(t *testing.T) {
// with one arg, Select adds no effect
x := NewVar(2)
- Atomically(Select(func(tx *Tx) {
+ Atomically(Select(VoidOperation(func(tx *Tx) {
tx.Assert(tx.Get(x).(int) == 2)
- }))
+ })))
- var picked int
- Atomically(Select(
+ picked := Atomically(Select(
// always blocks; should never be selected
- func(tx *Tx) {
+ VoidOperation(func(tx *Tx) {
tx.Retry()
- picked = 1
- },
+ }),
// always succeeds; should always be selected
- func(tx *Tx) {
- picked = 2
+ func(tx *Tx) interface{} {
+ return 2
},
// always succeeds; should never be selected
- func(tx *Tx) {
- picked = 3
+ func(tx *Tx) interface{} {
+ return 3
},
- ))
- if picked != 2 {
- t.Fatal("Select selected wrong transaction:", picked)
- }
+ )).(int)
+ assert.EqualValues(t, 2, picked)
}
func TestCompose(t *testing.T) {
nums := make([]int, 100)
- fns := make([]func(*Tx), 100)
+ fns := make([]Operation, 100)
for i := range fns {
- fns[i] = func(x int) func(*Tx) {
- return func(*Tx) { nums[x] = x }
+ fns[i] = func(x int) Operation {
+ return VoidOperation(func(*Tx) { nums[x] = x })
}(i) // capture loop var
}
Atomically(Compose(fns...))
@@ -178,14 +175,11 @@ func TestCompose(t *testing.T) {
}
func TestPanic(t *testing.T) {
- defer func() {
- if recover() == nil {
- t.Fatal("expected panic, got nil")
- }
- }()
// normal panics should escape Atomically
- Atomically(func(*Tx) {
- panic("foo")
+ assert.PanicsWithValue(t, "foo", func() {
+ Atomically(func(*Tx) interface{} {
+ panic("foo")
+ })
})
}
@@ -193,10 +187,10 @@ func TestReadWritten(t *testing.T) {
// reading a variable written in the same transaction should return the
// previously written value
x := NewVar(3)
- Atomically(func(tx *Tx) {
+ Atomically(VoidOperation(func(tx *Tx) {
tx.Set(x, 5)
tx.Assert(tx.Get(x).(int) == 5)
- })
+ }))
}
func TestAtomicSetRetry(t *testing.T) {
@@ -204,9 +198,9 @@ func TestAtomicSetRetry(t *testing.T) {
x := NewVar(3)
done := make(chan struct{})
go func() {
- Atomically(func(tx *Tx) {
+ Atomically(VoidOperation(func(tx *Tx) {
tx.Assert(tx.Get(x).(int) == 5)
- })
+ }))
done <- struct{}{}
}()
time.Sleep(10 * time.Millisecond)
@@ -226,18 +220,18 @@ func testPingPong(t testing.TB, n int, afterHit func(string)) {
var wg sync.WaitGroup
bat := func(from, to interface{}, noise string) {
defer wg.Done()
- for !Atomically(func(tx *Tx) {
+ for !Atomically(func(tx *Tx) interface{} {
if tx.Get(doneVar).(bool) {
- tx.Return(true)
+ return true
}
tx.Assert(tx.Get(ready).(bool))
if tx.Get(ball) == from {
tx.Set(ball, to)
tx.Set(hits, tx.Get(hits).(int)+1)
tx.Set(ready, false)
- tx.Return(false)
+ return false
}
- tx.Retry()
+ panic(Retry)
}).(bool) {
afterHit(noise)
AtomicSet(ready, true)
@@ -246,10 +240,10 @@ func testPingPong(t testing.TB, n int, afterHit func(string)) {
wg.Add(2)
go bat(false, true, "ping!")
go bat(true, false, "pong!")
- Atomically(func(tx *Tx) {
+ Atomically(VoidOperation(func(tx *Tx) {
tx.Assert(tx.Get(hits).(int) >= n)
tx.Set(doneVar, true)
- })
+ }))
wg.Wait()
}