Skip to content

Commit

Permalink
p2p: improve test TestTable_findnodeByID (#4047)
Browse files Browse the repository at this point in the history
* refactor test
* add a fast fixed examples test for the main suite
* split slow test for the integration suite
  • Loading branch information
battlmonstr authored May 3, 2022
1 parent cf44803 commit 2e4947b
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 51 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ GO_FLAGS += -ldflags "-X ${PACKAGE}/params.GitCommit=${GIT_COMMIT} -X ${PACKAGE}

GOBUILD = $(CGO_CFLAGS) $(GO) build $(GO_FLAGS)
GO_DBG_BUILD = $(DBG_CGO_CFLAGS) $(GO) build $(GO_FLAGS) -tags $(BUILD_TAGS),debug -gcflags=all="-N -l" # see delve docs
GOTEST = GODEBUG=cgocheck=0 $(GO) test $(GO_FLAGS) ./... -p 2
GOTEST = GODEBUG=cgocheck=0 $(GO) test $(GO_FLAGS) ./... -p 2 -tags $(BUILD_TAGS),integration

default: all

Expand Down
28 changes: 28 additions & 0 deletions p2p/discover/table_integration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//go:build integration
// +build integration

package discover

import (
"math/rand"
"testing"
"testing/quick"
"time"
)

func TestTable_findNodeByID_quickCheck(t *testing.T) {
t.Parallel()

config := quick.Config{
MaxCount: 1000,
Rand: rand.New(rand.NewSource(time.Now().Unix())),
}

test := func(nodesCount uint16, resultsCount byte) bool {
return testTableFindNodeByIDRun(t, nodesCount, resultsCount, config.Rand)
}

if err := quick.Check(test, &config); err != nil {
t.Error(err)
}
}
125 changes: 75 additions & 50 deletions p2p/discover/table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,31 +189,35 @@ func checkIPLimitInvariant(t *testing.T, tab *Table) {
}
}

