github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/cmd/swarm/run_test.go (about)

     1  // This file is part of the go-sberex library. The go-sberex library is 
     2  // free software: you can redistribute it and/or modify it under the terms 
     3  // of the GNU Lesser General Public License as published by the Free 
     4  // Software Foundation, either version 3 of the License, or (at your option)
     5  // any later version.
     6  //
     7  // The go-sberex library is distributed in the hope that it will be useful, 
     8  // but WITHOUT ANY WARRANTY; without even the implied warranty of
     9  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 
    10  // General Public License <http://www.gnu.org/licenses/> for more details.
    11  
    12  package main
    13  
    14  import (
    15  	"fmt"
    16  	"io/ioutil"
    17  	"net"
    18  	"os"
    19  	"path/filepath"
    20  	"runtime"
    21  	"testing"
    22  	"time"
    23  
    24  	"github.com/docker/docker/pkg/reexec"
    25  	"github.com/Sberex/go-sberex/accounts"
    26  	"github.com/Sberex/go-sberex/accounts/keystore"
    27  	"github.com/Sberex/go-sberex/internal/cmdtest"
    28  	"github.com/Sberex/go-sberex/node"
    29  	"github.com/Sberex/go-sberex/p2p"
    30  	"github.com/Sberex/go-sberex/rpc"
    31  	"github.com/Sberex/go-sberex/swarm"
    32  )
    33  
    34  func init() {
    35  	// Run the app if we've been exec'd as "swarm-test" in runSwarm.
    36  	reexec.Register("swarm-test", func() {
    37  		if err := app.Run(os.Args); err != nil {
    38  			fmt.Fprintln(os.Stderr, err)
    39  			os.Exit(1)
    40  		}
    41  		os.Exit(0)
    42  	})
    43  }
    44  
    45  func TestMain(m *testing.M) {
    46  	// check if we have been reexec'd
    47  	if reexec.Init() {
    48  		return
    49  	}
    50  	os.Exit(m.Run())
    51  }
    52  
    53  func runSwarm(t *testing.T, args ...string) *cmdtest.TestCmd {
    54  	tt := cmdtest.NewTestCmd(t, nil)
    55  
    56  	// Boot "swarm". This actually runs the test binary but the TestMain
    57  	// function will prevent any tests from running.
    58  	tt.Run("swarm-test", args...)
    59  
    60  	return tt
    61  }
    62  
    63  type testCluster struct {
    64  	Nodes  []*testNode
    65  	TmpDir string
    66  }
    67  
    68  // newTestCluster starts a test swarm cluster of the given size.
    69  //
    70  // A temporary directory is created and each node gets a data directory inside
    71  // it.
    72  //
    73  // Each node listens on 127.0.0.1 with random ports for both the HTTP and p2p
    74  // ports (assigned by first listening on 127.0.0.1:0 and then passing the ports
    75  // as flags).
    76  //
    77  // When starting more than one node, they are connected together using the
    78  // admin SetPeer RPC method.
    79  func newTestCluster(t *testing.T, size int) *testCluster {
    80  	cluster := &testCluster{}
    81  	defer func() {
    82  		if t.Failed() {
    83  			cluster.Shutdown()
    84  		}
    85  	}()
    86  
    87  	tmpdir, err := ioutil.TempDir("", "swarm-test")
    88  	if err != nil {
    89  		t.Fatal(err)
    90  	}
    91  	cluster.TmpDir = tmpdir
    92  
    93  	// start the nodes
    94  	cluster.Nodes = make([]*testNode, 0, size)
    95  	for i := 0; i < size; i++ {
    96  		dir := filepath.Join(cluster.TmpDir, fmt.Sprintf("swarm%02d", i))
    97  		if err := os.Mkdir(dir, 0700); err != nil {
    98  			t.Fatal(err)
    99  		}
   100  
   101  		node := newTestNode(t, dir)
   102  		node.Name = fmt.Sprintf("swarm%02d", i)
   103  
   104  		cluster.Nodes = append(cluster.Nodes, node)
   105  	}
   106  
   107  	if size == 1 {
   108  		return cluster
   109  	}
   110  
   111  	// connect the nodes together
   112  	for _, node := range cluster.Nodes {
   113  		if err := node.Client.Call(nil, "admin_addPeer", cluster.Nodes[0].Enode); err != nil {
   114  			t.Fatal(err)
   115  		}
   116  	}
   117  
   118  	// wait until all nodes have the correct number of peers
   119  outer:
   120  	for _, node := range cluster.Nodes {
   121  		var peers []*p2p.PeerInfo
   122  		for start := time.Now(); time.Since(start) < time.Minute; time.Sleep(50 * time.Millisecond) {
   123  			if err := node.Client.Call(&peers, "admin_peers"); err != nil {
   124  				t.Fatal(err)
   125  			}
   126  			if len(peers) == len(cluster.Nodes)-1 {
   127  				continue outer
   128  			}
   129  		}
   130  		t.Fatalf("%s only has %d / %d peers", node.Name, len(peers), len(cluster.Nodes)-1)
   131  	}
   132  
   133  	return cluster
   134  }
   135  
   136  func (c *testCluster) Shutdown() {
   137  	for _, node := range c.Nodes {
   138  		node.Shutdown()
   139  	}
   140  	os.RemoveAll(c.TmpDir)
   141  }
   142  
   143  type testNode struct {
   144  	Name   string
   145  	Addr   string
   146  	URL    string
   147  	Enode  string
   148  	Dir    string
   149  	Client *rpc.Client
   150  	Cmd    *cmdtest.TestCmd
   151  }
   152  
   153  const testPassphrase = "swarm-test-passphrase"
   154  
   155  func getTestAccount(t *testing.T, dir string) (conf *node.Config, account accounts.Account) {
   156  	// create key
   157  	conf = &node.Config{
   158  		DataDir: dir,
   159  		IPCPath: "bzzd.ipc",
   160  		NoUSB:   true,
   161  	}
   162  	n, err := node.New(conf)
   163  	if err != nil {
   164  		t.Fatal(err)
   165  	}
   166  	account, err = n.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore).NewAccount(testPassphrase)
   167  	if err != nil {
   168  		t.Fatal(err)
   169  	}
   170  
   171  	// use a unique IPCPath when running tests on Windows
   172  	if runtime.GOOS == "windows" {
   173  		conf.IPCPath = fmt.Sprintf("bzzd-%s.ipc", account.Address.String())
   174  	}
   175  
   176  	return conf, account
   177  }
   178  
   179  func newTestNode(t *testing.T, dir string) *testNode {
   180  
   181  	conf, account := getTestAccount(t, dir)
   182  	node := &testNode{Dir: dir}
   183  
   184  	// assign ports
   185  	httpPort, err := assignTCPPort()
   186  	if err != nil {
   187  		t.Fatal(err)
   188  	}
   189  	p2pPort, err := assignTCPPort()
   190  	if err != nil {
   191  		t.Fatal(err)
   192  	}
   193  
   194  	// start the node
   195  	node.Cmd = runSwarm(t,
   196  		"--port", p2pPort,
   197  		"--nodiscover",
   198  		"--datadir", dir,
   199  		"--ipcpath", conf.IPCPath,
   200  		"--ens-api", "",
   201  		"--bzzaccount", account.Address.String(),
   202  		"--bzznetworkid", "321",
   203  		"--bzzport", httpPort,
   204  		"--verbosity", "6",
   205  	)
   206  	node.Cmd.InputLine(testPassphrase)
   207  	defer func() {
   208  		if t.Failed() {
   209  			node.Shutdown()
   210  		}
   211  	}()
   212  
   213  	// wait for the node to start
   214  	for start := time.Now(); time.Since(start) < 10*time.Second; time.Sleep(50 * time.Millisecond) {
   215  		node.Client, err = rpc.Dial(conf.IPCEndpoint())
   216  		if err == nil {
   217  			break
   218  		}
   219  	}
   220  	if node.Client == nil {
   221  		t.Fatal(err)
   222  	}
   223  
   224  	// load info
   225  	var info swarm.Info
   226  	if err := node.Client.Call(&info, "bzz_info"); err != nil {
   227  		t.Fatal(err)
   228  	}
   229  	node.Addr = net.JoinHostPort("127.0.0.1", info.Port)
   230  	node.URL = "http://" + node.Addr
   231  
   232  	var nodeInfo p2p.NodeInfo
   233  	if err := node.Client.Call(&nodeInfo, "admin_nodeInfo"); err != nil {
   234  		t.Fatal(err)
   235  	}
   236  	node.Enode = fmt.Sprintf("enode://%s@127.0.0.1:%s", nodeInfo.ID, p2pPort)
   237  
   238  	return node
   239  }
   240  
   241  func (n *testNode) Shutdown() {
   242  	if n.Cmd != nil {
   243  		n.Cmd.Kill()
   244  	}
   245  }
   246  
   247  func assignTCPPort() (string, error) {
   248  	l, err := net.Listen("tcp", "127.0.0.1:0")
   249  	if err != nil {
   250  		return "", err
   251  	}
   252  	l.Close()
   253  	_, port, err := net.SplitHostPort(l.Addr().String())
   254  	if err != nil {
   255  		return "", err
   256  	}
   257  	return port, nil
   258  }