@ -54,7 +54,7 @@ func TestClientSyncTree(t *testing.T) {
wantSeq = uint ( 1 )
wantSeq = uint ( 1 )
)
)
c , _ := NewClient ( Config { Resolver : r , Logger : testlog . Logger ( t , log . LvlTrace ) } )
c := NewClient ( Config { Resolver : r , Logger : testlog . Logger ( t , log . LvlTrace ) } )
stree , err := c . SyncTree ( "enrtree://AKPYQIUQIL7PSIACI32J7FGZW56E5FKHEFCCOFHILBIMW3M6LWXS2@n" )
stree , err := c . SyncTree ( "enrtree://AKPYQIUQIL7PSIACI32J7FGZW56E5FKHEFCCOFHILBIMW3M6LWXS2@n" )
if err != nil {
if err != nil {
t . Fatal ( "sync error:" , err )
t . Fatal ( "sync error:" , err )
@ -68,9 +68,6 @@ func TestClientSyncTree(t *testing.T) {
if stree . Seq ( ) != wantSeq {
if stree . Seq ( ) != wantSeq {
t . Errorf ( "synced tree has wrong seq: %d" , stree . Seq ( ) )
t . Errorf ( "synced tree has wrong seq: %d" , stree . Seq ( ) )
}
}
if len ( c . trees ) > 0 {
t . Errorf ( "tree from SyncTree added to client" )
}
}
}
// In this test, syncing the tree fails because it contains an invalid ENR entry.
// In this test, syncing the tree fails because it contains an invalid ENR entry.
@ -91,7 +88,7 @@ func TestClientSyncTreeBadNode(t *testing.T) {
"C7HRFPF3BLGF3YR4DY5KX3SMBE.n" : "enrtree://AM5FCQLWIZX2QFPNJAP7VUERCCRNGRHWZG3YYHIUV7BVDQ5FDPRT2@morenodes.example.org" ,
"C7HRFPF3BLGF3YR4DY5KX3SMBE.n" : "enrtree://AM5FCQLWIZX2QFPNJAP7VUERCCRNGRHWZG3YYHIUV7BVDQ5FDPRT2@morenodes.example.org" ,
"INDMVBZEEQ4ESVYAKGIYU74EAA.n" : "enr:-----" ,
"INDMVBZEEQ4ESVYAKGIYU74EAA.n" : "enr:-----" ,
}
}
c , _ := NewClient ( Config { Resolver : r , Logger : testlog . Logger ( t , log . LvlTrace ) } )
c := NewClient ( Config { Resolver : r , Logger : testlog . Logger ( t , log . LvlTrace ) } )
_ , err := c . SyncTree ( "enrtree://AKPYQIUQIL7PSIACI32J7FGZW56E5FKHEFCCOFHILBIMW3M6LWXS2@n" )
_ , err := c . SyncTree ( "enrtree://AKPYQIUQIL7PSIACI32J7FGZW56E5FKHEFCCOFHILBIMW3M6LWXS2@n" )
wantErr := nameError { name : "INDMVBZEEQ4ESVYAKGIYU74EAA.n" , err : entryError { typ : "enr" , err : errInvalidENR } }
wantErr := nameError { name : "INDMVBZEEQ4ESVYAKGIYU74EAA.n" , err : entryError { typ : "enr" , err : errInvalidENR } }
if err != wantErr {
if err != wantErr {
@ -99,57 +96,89 @@ func TestClientSyncTreeBadNode(t *testing.T) {
}
}
}
}
// This test checks that RandomNode hit s all entries.
// This test checks that randomIterator find s all entries.
func TestClientRandomNode ( t * testing . T ) {
func TestIterator ( t * testing . T ) {
nodes := testNodes ( nodesSeed1 , 30 )
nodes := testNodes ( nodesSeed1 , 30 )
tree , url := makeTestTree ( "n" , nodes , nil )
tree , url := makeTestTree ( "n" , nodes , nil )
r := mapResolver ( tree . ToTXT ( "n" ) )
r := mapResolver ( tree . ToTXT ( "n" ) )
c , _ := NewClient ( Config { Resolver : r , Logger : testlog . Logger ( t , log . LvlTrace ) } )
c := NewClient ( Config {
if err := c . AddTree ( url ) ; err != nil {
Resolver : r ,
Logger : testlog . Logger ( t , log . LvlTrace ) ,
RateLimit : 500 ,
} )
it , err := c . NewIterator ( url )
if err != nil {
t . Fatal ( err )
t . Fatal ( err )
}
}
checkRandomNode ( t , c , nodes )
checkIterator ( t , it , nodes )
}
// This test checks if closing randomIterator races.
func TestIteratorClose ( t * testing . T ) {
nodes := testNodes ( nodesSeed1 , 500 )
tree1 , url1 := makeTestTree ( "t1" , nodes , nil )
c := NewClient ( Config { Resolver : newMapResolver ( tree1 . ToTXT ( "t1" ) ) } )
it , err := c . NewIterator ( url1 )
if err != nil {
t . Fatal ( err )
}
done := make ( chan struct { } )
go func ( ) {
for it . Next ( ) {
_ = it . Node ( )
}
close ( done )
} ( )
time . Sleep ( 50 * time . Millisecond )
it . Close ( )
<- done
}
}
// This test checks that RandomNode traverses linked trees as well as explicitly added trees.
// This test checks that randomIterator traverses linked trees as well as explicitly added trees.
func TestClientRandomNodeLinks ( t * testing . T ) {
func TestIterator Links ( t * testing . T ) {
nodes := testNodes ( nodesSeed1 , 40 )
nodes := testNodes ( nodesSeed1 , 40 )
tree1 , url1 := makeTestTree ( "t1" , nodes [ : 10 ] , nil )
tree1 , url1 := makeTestTree ( "t1" , nodes [ : 10 ] , nil )
tree2 , url2 := makeTestTree ( "t2" , nodes [ 10 : ] , [ ] string { url1 } )
tree2 , url2 := makeTestTree ( "t2" , nodes [ 10 : ] , [ ] string { url1 } )
cfg := Config {
c := NewClient ( Config {
Resolver : newMapResolver ( tree1 . ToTXT ( "t1" ) , tree2 . ToTXT ( "t2" ) ) ,
Resolver : newMapResolver ( tree1 . ToTXT ( "t1" ) , tree2 . ToTXT ( "t2" ) ) ,
Logger : testlog . Logger ( t , log . LvlTrace ) ,
Logger : testlog . Logger ( t , log . LvlTrace ) ,
}
RateLimit : 500 ,
c , _ := NewClient ( cfg )
} )
if err := c . AddTree ( url2 ) ; err != nil {
it , err := c . NewIterator ( url2 )
if err != nil {
t . Fatal ( err )
t . Fatal ( err )
}
}
checkRandomNode ( t , c , nodes )
checkIterator ( t , it , nodes )
}
}
// This test verifies that RandomNode re-checks the root of the tree to catch
// This test verifies that randomIterator re-checks the root of the tree to catch
// updates to nodes.
// updates to nodes.
func TestClientRandomNodeUpdates ( t * testing . T ) {
func TestIterator NodeUpdates ( t * testing . T ) {
var (
var (
clock = new ( mclock . Simulated )
clock = new ( mclock . Simulated )
nodes = testNodes ( nodesSeed1 , 30 )
nodes = testNodes ( nodesSeed1 , 30 )
resolver = newMapResolver ( )
resolver = newMapResolver ( )
cfg = Config {
c = NewClient ( Config {
Resolver : resolver ,
Resolver : resolver ,
Logger : testlog . Logger ( t , log . LvlTrace ) ,
Logger : testlog . Logger ( t , log . LvlTrace ) ,
RecheckInterval : 20 * time . Minute ,
RecheckInterval : 20 * time . Minute ,
}
RateLimit : 500 ,
c , _ = NewClient ( cfg )
} )
)
)
c . clock = clock
c . clock = clock
tree1 , url := makeTestTree ( "n" , nodes [ : 25 ] , nil )
tree1 , url := makeTestTree ( "n" , nodes [ : 25 ] , nil )
it , err := c . NewIterator ( url )
if err != nil {
t . Fatal ( err )
}
// Sync the original tree.
// s ync the original tree.
resolver . add ( tree1 . ToTXT ( "n" ) )
resolver . add ( tree1 . ToTXT ( "n" ) )
c . AddTree ( url )
checkIterator ( t , it , nodes [ : 25 ] )
checkRandomNode ( t , c , nodes [ : 25 ] )
// Update some nodes and ensure RandomNode returns the new nodes as well.
// Update some nodes and ensure RandomNode returns the new nodes as well.
keys := testKeys ( nodesSeed1 , len ( nodes ) )
keys := testKeys ( nodesSeed1 , len ( nodes ) )
@ -162,25 +191,25 @@ func TestClientRandomNodeUpdates(t *testing.T) {
nodes [ i ] = n2
nodes [ i ] = n2
}
}
tree2 , _ := makeTestTree ( "n" , nodes , nil )
tree2 , _ := makeTestTree ( "n" , nodes , nil )
clock . Run ( cfg . RecheckInterval + 1 * time . Second )
clock . Run ( c . c fg. RecheckInterval + 1 * time . Second )
resolver . clear ( )
resolver . clear ( )
resolver . add ( tree2 . ToTXT ( "n" ) )
resolver . add ( tree2 . ToTXT ( "n" ) )
checkRandomNode ( t , c , nodes )
checkIterator ( t , it , nodes )
}
}
// This test verifies that RandomNode re-checks the root of the tree to catch
// This test verifies that randomIterator re-checks the root of the tree to catch
// updates to links.
// updates to links.
func TestClientRandomNode LinkUpdates ( t * testing . T ) {
func TestIterator LinkUpdates ( t * testing . T ) {
var (
var (
clock = new ( mclock . Simulated )
clock = new ( mclock . Simulated )
nodes = testNodes ( nodesSeed1 , 30 )
nodes = testNodes ( nodesSeed1 , 30 )
resolver = newMapResolver ( )
resolver = newMapResolver ( )
cfg = Config {
c = NewClient ( Config {
Resolver : resolver ,
Resolver : resolver ,
Logger : testlog . Logger ( t , log . LvlTrace ) ,
Logger : testlog . Logger ( t , log . LvlTrace ) ,
RecheckInterval : 20 * time . Minute ,
RecheckInterval : 20 * time . Minute ,
}
RateLimit : 500 ,
c , _ = NewClient ( cfg )
} )
)
)
c . clock = clock
c . clock = clock
tree3 , url3 := makeTestTree ( "t3" , nodes [ 20 : 30 ] , nil )
tree3 , url3 := makeTestTree ( "t3" , nodes [ 20 : 30 ] , nil )
@ -190,49 +219,53 @@ func TestClientRandomNodeLinkUpdates(t *testing.T) {
resolver . add ( tree2 . ToTXT ( "t2" ) )
resolver . add ( tree2 . ToTXT ( "t2" ) )
resolver . add ( tree3 . ToTXT ( "t3" ) )
resolver . add ( tree3 . ToTXT ( "t3" ) )
it , err := c . NewIterator ( url1 )
if err != nil {
t . Fatal ( err )
}
// Sync tree1 using RandomNode.
// Sync tree1 using RandomNode.
c . AddTree ( url1 )
checkIterator ( t , it , nodes [ : 20 ] )
checkRandomNode ( t , c , nodes [ : 20 ] )
// Add link to tree3, remove link to tree2.
// Add link to tree3, remove link to tree2.
tree1 , _ = makeTestTree ( "t1" , nodes [ : 10 ] , [ ] string { url3 } )
tree1 , _ = makeTestTree ( "t1" , nodes [ : 10 ] , [ ] string { url3 } )
resolver . add ( tree1 . ToTXT ( "t1" ) )
resolver . add ( tree1 . ToTXT ( "t1" ) )
clock . Run ( cfg . RecheckInterval + 1 * time . Second )
clock . Run ( c . c fg. RecheckInterval + 1 * time . Second )
t . Log ( "tree1 updated" )
t . Log ( "tree1 updated" )
var wantNodes [ ] * enode . Node
var wantNodes [ ] * enode . Node
wantNodes = append ( wantNodes , tree1 . Nodes ( ) ... )
wantNodes = append ( wantNodes , tree1 . Nodes ( ) ... )
wantNodes = append ( wantNodes , tree3 . Nodes ( ) ... )
wantNodes = append ( wantNodes , tree3 . Nodes ( ) ... )
checkRandomNode ( t , c , wantNodes )
checkIterator ( t , it , wantNodes )
// Check that linked trees are GCed when they're no longer referenced.
// Check that linked trees are GCed when they're no longer referenced.
if len ( c . trees ) != 2 {
knownTrees := it . ( * randomIterator ) . trees
t . Errorf ( "client knows %d trees, want 2" , len ( c . trees ) )
if len ( knownTrees ) != 2 {
t . Errorf ( "client knows %d trees, want 2" , len ( knownTrees ) )
}
}
}
}
func checkRandomNode ( t * testing . T , c * Client , wantNodes [ ] * enode . Node ) {
func checkIterator ( t * testing . T , it enode . Iterator , wantNodes [ ] * enode . Node ) {
t . Helper ( )
t . Helper ( )
var (
var (
want = make ( map [ enode . ID ] * enode . Node )
want = make ( map [ enode . ID ] * enode . Node )
maxCalls = len ( wantNodes ) * 2
maxCalls = len ( wantNodes ) * 3
calls = 0
calls = 0
ctx = context . Background ( )
)
)
for _ , n := range wantNodes {
for _ , n := range wantNodes {
want [ n . ID ( ) ] = n
want [ n . ID ( ) ] = n
}
}
for ; len ( want ) > 0 && calls < maxCalls ; calls ++ {
for ; len ( want ) > 0 && calls < maxCalls ; calls ++ {
n := c . RandomNode ( ctx )
if ! it . Next ( ) {
if n == nil {
t . Fatalf ( "Next returned false (call %d)" , calls )
t . Fatalf ( "RandomNode returned nil (call %d)" , calls )
}
}
n := it . Node ( )
delete ( want , n . ID ( ) )
delete ( want , n . ID ( ) )
}
}
t . Logf ( "checkRandomNode called RandomNode %d times to find %d nodes" , calls , len ( wantNodes ) )
t . Logf ( "checkIterator called Next %d times to find %d nodes" , calls , len ( wantNodes ) )
for _ , n := range want {
for _ , n := range want {
t . Errorf ( "RandomNode didn't discover node %v" , n . ID ( ) )
t . Errorf ( "iterator didn't discover node %v" , n . ID ( ) )
}
}
}
}