github.com/theQRL/go-zond@v0.1.1/p2p/simulations/network_test.go (about) 1 // Copyright 2017 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package simulations 18 19 import ( 20 "bytes" 21 "context" 22 "encoding/json" 23 "fmt" 24 "reflect" 25 "strconv" 26 "strings" 27 "testing" 28 "time" 29 30 "github.com/theQRL/go-zond/log" 31 "github.com/theQRL/go-zond/node" 32 "github.com/theQRL/go-zond/p2p/enode" 33 "github.com/theQRL/go-zond/p2p/simulations/adapters" 34 ) 35 36 // Tests that a created snapshot with a minimal service only contains the expected connections 37 // and that a network when loaded with this snapshot only contains those same connections 38 func TestSnapshot(t *testing.T) { 39 // PART I 40 // create snapshot from ring network 41 42 // this is a minimal service, whose protocol will take exactly one message OR close of connection before quitting 43 adapter := adapters.NewSimAdapter(adapters.LifecycleConstructors{ 44 "noopwoop": func(ctx *adapters.ServiceContext, stack *node.Node) (node.Lifecycle, error) { 45 return NewNoopService(nil), nil 46 }, 47 }) 48 49 // create network 50 network := NewNetwork(adapter, &NetworkConfig{ 51 DefaultService: "noopwoop", 52 }) 53 // \todo consider making a member of network, set to true threadsafe when shutdown 54 runningOne := true 55 defer func() { 56 if runningOne { 57 network.Shutdown() 58 } 59 }() 60 61 // create and start nodes 62 nodeCount := 20 63 ids := make([]enode.ID, nodeCount) 64 for i := 0; i < nodeCount; i++ { 65 conf := adapters.RandomNodeConfig() 66 node, err := network.NewNodeWithConfig(conf) 67 if err != nil { 68 t.Fatalf("error creating node: %s", err) 69 } 70 if err := network.Start(node.ID()); err != nil { 71 t.Fatalf("error starting node: %s", err) 72 } 73 ids[i] = node.ID() 74 } 75 76 // subscribe to peer events 77 evC := make(chan *Event) 78 sub := network.Events().Subscribe(evC) 79 defer sub.Unsubscribe() 80 81 // connect nodes in a ring 82 // spawn separate thread to avoid deadlock in the event listeners 83 connectErr := make(chan error, 1) 84 go func() { 85 for i, id := range ids { 86 peerID := ids[(i+1)%len(ids)] 87 if err := network.Connect(id, peerID); err != nil { 88 connectErr <- err 89 return 90 } 91 } 92 }() 93 94 // collect connection events up to expected number 95 ctx, cancel := context.WithTimeout(context.TODO(), time.Second) 96 defer cancel() 97 checkIds := make(map[enode.ID][]enode.ID) 98 connEventCount := nodeCount 99 OUTER: 100 for { 101 select { 102 case <-ctx.Done(): 103 t.Fatal(ctx.Err()) 104 case err := <-connectErr: 105 t.Fatal(err) 106 case ev := <-evC: 107 if ev.Type == EventTypeConn && !ev.Control { 108 // fail on any disconnect 109 if !ev.Conn.Up { 110 t.Fatalf("unexpected disconnect: %v -> %v", ev.Conn.One, ev.Conn.Other) 111 } 112 checkIds[ev.Conn.One] = append(checkIds[ev.Conn.One], ev.Conn.Other) 113 checkIds[ev.Conn.Other] = append(checkIds[ev.Conn.Other], ev.Conn.One) 114 connEventCount-- 115 log.Debug("ev", "count", connEventCount) 116 if connEventCount == 0 { 117 break OUTER 118 } 119 } 120 } 121 } 122 123 // create snapshot of current network 124 snap, err := network.Snapshot() 125 if err != nil { 126 t.Fatal(err) 127 } 128 j, err := json.Marshal(snap) 129 if err != nil { 130 t.Fatal(err) 131 } 132 log.Debug("snapshot taken", "nodes", len(snap.Nodes), "conns", len(snap.Conns), "json", string(j)) 133 134 // verify that the snap element numbers check out 135 if len(checkIds) != len(snap.Conns) || len(checkIds) != len(snap.Nodes) { 136 t.Fatalf("snapshot wrong node,conn counts %d,%d != %d", len(snap.Nodes), len(snap.Conns), len(checkIds)) 137 } 138 139 // shut down sim network 140 runningOne = false 141 sub.Unsubscribe() 142 network.Shutdown() 143 144 // check that we have all the expected connections in the snapshot 145 for nodid, nodConns := range checkIds { 146 for _, nodConn := range nodConns { 147 var match bool 148 for _, snapConn := range snap.Conns { 149 if snapConn.One == nodid && snapConn.Other == nodConn { 150 match = true 151 break 152 } else if snapConn.Other == nodid && snapConn.One == nodConn { 153 match = true 154 break 155 } 156 } 157 if !match { 158 t.Fatalf("snapshot missing conn %v -> %v", nodid, nodConn) 159 } 160 } 161 } 162 log.Info("snapshot checked") 163 164 // PART II 165 // load snapshot and verify that exactly same connections are formed 166 167 adapter = adapters.NewSimAdapter(adapters.LifecycleConstructors{ 168 "noopwoop": func(ctx *adapters.ServiceContext, stack *node.Node) (node.Lifecycle, error) { 169 return NewNoopService(nil), nil 170 }, 171 }) 172 network = NewNetwork(adapter, &NetworkConfig{ 173 DefaultService: "noopwoop", 174 }) 175 defer func() { 176 network.Shutdown() 177 }() 178 179 // subscribe to peer events 180 // every node up and conn up event will generate one additional control event 181 // therefore multiply the count by two 182 evC = make(chan *Event, (len(snap.Conns)*2)+(len(snap.Nodes)*2)) 183 sub = network.Events().Subscribe(evC) 184 defer sub.Unsubscribe() 185 186 // load the snapshot 187 // spawn separate thread to avoid deadlock in the event listeners 188 err = network.Load(snap) 189 if err != nil { 190 t.Fatal(err) 191 } 192 193 // collect connection events up to expected number 194 ctx, cancel = context.WithTimeout(context.TODO(), time.Second*3) 195 defer cancel() 196 197 connEventCount = nodeCount 198 199 OuterTwo: 200 for { 201 select { 202 case <-ctx.Done(): 203 t.Fatal(ctx.Err()) 204 case ev := <-evC: 205 if ev.Type == EventTypeConn && !ev.Control { 206 // fail on any disconnect 207 if !ev.Conn.Up { 208 t.Fatalf("unexpected disconnect: %v -> %v", ev.Conn.One, ev.Conn.Other) 209 } 210 log.Debug("conn", "on", ev.Conn.One, "other", ev.Conn.Other) 211 checkIds[ev.Conn.One] = append(checkIds[ev.Conn.One], ev.Conn.Other) 212 checkIds[ev.Conn.Other] = append(checkIds[ev.Conn.Other], ev.Conn.One) 213 connEventCount-- 214 log.Debug("ev", "count", connEventCount) 215 if connEventCount == 0 { 216 break OuterTwo 217 } 218 } 219 } 220 } 221 222 // check that we have all expected connections in the network 223 for _, snapConn := range snap.Conns { 224 var match bool 225 for nodid, nodConns := range checkIds { 226 for _, nodConn := range nodConns { 227 if snapConn.One == nodid && snapConn.Other == nodConn { 228 match = true 229 break 230 } else if snapConn.Other == nodid && snapConn.One == nodConn { 231 match = true 232 break 233 } 234 } 235 } 236 if !match { 237 t.Fatalf("network missing conn %v -> %v", snapConn.One, snapConn.Other) 238 } 239 } 240 241 // verify that network didn't generate any other additional connection events after the ones we have collected within a reasonable period of time 242 ctx, cancel = context.WithTimeout(context.TODO(), time.Second) 243 defer cancel() 244 select { 245 case <-ctx.Done(): 246 case ev := <-evC: 247 if ev.Type == EventTypeConn { 248 t.Fatalf("Superfluous conn found %v -> %v", ev.Conn.One, ev.Conn.Other) 249 } 250 } 251 252 // This test validates if all connections from the snapshot 253 // are created in the network. 254 t.Run("conns after load", func(t *testing.T) { 255 // Create new network. 256 n := NewNetwork( 257 adapters.NewSimAdapter(adapters.LifecycleConstructors{ 258 "noopwoop": func(ctx *adapters.ServiceContext, stack *node.Node) (node.Lifecycle, error) { 259 return NewNoopService(nil), nil 260 }, 261 }), 262 &NetworkConfig{ 263 DefaultService: "noopwoop", 264 }, 265 ) 266 defer n.Shutdown() 267 268 // Load the same snapshot. 269 err := n.Load(snap) 270 if err != nil { 271 t.Fatal(err) 272 } 273 274 // Check every connection from the snapshot 275 // if it is in the network, too. 276 for _, c := range snap.Conns { 277 if n.GetConn(c.One, c.Other) == nil { 278 t.Errorf("missing connection: %s -> %s", c.One, c.Other) 279 } 280 } 281 }) 282 } 283 284 // TestNetworkSimulation creates a multi-node simulation network with each node 285 // connected in a ring topology, checks that all nodes successfully handshake 286 // with each other and that a snapshot fully represents the desired topology 287 func TestNetworkSimulation(t *testing.T) { 288 // create simulation network with 20 testService nodes 289 adapter := adapters.NewSimAdapter(adapters.LifecycleConstructors{ 290 "test": newTestService, 291 }) 292 network := NewNetwork(adapter, &NetworkConfig{ 293 DefaultService: "test", 294 }) 295 defer network.Shutdown() 296 nodeCount := 20 297 ids := make([]enode.ID, nodeCount) 298 for i := 0; i < nodeCount; i++ { 299 conf := adapters.RandomNodeConfig() 300 node, err := network.NewNodeWithConfig(conf) 301 if err != nil { 302 t.Fatalf("error creating node: %s", err) 303 } 304 if err := network.Start(node.ID()); err != nil { 305 t.Fatalf("error starting node: %s", err) 306 } 307 ids[i] = node.ID() 308 } 309 310 // perform a check which connects the nodes in a ring (so each node is 311 // connected to exactly two peers) and then checks that all nodes 312 // performed two handshakes by checking their peerCount 313 action := func(_ context.Context) error { 314 for i, id := range ids { 315 peerID := ids[(i+1)%len(ids)] 316 if err := network.Connect(id, peerID); err != nil { 317 return err 318 } 319 } 320 return nil 321 } 322 check := func(ctx context.Context, id enode.ID) (bool, error) { 323 // check we haven't run out of time 324 select { 325 case <-ctx.Done(): 326 return false, ctx.Err() 327 default: 328 } 329 330 // get the node 331 node := network.GetNode(id) 332 if node == nil { 333 return false, fmt.Errorf("unknown node: %s", id) 334 } 335 336 // check it has exactly two peers 337 client, err := node.Client() 338 if err != nil { 339 return false, err 340 } 341 var peerCount int64 342 if err := client.CallContext(ctx, &peerCount, "test_peerCount"); err != nil { 343 return false, err 344 } 345 switch { 346 case peerCount < 2: 347 return false, nil 348 case peerCount == 2: 349 return true, nil 350 default: 351 return false, fmt.Errorf("unexpected peerCount: %d", peerCount) 352 } 353 } 354 355 timeout := 30 * time.Second 356 ctx, cancel := context.WithTimeout(context.Background(), timeout) 357 defer cancel() 358 359 // trigger a check every 100ms 360 trigger := make(chan enode.ID) 361 go triggerChecks(ctx, ids, trigger, 100*time.Millisecond) 362 363 result := NewSimulation(network).Run(ctx, &Step{ 364 Action: action, 365 Trigger: trigger, 366 Expect: &Expectation{ 367 Nodes: ids, 368 Check: check, 369 }, 370 }) 371 if result.Error != nil { 372 t.Fatalf("simulation failed: %s", result.Error) 373 } 374 375 // take a network snapshot and check it contains the correct topology 376 snap, err := network.Snapshot() 377 if err != nil { 378 t.Fatal(err) 379 } 380 if len(snap.Nodes) != nodeCount { 381 t.Fatalf("expected snapshot to contain %d nodes, got %d", nodeCount, len(snap.Nodes)) 382 } 383 if len(snap.Conns) != nodeCount { 384 t.Fatalf("expected snapshot to contain %d connections, got %d", nodeCount, len(snap.Conns)) 385 } 386 for i, id := range ids { 387 conn := snap.Conns[i] 388 if conn.One != id { 389 t.Fatalf("expected conn[%d].One to be %s, got %s", i, id, conn.One) 390 } 391 peerID := ids[(i+1)%len(ids)] 392 if conn.Other != peerID { 393 t.Fatalf("expected conn[%d].Other to be %s, got %s", i, peerID, conn.Other) 394 } 395 } 396 } 397 398 func createTestNodes(count int, network *Network) (nodes []*Node, err error) { 399 for i := 0; i < count; i++ { 400 nodeConf := adapters.RandomNodeConfig() 401 node, err := network.NewNodeWithConfig(nodeConf) 402 if err != nil { 403 return nil, err 404 } 405 if err := network.Start(node.ID()); err != nil { 406 return nil, err 407 } 408 409 nodes = append(nodes, node) 410 } 411 412 return nodes, nil 413 } 414 415 func createTestNodesWithProperty(property string, count int, network *Network) (propertyNodes []*Node, err error) { 416 for i := 0; i < count; i++ { 417 nodeConf := adapters.RandomNodeConfig() 418 nodeConf.Properties = append(nodeConf.Properties, property) 419 420 node, err := network.NewNodeWithConfig(nodeConf) 421 if err != nil { 422 return nil, err 423 } 424 if err := network.Start(node.ID()); err != nil { 425 return nil, err 426 } 427 428 propertyNodes = append(propertyNodes, node) 429 } 430 431 return propertyNodes, nil 432 } 433 434 // TestGetNodeIDs creates a set of nodes and attempts to retrieve their IDs,. 435 // It then tests again whilst excluding a node ID from being returned. 436 // If a node ID is not returned, or more node IDs than expected are returned, the test fails. 437 func TestGetNodeIDs(t *testing.T) { 438 adapter := adapters.NewSimAdapter(adapters.LifecycleConstructors{ 439 "test": newTestService, 440 }) 441 network := NewNetwork(adapter, &NetworkConfig{ 442 DefaultService: "test", 443 }) 444 defer network.Shutdown() 445 446 numNodes := 5 447 nodes, err := createTestNodes(numNodes, network) 448 if err != nil { 449 t.Fatalf("Could not create test nodes %v", err) 450 } 451 452 gotNodeIDs := network.GetNodeIDs() 453 if len(gotNodeIDs) != numNodes { 454 t.Fatalf("Expected %d nodes, got %d", numNodes, len(gotNodeIDs)) 455 } 456 457 for _, node1 := range nodes { 458 match := false 459 for _, node2ID := range gotNodeIDs { 460 if bytes.Equal(node1.ID().Bytes(), node2ID.Bytes()) { 461 match = true 462 break 463 } 464 } 465 466 if !match { 467 t.Fatalf("A created node was not returned by GetNodes(), ID: %s", node1.ID().String()) 468 } 469 } 470 471 excludeNodeID := nodes[3].ID() 472 gotNodeIDsExcl := network.GetNodeIDs(excludeNodeID) 473 if len(gotNodeIDsExcl) != numNodes-1 { 474 t.Fatalf("Expected one less node ID to be returned") 475 } 476 for _, nodeID := range gotNodeIDsExcl { 477 if bytes.Equal(excludeNodeID.Bytes(), nodeID.Bytes()) { 478 t.Fatalf("GetNodeIDs returned the node ID we excluded, ID: %s", nodeID.String()) 479 } 480 } 481 } 482 483 // TestGetNodes creates a set of nodes and attempts to retrieve them again. 484 // It then tests again whilst excluding a node from being returned. 485 // If a node is not returned, or more nodes than expected are returned, the test fails. 486 func TestGetNodes(t *testing.T) { 487 adapter := adapters.NewSimAdapter(adapters.LifecycleConstructors{ 488 "test": newTestService, 489 }) 490 network := NewNetwork(adapter, &NetworkConfig{ 491 DefaultService: "test", 492 }) 493 defer network.Shutdown() 494 495 numNodes := 5 496 nodes, err := createTestNodes(numNodes, network) 497 if err != nil { 498 t.Fatalf("Could not create test nodes %v", err) 499 } 500 501 gotNodes := network.GetNodes() 502 if len(gotNodes) != numNodes { 503 t.Fatalf("Expected %d nodes, got %d", numNodes, len(gotNodes)) 504 } 505 506 for _, node1 := range nodes { 507 match := false 508 for _, node2 := range gotNodes { 509 if bytes.Equal(node1.ID().Bytes(), node2.ID().Bytes()) { 510 match = true 511 break 512 } 513 } 514 515 if !match { 516 t.Fatalf("A created node was not returned by GetNodes(), ID: %s", node1.ID().String()) 517 } 518 } 519 520 excludeNodeID := nodes[3].ID() 521 gotNodesExcl := network.GetNodes(excludeNodeID) 522 if len(gotNodesExcl) != numNodes-1 { 523 t.Fatalf("Expected one less node to be returned") 524 } 525 for _, node := range gotNodesExcl { 526 if bytes.Equal(excludeNodeID.Bytes(), node.ID().Bytes()) { 527 t.Fatalf("GetNodes returned the node we excluded, ID: %s", node.ID().String()) 528 } 529 } 530 } 531 532 // TestGetNodesByID creates a set of nodes and attempts to retrieve a subset of them by ID 533 // If a node is not returned, or more nodes than expected are returned, the test fails. 534 func TestGetNodesByID(t *testing.T) { 535 adapter := adapters.NewSimAdapter(adapters.LifecycleConstructors{ 536 "test": newTestService, 537 }) 538 network := NewNetwork(adapter, &NetworkConfig{ 539 DefaultService: "test", 540 }) 541 defer network.Shutdown() 542 543 numNodes := 5 544 nodes, err := createTestNodes(numNodes, network) 545 if err != nil { 546 t.Fatalf("Could not create test nodes: %v", err) 547 } 548 549 numSubsetNodes := 2 550 subsetNodes := nodes[0:numSubsetNodes] 551 var subsetNodeIDs []enode.ID 552 for _, node := range subsetNodes { 553 subsetNodeIDs = append(subsetNodeIDs, node.ID()) 554 } 555 556 gotNodesByID := network.GetNodesByID(subsetNodeIDs) 557 if len(gotNodesByID) != numSubsetNodes { 558 t.Fatalf("Expected %d nodes, got %d", numSubsetNodes, len(gotNodesByID)) 559 } 560 561 for _, node1 := range subsetNodes { 562 match := false 563 for _, node2 := range gotNodesByID { 564 if bytes.Equal(node1.ID().Bytes(), node2.ID().Bytes()) { 565 match = true 566 break 567 } 568 } 569 570 if !match { 571 t.Fatalf("A created node was not returned by GetNodesByID(), ID: %s", node1.ID().String()) 572 } 573 } 574 } 575 576 // TestGetNodesByProperty creates a subset of nodes with a property assigned. 577 // GetNodesByProperty is then checked for correctness by comparing the nodes returned to those initially created. 578 // If a node with a property is not found, or more nodes than expected are returned, the test fails. 579 func TestGetNodesByProperty(t *testing.T) { 580 adapter := adapters.NewSimAdapter(adapters.LifecycleConstructors{ 581 "test": newTestService, 582 }) 583 network := NewNetwork(adapter, &NetworkConfig{ 584 DefaultService: "test", 585 }) 586 defer network.Shutdown() 587 588 numNodes := 3 589 _, err := createTestNodes(numNodes, network) 590 if err != nil { 591 t.Fatalf("Failed to create nodes: %v", err) 592 } 593 594 numPropertyNodes := 3 595 propertyTest := "test" 596 propertyNodes, err := createTestNodesWithProperty(propertyTest, numPropertyNodes, network) 597 if err != nil { 598 t.Fatalf("Failed to create nodes with property: %v", err) 599 } 600 601 gotNodesByProperty := network.GetNodesByProperty(propertyTest) 602 if len(gotNodesByProperty) != numPropertyNodes { 603 t.Fatalf("Expected %d nodes with a property, got %d", numPropertyNodes, len(gotNodesByProperty)) 604 } 605 606 for _, node1 := range propertyNodes { 607 match := false 608 for _, node2 := range gotNodesByProperty { 609 if bytes.Equal(node1.ID().Bytes(), node2.ID().Bytes()) { 610 match = true 611 break 612 } 613 } 614 615 if !match { 616 t.Fatalf("A created node with property was not returned by GetNodesByProperty(), ID: %s", node1.ID().String()) 617 } 618 } 619 } 620 621 // TestGetNodeIDsByProperty creates a subset of nodes with a property assigned. 622 // GetNodeIDsByProperty is then checked for correctness by comparing the node IDs returned to those initially created. 623 // If a node ID with a property is not found, or more nodes IDs than expected are returned, the test fails. 624 func TestGetNodeIDsByProperty(t *testing.T) { 625 adapter := adapters.NewSimAdapter(adapters.LifecycleConstructors{ 626 "test": newTestService, 627 }) 628 network := NewNetwork(adapter, &NetworkConfig{ 629 DefaultService: "test", 630 }) 631 defer network.Shutdown() 632 633 numNodes := 3 634 _, err := createTestNodes(numNodes, network) 635 if err != nil { 636 t.Fatalf("Failed to create nodes: %v", err) 637 } 638 639 numPropertyNodes := 3 640 propertyTest := "test" 641 propertyNodes, err := createTestNodesWithProperty(propertyTest, numPropertyNodes, network) 642 if err != nil { 643 t.Fatalf("Failed to created nodes with property: %v", err) 644 } 645 646 gotNodeIDsByProperty := network.GetNodeIDsByProperty(propertyTest) 647 if len(gotNodeIDsByProperty) != numPropertyNodes { 648 t.Fatalf("Expected %d nodes with a property, got %d", numPropertyNodes, len(gotNodeIDsByProperty)) 649 } 650 651 for _, node1 := range propertyNodes { 652 match := false 653 id1 := node1.ID() 654 for _, id2 := range gotNodeIDsByProperty { 655 if bytes.Equal(id1.Bytes(), id2.Bytes()) { 656 match = true 657 break 658 } 659 } 660 661 if !match { 662 t.Fatalf("Not all nodes IDs were returned by GetNodeIDsByProperty(), ID: %s", id1.String()) 663 } 664 } 665 } 666 667 func triggerChecks(ctx context.Context, ids []enode.ID, trigger chan enode.ID, interval time.Duration) { 668 tick := time.NewTicker(interval) 669 defer tick.Stop() 670 for { 671 select { 672 case <-tick.C: 673 for _, id := range ids { 674 select { 675 case trigger <- id: 676 case <-ctx.Done(): 677 return 678 } 679 } 680 case <-ctx.Done(): 681 return 682 } 683 } 684 } 685 686 // \todo: refactor to implement shapshots 687 // and connect configuration methods once these are moved from 688 // swarm/network/simulations/connect.go 689 func BenchmarkMinimalService(b *testing.B) { 690 b.Run("ring/32", benchmarkMinimalServiceTmp) 691 } 692 693 func benchmarkMinimalServiceTmp(b *testing.B) { 694 // stop timer to discard setup time pollution 695 args := strings.Split(b.Name(), "/") 696 nodeCount, err := strconv.ParseInt(args[2], 10, 16) 697 if err != nil { 698 b.Fatal(err) 699 } 700 701 for i := 0; i < b.N; i++ { 702 // this is a minimal service, whose protocol will close a channel upon run of protocol 703 // making it possible to bench the time it takes for the service to start and protocol actually to be run 704 protoCMap := make(map[enode.ID]map[enode.ID]chan struct{}) 705 adapter := adapters.NewSimAdapter(adapters.LifecycleConstructors{ 706 "noopwoop": func(ctx *adapters.ServiceContext, stack *node.Node) (node.Lifecycle, error) { 707 protoCMap[ctx.Config.ID] = make(map[enode.ID]chan struct{}) 708 svc := NewNoopService(protoCMap[ctx.Config.ID]) 709 return svc, nil 710 }, 711 }) 712 713 // create network 714 network := NewNetwork(adapter, &NetworkConfig{ 715 DefaultService: "noopwoop", 716 }) 717 defer network.Shutdown() 718 719 // create and start nodes 720 ids := make([]enode.ID, nodeCount) 721 for i := 0; i < int(nodeCount); i++ { 722 conf := adapters.RandomNodeConfig() 723 node, err := network.NewNodeWithConfig(conf) 724 if err != nil { 725 b.Fatalf("error creating node: %s", err) 726 } 727 if err := network.Start(node.ID()); err != nil { 728 b.Fatalf("error starting node: %s", err) 729 } 730 ids[i] = node.ID() 731 } 732 733 // ready, set, go 734 b.ResetTimer() 735 736 // connect nodes in a ring 737 for i, id := range ids { 738 peerID := ids[(i+1)%len(ids)] 739 if err := network.Connect(id, peerID); err != nil { 740 b.Fatal(err) 741 } 742 } 743 744 // wait for all protocols to signal to close down 745 ctx, cancel := context.WithTimeout(context.TODO(), time.Second) 746 defer cancel() 747 for nodid, peers := range protoCMap { 748 for peerid, peerC := range peers { 749 log.Debug("getting ", "node", nodid, "peer", peerid) 750 select { 751 case <-ctx.Done(): 752 b.Fatal(ctx.Err()) 753 case <-peerC: 754 } 755 } 756 } 757 } 758 } 759 760 func TestNode_UnmarshalJSON(t *testing.T) { 761 t.Run("up_field", func(t *testing.T) { 762 runNodeUnmarshalJSON(t, casesNodeUnmarshalJSONUpField()) 763 }) 764 t.Run("config_field", func(t *testing.T) { 765 runNodeUnmarshalJSON(t, casesNodeUnmarshalJSONConfigField()) 766 }) 767 } 768 769 func runNodeUnmarshalJSON(t *testing.T, tests []nodeUnmarshalTestCase) { 770 t.Helper() 771 for _, tt := range tests { 772 t.Run(tt.name, func(t *testing.T) { 773 var got *Node 774 if err := json.Unmarshal([]byte(tt.marshaled), &got); err != nil { 775 expectErrorMessageToContain(t, err, tt.wantErr) 776 got = nil 777 } 778 expectNodeEquality(t, got, tt.want) 779 }) 780 } 781 } 782 783 type nodeUnmarshalTestCase struct { 784 name string 785 marshaled string 786 want *Node 787 wantErr string 788 } 789 790 func expectErrorMessageToContain(t *testing.T, got error, want string) { 791 t.Helper() 792 if got == nil && want == "" { 793 return 794 } 795 796 if got == nil && want != "" { 797 t.Errorf("error was expected, got: nil, want: %v", want) 798 return 799 } 800 801 if !strings.Contains(got.Error(), want) { 802 t.Errorf( 803 "unexpected error message, got %v, want: %v", 804 want, 805 got, 806 ) 807 } 808 } 809 810 func expectNodeEquality(t *testing.T, got, want *Node) { 811 t.Helper() 812 if !reflect.DeepEqual(got, want) { 813 t.Errorf("Node.UnmarshalJSON() = %v, want %v", got, want) 814 } 815 } 816 817 func casesNodeUnmarshalJSONUpField() []nodeUnmarshalTestCase { 818 return []nodeUnmarshalTestCase{ 819 { 820 name: "empty json", 821 marshaled: "{}", 822 want: newNode(nil, nil, false), 823 }, 824 { 825 name: "a stopped node", 826 marshaled: "{\"up\": false}", 827 want: newNode(nil, nil, false), 828 }, 829 { 830 name: "a running node", 831 marshaled: "{\"up\": true}", 832 want: newNode(nil, nil, true), 833 }, 834 { 835 name: "invalid JSON value on valid key", 836 marshaled: "{\"up\": foo}", 837 wantErr: "invalid character", 838 }, 839 { 840 name: "invalid JSON key and value", 841 marshaled: "{foo: bar}", 842 wantErr: "invalid character", 843 }, 844 { 845 name: "bool value expected but got something else (string)", 846 marshaled: "{\"up\": \"true\"}", 847 wantErr: "cannot unmarshal string into Go struct", 848 }, 849 } 850 } 851 852 func casesNodeUnmarshalJSONConfigField() []nodeUnmarshalTestCase { 853 // Don't do a big fuss around testing, as adapters.NodeConfig should 854 // handle it's own serialization. Just do a sanity check. 855 return []nodeUnmarshalTestCase{ 856 { 857 name: "Config field is omitted", 858 marshaled: "{}", 859 want: newNode(nil, nil, false), 860 }, 861 { 862 name: "Config field is nil", 863 marshaled: "{\"config\": null}", 864 want: newNode(nil, nil, false), 865 }, 866 { 867 name: "a non default Config field", 868 marshaled: "{\"config\":{\"name\":\"node_ecdd0\",\"port\":44665}}", 869 want: newNode(nil, &adapters.NodeConfig{Name: "node_ecdd0", Port: 44665}, false), 870 }, 871 } 872 }