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