github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/p2p/simulations/network_test.go (about) 1 // Copyright 2017 The Spectrum Authors 2 // This file is part of the Spectrum library. 3 // 4 // The Spectrum 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 Spectrum 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 Spectrum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package simulations 18 19 import ( 20 "context" 21 "fmt" 22 "testing" 23 "time" 24 25 "github.com/SmartMeshFoundation/Spectrum/p2p/discover" 26 "github.com/SmartMeshFoundation/Spectrum/p2p/simulations/adapters" 27 ) 28 29 // TestNetworkSimulation creates a multi-node simulation network with each node 30 // connected in a ring topology, checks that all nodes successfully handshake 31 // with each other and that a snapshot fully represents the desired topology 32 func TestNetworkSimulation(t *testing.T) { 33 // create simulation network with 20 testService nodes 34 adapter := adapters.NewSimAdapter(adapters.Services{ 35 "test": newTestService, 36 }) 37 network := NewNetwork(adapter, &NetworkConfig{ 38 DefaultService: "test", 39 }) 40 defer network.Shutdown() 41 nodeCount := 20 42 ids := make([]discover.NodeID, nodeCount) 43 for i := 0; i < nodeCount; i++ { 44 node, err := network.NewNode() 45 if err != nil { 46 t.Fatalf("error creating node: %s", err) 47 } 48 if err := network.Start(node.ID()); err != nil { 49 t.Fatalf("error starting node: %s", err) 50 } 51 ids[i] = node.ID() 52 } 53 54 // perform a check which connects the nodes in a ring (so each node is 55 // connected to exactly two peers) and then checks that all nodes 56 // performed two handshakes by checking their peerCount 57 action := func(_ context.Context) error { 58 for i, id := range ids { 59 peerID := ids[(i+1)%len(ids)] 60 if err := network.Connect(id, peerID); err != nil { 61 return err 62 } 63 } 64 return nil 65 } 66 check := func(ctx context.Context, id discover.NodeID) (bool, error) { 67 // check we haven't run out of time 68 select { 69 case <-ctx.Done(): 70 return false, ctx.Err() 71 default: 72 } 73 74 // get the node 75 node := network.GetNode(id) 76 if node == nil { 77 return false, fmt.Errorf("unknown node: %s", id) 78 } 79 80 // check it has exactly two peers 81 client, err := node.Client() 82 if err != nil { 83 return false, err 84 } 85 var peerCount int64 86 if err := client.CallContext(ctx, &peerCount, "test_peerCount"); err != nil { 87 return false, err 88 } 89 switch { 90 case peerCount < 2: 91 return false, nil 92 case peerCount == 2: 93 return true, nil 94 default: 95 return false, fmt.Errorf("unexpected peerCount: %d", peerCount) 96 } 97 } 98 99 timeout := 30 * time.Second 100 ctx, cancel := context.WithTimeout(context.Background(), timeout) 101 defer cancel() 102 103 // trigger a check every 100ms 104 trigger := make(chan discover.NodeID) 105 go triggerChecks(ctx, ids, trigger, 100*time.Millisecond) 106 107 result := NewSimulation(network).Run(ctx, &Step{ 108 Action: action, 109 Trigger: trigger, 110 Expect: &Expectation{ 111 Nodes: ids, 112 Check: check, 113 }, 114 }) 115 if result.Error != nil { 116 t.Fatalf("simulation failed: %s", result.Error) 117 } 118 119 // take a network snapshot and check it contains the correct topology 120 snap, err := network.Snapshot() 121 if err != nil { 122 t.Fatal(err) 123 } 124 if len(snap.Nodes) != nodeCount { 125 t.Fatalf("expected snapshot to contain %d nodes, got %d", nodeCount, len(snap.Nodes)) 126 } 127 if len(snap.Conns) != nodeCount { 128 t.Fatalf("expected snapshot to contain %d connections, got %d", nodeCount, len(snap.Conns)) 129 } 130 for i, id := range ids { 131 conn := snap.Conns[i] 132 if conn.One != id { 133 t.Fatalf("expected conn[%d].One to be %s, got %s", i, id, conn.One) 134 } 135 peerID := ids[(i+1)%len(ids)] 136 if conn.Other != peerID { 137 t.Fatalf("expected conn[%d].Other to be %s, got %s", i, peerID, conn.Other) 138 } 139 } 140 } 141 142 func triggerChecks(ctx context.Context, ids []discover.NodeID, trigger chan discover.NodeID, interval time.Duration) { 143 tick := time.NewTicker(interval) 144 defer tick.Stop() 145 for { 146 select { 147 case <-tick.C: 148 for _, id := range ids { 149 select { 150 case trigger <- id: 151 case <-ctx.Done(): 152 return 153 } 154 } 155 case <-ctx.Done(): 156 return 157 } 158 } 159 }