func TestTable_findnodeByID(t *testing.T) {
t.Parallel()

test := func(test *closeTest) bool {
func testTableFindNodeByIDRun(t *testing.T, nodesCountGen uint16, resultsCountGen byte, rand *rand.Rand) bool {
if !t.Skipped() {
// for any node table, Target and N
transport := newPingRecorder()
tab, db := newTestTable(transport)
defer db.Close()
defer tab.close()
fillTable(tab, test.All)

nodesCount := int(nodesCountGen) % (bucketSize*nBuckets + 1)
testNodes := generateNodes(rand, nodesCount)
fillTable(tab, testNodes)

target := enode.ID{}
resultsCount := int(resultsCountGen) % (bucketSize + 1)

// check that closest(Target, N) returns nodes
result := tab.findnodeByID(test.Target, test.N, false).entries
result := tab.findnodeByID(target, resultsCount, false).entries
if hasDuplicates(result) {
t.Errorf("result contains duplicates")
return false
}
if !sortedByDistanceTo(test.Target, result) {
if !sortedByDistanceTo(target, result) {
t.Errorf("result is not sorted by distance to target")
return false
}

// check that the number of results is min(N, tablen)
wantN := test.N
if tlen := tab.len(); tlen < test.N {
wantN := resultsCount
if tlen := tab.len(); tlen < resultsCount {
wantN = tlen
}
if len(result) != wantN {
Expand All @@ -230,9 +234,9 @@ func TestTable_findnodeByID(t *testing.T) {
continue // don't run the check below for nodes in result
}
farthestResult := result[len(result)-1].ID()
if enode.DistCmp(test.Target, n.ID(), farthestResult) < 0 {
if enode.DistCmp(target, n.ID(), farthestResult) < 0 {
t.Errorf("table contains node that is closer to target but it's not in result")
t.Logf(" Target: %v", test.Target)
t.Logf(" Target: %v", target)
t.Logf(" Farthest Result: %v", farthestResult)
t.Logf(" ID: %v", n.ID())
return false
Expand All @@ -241,9 +245,50 @@ func TestTable_findnodeByID(t *testing.T) {
}
return true
}
if err := quick.Check(test, quickcfg()); err != nil {
t.Error(err)
}
return true
}

func TestTable_findNodeByID_examples(t *testing.T) {
t.Parallel()

randGen := rand.New(rand.NewSource(time.Now().Unix()))

t.Run("n0r1", func(t *testing.T) {
testTableFindNodeByIDRun(t, 0, 1, randGen)
})
t.Run("n1r1", func(t *testing.T) {
testTableFindNodeByIDRun(t, 1, 1, randGen)
})
t.Run("n16r1", func(t *testing.T) {
testTableFindNodeByIDRun(t, bucketSize, 1, randGen)
})
t.Run("nMr1", func(t *testing.T) {
testTableFindNodeByIDRun(t, uint16(bucketSize*nBuckets), 1, randGen)
})
t.Run("n0r2", func(t *testing.T) {
testTableFindNodeByIDRun(t, 0, 2, randGen)
})
t.Run("n1r2", func(t *testing.T) {
testTableFindNodeByIDRun(t, 1, 2, randGen)
})
t.Run("n16r2", func(t *testing.T) {
testTableFindNodeByIDRun(t, bucketSize, 2, randGen)
})
t.Run("nMr2", func(t *testing.T) {
testTableFindNodeByIDRun(t, uint16(bucketSize*nBuckets), 2, randGen)
})
t.Run("n0rM", func(t *testing.T) {
testTableFindNodeByIDRun(t, 0, bucketSize, randGen)
})
t.Run("n1rM", func(t *testing.T) {
testTableFindNodeByIDRun(t, 1, bucketSize, randGen)
})
t.Run("n16rM", func(t *testing.T) {
testTableFindNodeByIDRun(t, bucketSize, bucketSize, randGen)
})
t.Run("nMrM", func(t *testing.T) {
testTableFindNodeByIDRun(t, uint16(bucketSize*nBuckets), bucketSize, randGen)
})
}

func TestTable_ReadRandomNodesGetAll(t *testing.T) {
Expand Down Expand Up @@ -281,27 +326,24 @@ func TestTable_ReadRandomNodesGetAll(t *testing.T) {
}
}

type closeTest struct {
Self enode.ID
Target enode.ID
All []*node
N int
func generateNodes(rand *rand.Rand, count int) []*node {
nodes := make([]*node, 0, count)
for i := 0; i < count; i++ {
nodes = append(nodes, generateNode(rand))
}
return nodes
}

func (*closeTest) Generate(rand *rand.Rand, size int) reflect.Value {
t := &closeTest{
Self: gen(enode.ID{}, rand).(enode.ID),
Target: gen(enode.ID{}, rand).(enode.ID),
N: rand.Intn(bucketSize),
}
for _, id := range gen([]enode.ID{}, rand).([]enode.ID) {
r := new(enr.Record)
r.Set(enr.IP(genIP(rand)))
n := wrapNode(enode.SignNull(r, id))
n.livenessChecks = 1
t.All = append(t.All, n)
}
return reflect.ValueOf(t)
func generateNode(rand *rand.Rand) *node {
var id enode.ID
rand.Read(id[:])

r := new(enr.Record)
r.Set(enr.IP(genIP(rand)))

n := wrapNode(enode.SignNull(r, id))
n.livenessChecks = 1
return n
}

func TestTable_addVerifiedNode(t *testing.T) {
Expand Down Expand Up @@ -395,29 +437,12 @@ func TestTable_revalidateSyncRecord(t *testing.T) {
}
}

// gen wraps quick.Value so it's easier to use.
// it generates a random value of the given value's type.
func gen(typ interface{}, rand *rand.Rand) interface{} {
v, ok := quick.Value(reflect.TypeOf(typ), rand)
if !ok {
panic(fmt.Sprintf("couldn't generate random value of type %T", typ))
}
return v.Interface()
}

func genIP(rand *rand.Rand) net.IP {
ip := make(net.IP, 4)
rand.Read(ip)
return ip
}

func quickcfg() *quick.Config {
return &quick.Config{
MaxCount: 1000,
Rand: rand.New(rand.NewSource(time.Now().Unix())),
}
}

func newkey() *ecdsa.PrivateKey {
key, err := crypto.GenerateKey()
if err != nil {
Expand Down

0 comments on commit 2e4947b

Please sign in to comment.