github.com/oskarth/go-ethereum@v1.6.8-0.20191013093314-dac24a9d3494/swarm/network/simulation/simulation_test.go (about)

     1  // Copyright 2018 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 simulation
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"flag"
    23  	"sync"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/ethereum/go-ethereum/log"
    28  	"github.com/ethereum/go-ethereum/node"
    29  	"github.com/ethereum/go-ethereum/p2p"
    30  	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
    31  	"github.com/ethereum/go-ethereum/rpc"
    32  	colorable "github.com/mattn/go-colorable"
    33  )
    34  
    35  var (
    36  	loglevel = flag.Int("loglevel", 2, "verbosity of logs")
    37  )
    38  
    39  func init() {
    40  	flag.Parse()
    41  	log.PrintOrigins(true)
    42  	log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*loglevel), log.StreamHandler(colorable.NewColorableStderr(), log.TerminalFormat(true))))
    43  }
    44  
    45  // TestRun tests if Run method calls RunFunc and if it handles context properly.
    46  func TestRun(t *testing.T) {
    47  	sim := New(noopServiceFuncMap)
    48  	defer sim.Close()
    49  
    50  	t.Run("call", func(t *testing.T) {
    51  		expect := "something"
    52  		var got string
    53  		r := sim.Run(context.Background(), func(ctx context.Context, sim *Simulation) error {
    54  			got = expect
    55  			return nil
    56  		})
    57  
    58  		if r.Error != nil {
    59  			t.Errorf("unexpected error: %v", r.Error)
    60  		}
    61  		if got != expect {
    62  			t.Errorf("expected %q, got %q", expect, got)
    63  		}
    64  	})
    65  
    66  	t.Run("cancellation", func(t *testing.T) {
    67  		ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
    68  		defer cancel()
    69  
    70  		r := sim.Run(ctx, func(ctx context.Context, sim *Simulation) error {
    71  			time.Sleep(time.Second)
    72  			return nil
    73  		})
    74  
    75  		if r.Error != context.DeadlineExceeded {
    76  			t.Errorf("unexpected error: %v", r.Error)
    77  		}
    78  	})
    79  
    80  	t.Run("context value and duration", func(t *testing.T) {
    81  		ctx := context.WithValue(context.Background(), "hey", "there")
    82  		sleep := 50 * time.Millisecond
    83  
    84  		r := sim.Run(ctx, func(ctx context.Context, sim *Simulation) error {
    85  			if ctx.Value("hey") != "there" {
    86  				return errors.New("expected context value not passed")
    87  			}
    88  			time.Sleep(sleep)
    89  			return nil
    90  		})
    91  
    92  		if r.Error != nil {
    93  			t.Errorf("unexpected error: %v", r.Error)
    94  		}
    95  		if r.Duration < sleep {
    96  			t.Errorf("reported run duration less then expected: %s", r.Duration)
    97  		}
    98  	})
    99  }
   100  
   101  // TestClose tests are Close method triggers all close functions and are all nodes not up anymore.
   102  func TestClose(t *testing.T) {
   103  	var mu sync.Mutex
   104  	var cleanupCount int
   105  
   106  	sleep := 50 * time.Millisecond
   107  
   108  	sim := New(map[string]ServiceFunc{
   109  		"noop": func(ctx *adapters.ServiceContext, b *sync.Map) (node.Service, func(), error) {
   110  			return newNoopService(), func() {
   111  				time.Sleep(sleep)
   112  				mu.Lock()
   113  				defer mu.Unlock()
   114  				cleanupCount++
   115  			}, nil
   116  		},
   117  	})
   118  
   119  	nodeCount := 30
   120  
   121  	_, err := sim.AddNodes(nodeCount)
   122  	if err != nil {
   123  		t.Fatal(err)
   124  	}
   125  
   126  	var upNodeCount int
   127  	for _, n := range sim.Net.GetNodes() {
   128  		if n.Up {
   129  			upNodeCount++
   130  		}
   131  	}
   132  	if upNodeCount != nodeCount {
   133  		t.Errorf("all nodes should be up, insted only %v are up", upNodeCount)
   134  	}
   135  
   136  	sim.Close()
   137  
   138  	if cleanupCount != nodeCount {
   139  		t.Errorf("number of cleanups expected %v, got %v", nodeCount, cleanupCount)
   140  	}
   141  
   142  	upNodeCount = 0
   143  	for _, n := range sim.Net.GetNodes() {
   144  		if n.Up {
   145  			upNodeCount++
   146  		}
   147  	}
   148  	if upNodeCount != 0 {
   149  		t.Errorf("all nodes should be down, insted %v are up", upNodeCount)
   150  	}
   151  }
   152  
   153  // TestDone checks if Close method triggers the closing of done channel.
   154  func TestDone(t *testing.T) {
   155  	sim := New(noopServiceFuncMap)
   156  	sleep := 50 * time.Millisecond
   157  	timeout := 2 * time.Second
   158  
   159  	start := time.Now()
   160  	go func() {
   161  		time.Sleep(sleep)
   162  		sim.Close()
   163  	}()
   164  
   165  	select {
   166  	case <-time.After(timeout):
   167  		t.Error("done channel closing timed out")
   168  	case <-sim.Done():
   169  		if d := time.Since(start); d < sleep {
   170  			t.Errorf("done channel closed sooner then expected: %s", d)
   171  		}
   172  	}
   173  }
   174  
   175  // a helper map for usual services that do not do anything
   176  var noopServiceFuncMap = map[string]ServiceFunc{
   177  	"noop": noopServiceFunc,
   178  }
   179  
   180  // a helper function for most basic noop service
   181  func noopServiceFunc(ctx *adapters.ServiceContext, b *sync.Map) (node.Service, func(), error) {
   182  	return newNoopService(), nil, nil
   183  }
   184  
   185  // noopService is the service that does not do anything
   186  // but implements node.Service interface.
   187  type noopService struct{}
   188  
   189  func newNoopService() node.Service {
   190  	return &noopService{}
   191  }
   192  
   193  func (t *noopService) Protocols() []p2p.Protocol {
   194  	return []p2p.Protocol{}
   195  }
   196  
   197  func (t *noopService) APIs() []rpc.API {
   198  	return []rpc.API{}
   199  }
   200  
   201  func (t *noopService) Start(server *p2p.Server) error {
   202  	return nil
   203  }
   204  
   205  func (t *noopService) Stop() error {
   206  	return nil
   207  }