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  }