mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-02-22 14:22:37 +01:00
autopilot/choice_test: add chooseN tests
This commit is contained in:
parent
a202860ce1
commit
3d2a39a18c
1 changed files with 196 additions and 0 deletions
196
autopilot/choice_test.go
Normal file
196
autopilot/choice_test.go
Normal file
|
@ -0,0 +1,196 @@
|
|||
package autopilot
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"testing"
|
||||
"testing/quick"
|
||||
)
|
||||
|
||||
var (
|
||||
nID1 = NodeID([33]byte{1})
|
||||
nID2 = NodeID([33]byte{2})
|
||||
nID3 = NodeID([33]byte{3})
|
||||
nID4 = NodeID([33]byte{4})
|
||||
)
|
||||
|
||||
// TestChooseNEmptyMap checks that chooseN returns an empty result when no
|
||||
// nodes are chosen among.
|
||||
func TestChooseNEmptyMap(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
nodes := map[NodeID]*AttachmentDirective{}
|
||||
property := func(n uint32) bool {
|
||||
res, err := chooseN(n, nodes)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Result should always be empty.
|
||||
return len(res) == 0
|
||||
}
|
||||
|
||||
if err := quick.Check(property, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// candidateMapVarLen is a type we'll use to generate maps of various lengths
|
||||
// up to 255 to be used during QuickTests.
|
||||
type candidateMapVarLen map[NodeID]*AttachmentDirective
|
||||
|
||||
// Generate generates a value of type candidateMapVarLen to be used during
|
||||
// QuickTests.
|
||||
func (candidateMapVarLen) Generate(rand *rand.Rand, size int) reflect.Value {
|
||||
nodes := make(map[NodeID]*AttachmentDirective)
|
||||
|
||||
// To avoid creating huge maps, we restrict them to max uint8 len.
|
||||
n := uint8(rand.Uint32())
|
||||
|
||||
for i := uint8(0); i < n; i++ {
|
||||
s := rand.Float64()
|
||||
|
||||
// We set small values to zero, to ensure we handle these
|
||||
// correctly.
|
||||
if s < 0.01 {
|
||||
s = 0
|
||||
}
|
||||
|
||||
var nID [33]byte
|
||||
binary.BigEndian.PutUint32(nID[:], uint32(i))
|
||||
nodes[nID] = &AttachmentDirective{
|
||||
Score: s,
|
||||
}
|
||||
}
|
||||
|
||||
return reflect.ValueOf(nodes)
|
||||
}
|
||||
|
||||
// TestChooseNMinimum test that chooseN returns the minimum of the number of
|
||||
// nodes we request and the number of positively scored nodes in the given map.
|
||||
func TestChooseNMinimum(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Helper to count the number of positive scores in the given map.
|
||||
numPositive := func(nodes map[NodeID]*AttachmentDirective) int {
|
||||
cnt := 0
|
||||
for _, v := range nodes {
|
||||
if v.Score > 0 {
|
||||
cnt++
|
||||
}
|
||||
}
|
||||
return cnt
|
||||
}
|
||||
|
||||
// We use let the type of n be uint8 to avoid generating huge numbers.
|
||||
property := func(nodes candidateMapVarLen, n uint8) bool {
|
||||
res, err := chooseN(uint32(n), nodes)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
positive := numPositive(nodes)
|
||||
|
||||
// Result should always be the minimum of the number of nodes
|
||||
// we wanted to select and the number of positively scored
|
||||
// nodes in the map.
|
||||
min := positive
|
||||
if int(n) < min {
|
||||
min = int(n)
|
||||
}
|
||||
|
||||
if len(res) != min {
|
||||
return false
|
||||
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if err := quick.Check(property, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestChooseNSample sanity checks that nodes are picked by chooseN according
|
||||
// to their scores.
|
||||
func TestChooseNSample(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const numNodes = 500
|
||||
const maxIterations = 100000
|
||||
fifth := uint32(numNodes / 5)
|
||||
|
||||
nodes := make(map[NodeID]*AttachmentDirective)
|
||||
|
||||
// we make 5 buckets of nodes: 0, 0.1, 0.2, 0.4 and 0.8 score. We want
|
||||
// to check that zero scores never gets chosen, while a doubling the
|
||||
// score makes a node getting chosen about double the amount (this is
|
||||
// true only when n <<< numNodes).
|
||||
j := 2 * fifth
|
||||
score := 0.1
|
||||
for i := uint32(0); i < numNodes; i++ {
|
||||
|
||||
// Each time i surpasses j we double the score we give to the
|
||||
// next fifth of nodes.
|
||||
if i >= j {
|
||||
score *= 2
|
||||
j += fifth
|
||||
}
|
||||
s := score
|
||||
|
||||
// The first 1/5 of nodes we give a score of 0.
|
||||
if i < fifth {
|
||||
s = 0
|
||||
}
|
||||
|
||||
var nID [33]byte
|
||||
binary.BigEndian.PutUint32(nID[:], i)
|
||||
nodes[nID] = &AttachmentDirective{
|
||||
Score: s,
|
||||
}
|
||||
}
|
||||
|
||||
// For each value of N we'll check that the nodes are picked the
|
||||
// expected number of times over time.
|
||||
for _, n := range []uint32{1, 5, 10, 20, 50} {
|
||||
// Since choosing more nodes will result in chooseN getting
|
||||
// slower we decrease the number of iterations. This is okay
|
||||
// since the variance in the total picks for a node will be
|
||||
// lower when choosing more nodes each time.
|
||||
iterations := maxIterations / n
|
||||
count := make(map[NodeID]int)
|
||||
for i := 0; i < int(iterations); i++ {
|
||||
res, err := chooseN(n, nodes)
|
||||
if err != nil {
|
||||
t.Fatalf("failed choosing nodes: %v", err)
|
||||
}
|
||||
|
||||
for nID := range res {
|
||||
count[nID]++
|
||||
}
|
||||
}
|
||||
|
||||
// Sum the number of times a node in each score bucket was
|
||||
// picked.
|
||||
sums := make(map[float64]int)
|
||||
for nID, s := range nodes {
|
||||
sums[s.Score] += count[nID]
|
||||
}
|
||||
|
||||
// The count of each bucket should be about double of the
|
||||
// previous bucket. Since this is all random, we check that
|
||||
// the result is within 20% of the expected value.
|
||||
for _, score := range []float64{0.2, 0.4, 0.8} {
|
||||
cnt := sums[score]
|
||||
half := cnt / 2
|
||||
expLow := half - half/5
|
||||
expHigh := half + half/5
|
||||
if sums[score/2] < expLow || sums[score/2] > expHigh {
|
||||
t.Fatalf("expected the nodes with score %v "+
|
||||
"to be chosen about %v times, instead "+
|
||||
"was %v", score/2, half, sums[score/2])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue