218 lines
4.0 KiB
Go
218 lines
4.0 KiB
Go
package pool_test
|
|
|
|
import (
|
|
"bytes"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/lightningnetwork/lnd/buffer"
|
|
"github.com/lightningnetwork/lnd/pool"
|
|
)
|
|
|
|
type mockRecycler bool
|
|
|
|
func (m *mockRecycler) Recycle() {
|
|
*m = false
|
|
}
|
|
|
|
// TestRecyclers verifies that known recyclable types properly return to their
|
|
// zero-value after invoking Recycle.
|
|
func TestRecyclers(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
newItem func() interface{}
|
|
}{
|
|
{
|
|
"mock recycler",
|
|
func() interface{} { return new(mockRecycler) },
|
|
},
|
|
{
|
|
"write_buffer",
|
|
func() interface{} { return new(buffer.Write) },
|
|
},
|
|
{
|
|
"read_buffer",
|
|
func() interface{} { return new(buffer.Read) },
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
// Initialize the Recycler to test.
|
|
r := test.newItem().(pool.Recycler)
|
|
|
|
// Dirty the item.
|
|
dirtyGeneric(t, r)
|
|
|
|
// Invoke Recycle to clear the item.
|
|
r.Recycle()
|
|
|
|
// Assert the item is now clean.
|
|
isCleanGeneric(t, r)
|
|
})
|
|
}
|
|
}
|
|
|
|
type recyclePoolTest struct {
|
|
name string
|
|
newPool func() interface{}
|
|
}
|
|
|
|
// TestGenericRecyclePoolTests generically tests that pools derived from the
|
|
// base Recycle pool properly are properly configured.
|
|
func TestConcreteRecyclePoolTests(t *testing.T) {
|
|
const (
|
|
gcInterval = time.Second
|
|
expiryInterval = 250 * time.Millisecond
|
|
)
|
|
|
|
tests := []recyclePoolTest{
|
|
{
|
|
name: "write buffer pool",
|
|
newPool: func() interface{} {
|
|
return pool.NewWriteBuffer(
|
|
gcInterval, expiryInterval,
|
|
)
|
|
},
|
|
},
|
|
{
|
|
name: "read buffer pool",
|
|
newPool: func() interface{} {
|
|
return pool.NewReadBuffer(
|
|
gcInterval, expiryInterval,
|
|
)
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
testRecyclePool(t, test)
|
|
})
|
|
}
|
|
}
|
|
|
|
func testRecyclePool(t *testing.T, test recyclePoolTest) {
|
|
p := test.newPool()
|
|
|
|
// Take an item from the pool.
|
|
r1 := takeGeneric(t, p)
|
|
|
|
// Dirty the item.
|
|
dirtyGeneric(t, r1)
|
|
|
|
// Return the item to the pool.
|
|
returnGeneric(t, p, r1)
|
|
|
|
// Take items from the pool until we find the original. We expect at
|
|
// most two, in the event that a fresh item is populated after the
|
|
// first is taken.
|
|
for i := 0; i < 2; i++ {
|
|
// Wait a small duration to ensure the tests are reliable, and
|
|
// don't to active the non-blocking case unintentionally.
|
|
<-time.After(time.Millisecond)
|
|
|
|
r2 := takeGeneric(t, p)
|
|
|
|
// Take an item, skipping those whose pointer does not match the
|
|
// one we dirtied.
|
|
if r1 != r2 {
|
|
continue
|
|
}
|
|
|
|
// Finally, verify that the item has been properly cleaned.
|
|
isCleanGeneric(t, r2)
|
|
|
|
return
|
|
}
|
|
|
|
t.Fatalf("original item not found")
|
|
}
|
|
|
|
func takeGeneric(t *testing.T, p interface{}) pool.Recycler {
|
|
t.Helper()
|
|
|
|
switch pp := p.(type) {
|
|
case *pool.WriteBuffer:
|
|
return pp.Take()
|
|
|
|
case *pool.ReadBuffer:
|
|
return pp.Take()
|
|
|
|
default:
|
|
t.Fatalf("unknown pool type: %T", p)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func returnGeneric(t *testing.T, p, item interface{}) {
|
|
t.Helper()
|
|
|
|
switch pp := p.(type) {
|
|
case *pool.WriteBuffer:
|
|
pp.Return(item.(*buffer.Write))
|
|
|
|
case *pool.ReadBuffer:
|
|
pp.Return(item.(*buffer.Read))
|
|
|
|
default:
|
|
t.Fatalf("unknown pool type: %T", p)
|
|
}
|
|
}
|
|
|
|
func dirtyGeneric(t *testing.T, i interface{}) {
|
|
t.Helper()
|
|
|
|
switch item := i.(type) {
|
|
case *mockRecycler:
|
|
*item = true
|
|
|
|
case *buffer.Write:
|
|
dirtySlice(item[:])
|
|
|
|
case *buffer.Read:
|
|
dirtySlice(item[:])
|
|
|
|
default:
|
|
t.Fatalf("unknown item type: %T", i)
|
|
}
|
|
|
|
}
|
|
|
|
func dirtySlice(slice []byte) {
|
|
for i := range slice {
|
|
slice[i] = 0xff
|
|
}
|
|
}
|
|
|
|
func isCleanGeneric(t *testing.T, i interface{}) {
|
|
t.Helper()
|
|
|
|
switch item := i.(type) {
|
|
case *mockRecycler:
|
|
if isDirty := *item; isDirty {
|
|
t.Fatalf("mock recycler still diry")
|
|
}
|
|
|
|
case *buffer.Write:
|
|
isCleanSlice(t, item[:])
|
|
|
|
case *buffer.Read:
|
|
isCleanSlice(t, item[:])
|
|
|
|
default:
|
|
t.Fatalf("unknown item type: %T", i)
|
|
}
|
|
}
|
|
|
|
func isCleanSlice(t *testing.T, slice []byte) {
|
|
t.Helper()
|
|
|
|
expSlice := make([]byte, len(slice))
|
|
if !bytes.Equal(expSlice, slice) {
|
|
t.Fatalf("slice not recycled, want: %v, got: %v",
|
|
expSlice, slice)
|
|
}
|
|
}
|