github.com/aaa256/atlantis@v0.0.0-20210707112435-42ee889287a2/swarm/network/stream/testing/testing.go (about) 1 // Copyright 2018 The go-athereum Authors 2 // This file is part of the go-athereum library. 3 // 4 // The go-athereum 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-athereum 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-athereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package testing 18 19 import ( 20 "context" 21 "errors" 22 "fmt" 23 "io/ioutil" 24 "math/rand" 25 "os" 26 "sync" 27 "testing" 28 "time" 29 30 "github.com/athereum/go-athereum/p2p" 31 "github.com/athereum/go-athereum/p2p/discover" 32 "github.com/athereum/go-athereum/p2p/simulations" 33 "github.com/athereum/go-athereum/p2p/simulations/adapters" 34 "github.com/athereum/go-athereum/rpc" 35 "github.com/athereum/go-athereum/swarm/log" 36 "github.com/athereum/go-athereum/swarm/network" 37 "github.com/athereum/go-athereum/swarm/storage" 38 ) 39 40 type Simulation struct { 41 Net *simulations.Network 42 Stores []storage.ChunkStore 43 Addrs []network.Addr 44 IDs []discover.NodeID 45 } 46 47 func SetStores(addrs ...network.Addr) ([]storage.ChunkStore, func(), error) { 48 var datadirs []string 49 stores := make([]storage.ChunkStore, len(addrs)) 50 var err error 51 for i, addr := range addrs { 52 var datadir string 53 datadir, err = ioutil.TempDir("", "streamer") 54 if err != nil { 55 break 56 } 57 var store storage.ChunkStore 58 params := storage.NewDefaultLocalStoreParams() 59 params.Init(datadir) 60 params.BaseKey = addr.Over() 61 store, err = storage.NewTestLocalStoreForAddr(params) 62 if err != nil { 63 break 64 } 65 datadirs = append(datadirs, datadir) 66 stores[i] = store 67 } 68 teardown := func() { 69 for i, datadir := range datadirs { 70 stores[i].Close() 71 os.RemoveAll(datadir) 72 } 73 } 74 return stores, teardown, err 75 } 76 77 func NewAdapter(adapterType string, services adapters.Services) (adapter adapters.NodeAdapter, teardown func(), err error) { 78 teardown = func() {} 79 switch adapterType { 80 case "sim": 81 adapter = adapters.NewSimAdapter(services) 82 case "exec": 83 baseDir, err0 := ioutil.TempDir("", "swarm-test") 84 if err0 != nil { 85 return nil, teardown, err0 86 } 87 teardown = func() { os.RemoveAll(baseDir) } 88 adapter = adapters.NewExecAdapter(baseDir) 89 case "docker": 90 adapter, err = adapters.NewDockerAdapter() 91 if err != nil { 92 return nil, teardown, err 93 } 94 default: 95 return nil, teardown, errors.New("adapter needs to be one of sim, exec, docker") 96 } 97 return adapter, teardown, nil 98 } 99 100 func CheckResult(t *testing.T, result *simulations.StepResult, startedAt, finishedAt time.Time) { 101 t.Logf("Simulation passed in %s", result.FinishedAt.Sub(result.StartedAt)) 102 if len(result.Passes) > 1 { 103 var min, max time.Duration 104 var sum int 105 for _, pass := range result.Passes { 106 duration := pass.Sub(result.StartedAt) 107 if sum == 0 || duration < min { 108 min = duration 109 } 110 if duration > max { 111 max = duration 112 } 113 sum += int(duration.Nanoseconds()) 114 } 115 t.Logf("Min: %s, Max: %s, Average: %s", min, max, time.Duration(sum/len(result.Passes))*time.Nanosecond) 116 } 117 t.Logf("Setup: %s, Shutdown: %s", result.StartedAt.Sub(startedAt), finishedAt.Sub(result.FinishedAt)) 118 } 119 120 type RunConfig struct { 121 Adapter string 122 Step *simulations.Step 123 NodeCount int 124 ConnLevel int 125 ToAddr func(discover.NodeID) *network.BzzAddr 126 Services adapters.Services 127 DefaultService string 128 EnableMsgEvents bool 129 } 130 131 func NewSimulation(conf *RunConfig) (*Simulation, func(), error) { 132 // create network 133 nodes := conf.NodeCount 134 adapter, adapterTeardown, err := NewAdapter(conf.Adapter, conf.Services) 135 if err != nil { 136 return nil, adapterTeardown, err 137 } 138 defaultService := "streamer" 139 if conf.DefaultService != "" { 140 defaultService = conf.DefaultService 141 } 142 net := simulations.NewNetwork(adapter, &simulations.NetworkConfig{ 143 ID: "0", 144 DefaultService: defaultService, 145 }) 146 teardown := func() { 147 adapterTeardown() 148 net.Shutdown() 149 } 150 ids := make([]discover.NodeID, nodes) 151 addrs := make([]network.Addr, nodes) 152 // start nodes 153 for i := 0; i < nodes; i++ { 154 nodeconf := adapters.RandomNodeConfig() 155 nodeconf.EnableMsgEvents = conf.EnableMsgEvents 156 node, err := net.NewNodeWithConfig(nodeconf) 157 if err != nil { 158 return nil, teardown, fmt.Errorf("error creating node: %s", err) 159 } 160 ids[i] = node.ID() 161 addrs[i] = conf.ToAddr(ids[i]) 162 } 163 // set nodes number of Stores available 164 stores, storeTeardown, err := SetStores(addrs...) 165 teardown = func() { 166 net.Shutdown() 167 adapterTeardown() 168 storeTeardown() 169 } 170 if err != nil { 171 return nil, teardown, err 172 } 173 s := &Simulation{ 174 Net: net, 175 Stores: stores, 176 IDs: ids, 177 Addrs: addrs, 178 } 179 return s, teardown, nil 180 } 181 182 func (s *Simulation) Run(ctx context.Context, conf *RunConfig) (*simulations.StepResult, error) { 183 // bring up nodes, launch the servive 184 nodes := conf.NodeCount 185 conns := conf.ConnLevel 186 for i := 0; i < nodes; i++ { 187 if err := s.Net.Start(s.IDs[i]); err != nil { 188 return nil, fmt.Errorf("error starting node %s: %s", s.IDs[i].TerminalString(), err) 189 } 190 } 191 // run a simulation which connects the 10 nodes in a chain 192 wg := sync.WaitGroup{} 193 for i := range s.IDs { 194 // collect the overlay addresses, to 195 for j := 0; j < conns; j++ { 196 var k int 197 if j == 0 { 198 k = i - 1 199 } else { 200 k = rand.Intn(len(s.IDs)) 201 } 202 if i > 0 { 203 wg.Add(1) 204 go func(i, k int) { 205 defer wg.Done() 206 s.Net.Connect(s.IDs[i], s.IDs[k]) 207 }(i, k) 208 } 209 } 210 } 211 wg.Wait() 212 log.Info(fmt.Sprintf("simulation with %v nodes", len(s.Addrs))) 213 214 // create an only locally retrieving FileStore for the pivot node to test 215 // if retriee requests have arrived 216 result := simulations.NewSimulation(s.Net).Run(ctx, conf.Step) 217 return result, nil 218 } 219 220 // WatchDisconnections subscribes to admin peerEvents and sends peer event drop 221 // errors to the errc channel. Channel quitC signals the termination of the event loop. 222 // Returned doneC will be closed after the rpc subscription is unsubscribed, 223 // signaling that simulations network is safe to shutdown. 224 func WatchDisconnections(id discover.NodeID, client *rpc.Client, errc chan error, quitC chan struct{}) (doneC <-chan struct{}, err error) { 225 events := make(chan *p2p.PeerEvent) 226 sub, err := client.Subscribe(context.Background(), "admin", events, "peerEvents") 227 if err != nil { 228 return nil, fmt.Errorf("error getting peer events for node %v: %s", id, err) 229 } 230 c := make(chan struct{}) 231 go func() { 232 defer func() { 233 log.Trace("watch disconnections: unsubscribe", "id", id) 234 sub.Unsubscribe() 235 close(c) 236 }() 237 for { 238 select { 239 case <-quitC: 240 return 241 case e := <-events: 242 if e.Type == p2p.PeerEventTypeDrop { 243 select { 244 case errc <- fmt.Errorf("peerEvent for node %v: %v", id, e): 245 case <-quitC: 246 return 247 } 248 } 249 case err := <-sub.Err(): 250 if err != nil { 251 select { 252 case errc <- fmt.Errorf("error getting peer events for node %v: %v", id, err): 253 case <-quitC: 254 return 255 } 256 } 257 } 258 } 259 }() 260 return c, nil 261 } 262 263 func Trigger(d time.Duration, quitC chan struct{}, ids ...discover.NodeID) chan discover.NodeID { 264 trigger := make(chan discover.NodeID) 265 go func() { 266 defer close(trigger) 267 ticker := time.NewTicker(d) 268 defer ticker.Stop() 269 // we are only testing the pivot node (net.Nodes[0]) 270 for range ticker.C { 271 for _, id := range ids { 272 select { 273 case trigger <- id: 274 case <-quitC: 275 return 276 } 277 } 278 } 279 }() 280 return trigger 281 } 282 283 func (sim *Simulation) CallClient(id discover.NodeID, f func(*rpc.Client) error) error { 284 node := sim.Net.GetNode(id) 285 if node == nil { 286 return fmt.Errorf("unknown node: %s", id) 287 } 288 client, err := node.Client() 289 if err != nil { 290 return fmt.Errorf("error getting node client: %s", err) 291 } 292 return f(client) 293 }