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