github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/cmd/swarm/run_test.go (about)

     1  
     2  //<developer>
     3  //    <name>linapex 曹一峰</name>
     4  //    <email>linapex@163.com</email>
     5  //    <wx>superexc</wx>
     6  //    <qqgroup>128148617</qqgroup>
     7  //    <url>https://jsq.ink</url>
     8  //    <role>pku engineer</role>
     9  //    <date>2019-03-16 12:09:31</date>
    10  //</624342606255493120>
    11  
    12  
    13  package main
    14  
    15  import (
    16  	"context"
    17  	"crypto/ecdsa"
    18  	"fmt"
    19  	"io/ioutil"
    20  	"net"
    21  	"os"
    22  	"path"
    23  	"path/filepath"
    24  	"runtime"
    25  	"sync"
    26  	"syscall"
    27  	"testing"
    28  	"time"
    29  
    30  	"github.com/docker/docker/pkg/reexec"
    31  	"github.com/ethereum/go-ethereum/accounts"
    32  	"github.com/ethereum/go-ethereum/accounts/keystore"
    33  	"github.com/ethereum/go-ethereum/internal/cmdtest"
    34  	"github.com/ethereum/go-ethereum/node"
    35  	"github.com/ethereum/go-ethereum/p2p"
    36  	"github.com/ethereum/go-ethereum/rpc"
    37  	"github.com/ethereum/go-ethereum/swarm"
    38  )
    39  
    40  func init() {
    41  //如果我们在run swarm中被执行为“swarm测试”,就运行这个应用程序。
    42  	reexec.Register("swarm-test", func() {
    43  		if err := app.Run(os.Args); err != nil {
    44  			fmt.Fprintln(os.Stderr, err)
    45  			os.Exit(1)
    46  		}
    47  		os.Exit(0)
    48  	})
    49  }
    50  
    51  func TestMain(m *testing.M) {
    52  //检查我们是否被重新执行了
    53  	if reexec.Init() {
    54  		return
    55  	}
    56  	os.Exit(m.Run())
    57  }
    58  
    59  func runSwarm(t *testing.T, args ...string) *cmdtest.TestCmd {
    60  	tt := cmdtest.NewTestCmd(t, nil)
    61  
    62  //
    63  //函数将阻止任何测试运行。
    64  	tt.Run("swarm-test", args...)
    65  
    66  	return tt
    67  }
    68  
    69  type testCluster struct {
    70  	Nodes  []*testNode
    71  	TmpDir string
    72  }
    73  
    74  //
    75  //
    76  //
    77  //它。
    78  //
    79  //
    80  //
    81  //
    82  //
    83  //
    84  //
    85  
    86  func newTestCluster(t *testing.T, size int) *testCluster {
    87  	cluster := &testCluster{}
    88  	defer func() {
    89  		if t.Failed() {
    90  			cluster.Shutdown()
    91  		}
    92  	}()
    93  
    94  	tmpdir, err := ioutil.TempDir("", "swarm-test")
    95  	if err != nil {
    96  		t.Fatal(err)
    97  	}
    98  	cluster.TmpDir = tmpdir
    99  
   100  //
   101  	cluster.StartNewNodes(t, size)
   102  
   103  	if size == 1 {
   104  		return cluster
   105  	}
   106  
   107  //
   108  	for _, node := range cluster.Nodes {
   109  		if err := node.Client.Call(nil, "admin_addPeer", cluster.Nodes[0].Enode); err != nil {
   110  			t.Fatal(err)
   111  		}
   112  	}
   113  
   114  //
   115  outer:
   116  	for _, node := range cluster.Nodes {
   117  		var peers []*p2p.PeerInfo
   118  		for start := time.Now(); time.Since(start) < time.Minute; time.Sleep(50 * time.Millisecond) {
   119  			if err := node.Client.Call(&peers, "admin_peers"); err != nil {
   120  				t.Fatal(err)
   121  			}
   122  			if len(peers) == len(cluster.Nodes)-1 {
   123  				continue outer
   124  			}
   125  		}
   126  		t.Fatalf("%s only has %d / %d peers", node.Name, len(peers), len(cluster.Nodes)-1)
   127  	}
   128  
   129  	return cluster
   130  }
   131  
   132  func (c *testCluster) Shutdown() {
   133  	for _, node := range c.Nodes {
   134  		node.Shutdown()
   135  	}
   136  	os.RemoveAll(c.TmpDir)
   137  }
   138  
   139  func (c *testCluster) Stop() {
   140  	for _, node := range c.Nodes {
   141  		node.Shutdown()
   142  	}
   143  }
   144  
   145  func (c *testCluster) StartNewNodes(t *testing.T, size int) {
   146  	c.Nodes = make([]*testNode, 0, size)
   147  	for i := 0; i < size; i++ {
   148  		dir := filepath.Join(c.TmpDir, fmt.Sprintf("swarm%02d", i))
   149  		if err := os.Mkdir(dir, 0700); err != nil {
   150  			t.Fatal(err)
   151  		}
   152  
   153  		node := newTestNode(t, dir)
   154  		node.Name = fmt.Sprintf("swarm%02d", i)
   155  
   156  		c.Nodes = append(c.Nodes, node)
   157  	}
   158  }
   159  
   160  func (c *testCluster) StartExistingNodes(t *testing.T, size int, bzzaccount string) {
   161  	c.Nodes = make([]*testNode, 0, size)
   162  	for i := 0; i < size; i++ {
   163  		dir := filepath.Join(c.TmpDir, fmt.Sprintf("swarm%02d", i))
   164  		node := existingTestNode(t, dir, bzzaccount)
   165  		node.Name = fmt.Sprintf("swarm%02d", i)
   166  
   167  		c.Nodes = append(c.Nodes, node)
   168  	}
   169  }
   170  
   171  func (c *testCluster) Cleanup() {
   172  	os.RemoveAll(c.TmpDir)
   173  }
   174  
   175  type testNode struct {
   176  	Name       string
   177  	Addr       string
   178  	URL        string
   179  	Enode      string
   180  	Dir        string
   181  	IpcPath    string
   182  	PrivateKey *ecdsa.PrivateKey
   183  	Client     *rpc.Client
   184  	Cmd        *cmdtest.TestCmd
   185  }
   186  
   187  const testPassphrase = "swarm-test-passphrase"
   188  
   189  func getTestAccount(t *testing.T, dir string) (conf *node.Config, account accounts.Account) {
   190  //创建密钥
   191  	conf = &node.Config{
   192  		DataDir: dir,
   193  		IPCPath: "bzzd.ipc",
   194  		NoUSB:   true,
   195  	}
   196  	n, err := node.New(conf)
   197  	if err != nil {
   198  		t.Fatal(err)
   199  	}
   200  	account, err = n.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore).NewAccount(testPassphrase)
   201  	if err != nil {
   202  		t.Fatal(err)
   203  	}
   204  
   205  //在Windows上运行测试时使用唯一的ipcpath
   206  	if runtime.GOOS == "windows" {
   207  		conf.IPCPath = fmt.Sprintf("bzzd-%s.ipc", account.Address.String())
   208  	}
   209  
   210  	return conf, account
   211  }
   212  
   213  func existingTestNode(t *testing.T, dir string, bzzaccount string) *testNode {
   214  	conf, _ := getTestAccount(t, dir)
   215  	node := &testNode{Dir: dir}
   216  
   217  //在Windows上运行测试时使用唯一的ipcpath
   218  	if runtime.GOOS == "windows" {
   219  		conf.IPCPath = fmt.Sprintf("bzzd-%s.ipc", bzzaccount)
   220  	}
   221  
   222  //指定端口
   223  	ports, err := getAvailableTCPPorts(2)
   224  	if err != nil {
   225  		t.Fatal(err)
   226  	}
   227  	p2pPort := ports[0]
   228  	httpPort := ports[1]
   229  
   230  //启动节点
   231  	node.Cmd = runSwarm(t,
   232  		"--port", p2pPort,
   233  		"--nodiscover",
   234  		"--datadir", dir,
   235  		"--ipcpath", conf.IPCPath,
   236  		"--ens-api", "",
   237  		"--bzzaccount", bzzaccount,
   238  		"--bzznetworkid", "321",
   239  		"--bzzport", httpPort,
   240  		"--verbosity", "6",
   241  	)
   242  	node.Cmd.InputLine(testPassphrase)
   243  	defer func() {
   244  		if t.Failed() {
   245  			node.Shutdown()
   246  		}
   247  	}()
   248  
   249  	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
   250  	defer cancel()
   251  
   252  //确保所有端口都有活动的侦听器
   253  //这样下一个节点就不会得到相同的
   254  //调用GetAvailableTCPPorts时
   255  	err = waitTCPPorts(ctx, ports...)
   256  	if err != nil {
   257  		t.Fatal(err)
   258  	}
   259  
   260  //等待节点启动
   261  	for start := time.Now(); time.Since(start) < 10*time.Second; time.Sleep(50 * time.Millisecond) {
   262  		node.Client, err = rpc.Dial(conf.IPCEndpoint())
   263  		if err == nil {
   264  			break
   265  		}
   266  	}
   267  	if node.Client == nil {
   268  		t.Fatal(err)
   269  	}
   270  
   271  //加载信息
   272  	var info swarm.Info
   273  	if err := node.Client.Call(&info, "bzz_info"); err != nil {
   274  		t.Fatal(err)
   275  	}
   276  	node.Addr = net.JoinHostPort("127.0.0.1", info.Port)
   277  node.URL = "http://“+No.ADDR”
   278  
   279  	var nodeInfo p2p.NodeInfo
   280  	if err := node.Client.Call(&nodeInfo, "admin_nodeInfo"); err != nil {
   281  		t.Fatal(err)
   282  	}
   283  node.Enode = fmt.Sprintf("enode://%s@127.0.0.1:%s”,nodeinfo.id,p2pport)
   284  
   285  	return node
   286  }
   287  
   288  func newTestNode(t *testing.T, dir string) *testNode {
   289  
   290  	conf, account := getTestAccount(t, dir)
   291  	ks := keystore.NewKeyStore(path.Join(dir, "keystore"), 1<<18, 1)
   292  
   293  	pk := decryptStoreAccount(ks, account.Address.Hex(), []string{testPassphrase})
   294  
   295  	node := &testNode{Dir: dir, PrivateKey: pk}
   296  
   297  //指定端口
   298  	ports, err := getAvailableTCPPorts(2)
   299  	if err != nil {
   300  		t.Fatal(err)
   301  	}
   302  	p2pPort := ports[0]
   303  	httpPort := ports[1]
   304  
   305  //启动节点
   306  	node.Cmd = runSwarm(t,
   307  		"--port", p2pPort,
   308  		"--nodiscover",
   309  		"--datadir", dir,
   310  		"--ipcpath", conf.IPCPath,
   311  		"--ens-api", "",
   312  		"--bzzaccount", account.Address.String(),
   313  		"--bzznetworkid", "321",
   314  		"--bzzport", httpPort,
   315  		"--verbosity", "6",
   316  	)
   317  	node.Cmd.InputLine(testPassphrase)
   318  	defer func() {
   319  		if t.Failed() {
   320  			node.Shutdown()
   321  		}
   322  	}()
   323  
   324  	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
   325  	defer cancel()
   326  
   327  //确保所有端口都有活动的侦听器
   328  //这样下一个节点就不会得到相同的
   329  //调用GetAvailableTCPPorts时
   330  	err = waitTCPPorts(ctx, ports...)
   331  	if err != nil {
   332  		t.Fatal(err)
   333  	}
   334  
   335  //等待节点启动
   336  	for start := time.Now(); time.Since(start) < 10*time.Second; time.Sleep(50 * time.Millisecond) {
   337  		node.Client, err = rpc.Dial(conf.IPCEndpoint())
   338  		if err == nil {
   339  			break
   340  		}
   341  	}
   342  	if node.Client == nil {
   343  		t.Fatal(err)
   344  	}
   345  
   346  //加载信息
   347  	var info swarm.Info
   348  	if err := node.Client.Call(&info, "bzz_info"); err != nil {
   349  		t.Fatal(err)
   350  	}
   351  	node.Addr = net.JoinHostPort("127.0.0.1", info.Port)
   352  node.URL = "http://“+No.ADDR”
   353  
   354  	var nodeInfo p2p.NodeInfo
   355  	if err := node.Client.Call(&nodeInfo, "admin_nodeInfo"); err != nil {
   356  		t.Fatal(err)
   357  	}
   358  node.Enode = fmt.Sprintf("enode://%s@127.0.0.1:%s”,nodeinfo.id,p2pport)
   359  	node.IpcPath = conf.IPCPath
   360  
   361  	return node
   362  }
   363  
   364  func (n *testNode) Shutdown() {
   365  	if n.Cmd != nil {
   366  		n.Cmd.Kill()
   367  	}
   368  }
   369  
   370  //
   371  //
   372  //
   373  //
   374  //并保证同一港口将被运回
   375  //不同的调用,因为侦听器在函数内关闭,
   376  //
   377  //可用端口。
   378  func getAvailableTCPPorts(count int) (ports []string, err error) {
   379  	for i := 0; i < count; i++ {
   380  		l, err := net.Listen("tcp", "127.0.0.1:0")
   381  		if err != nil {
   382  			return nil, err
   383  		}
   384  //在循环中延迟关闭以确保同一端口不会
   385  //在下一个迭代中被选择
   386  		defer l.Close()
   387  
   388  		_, port, err := net.SplitHostPort(l.Addr().String())
   389  		if err != nil {
   390  			return nil, err
   391  		}
   392  		ports = append(ports, port)
   393  	}
   394  	return ports, nil
   395  }
   396  
   397  //
   398  //
   399  //
   400  //遇到错误。
   401  //另请参见waitcpport。
   402  func waitTCPPorts(ctx context.Context, ports ...string) error {
   403  	var err error
   404  //在中分配的mu locks err变量
   405  //其他Goroutines
   406  	var mu sync.Mutex
   407  
   408  //
   409  //
   410  //防止不必要的等待
   411  	ctx, cancel := context.WithCancel(ctx)
   412  	defer cancel()
   413  
   414  	var wg sync.WaitGroup
   415  	for _, port := range ports {
   416  		wg.Add(1)
   417  		go func(port string) {
   418  			defer wg.Done()
   419  
   420  			e := waitTCPPort(ctx, port)
   421  
   422  			mu.Lock()
   423  			defer mu.Unlock()
   424  			if e != nil && err == nil {
   425  				err = e
   426  				cancel()
   427  			}
   428  		}(port)
   429  	}
   430  	wg.Wait()
   431  
   432  	return err
   433  }
   434  
   435  //
   436  //ONA提供的端口。它最多有3分钟的超时时间,
   437  //
   438  //提供的上下文实例。拨号程序超时10秒
   439  //在每次迭代中,连接被拒绝的错误将
   440  //在100毫秒内重试。
   441  func waitTCPPort(ctx context.Context, port string) error {
   442  	ctx, cancel := context.WithTimeout(ctx, 3*time.Minute)
   443  	defer cancel()
   444  
   445  	for {
   446  		c, err := (&net.Dialer{Timeout: 10 * time.Second}).DialContext(ctx, "tcp", "127.0.0.1:"+port)
   447  		if err != nil {
   448  			if operr, ok := err.(*net.OpError); ok {
   449  				if syserr, ok := operr.Err.(*os.SyscallError); ok && syserr.Err == syscall.ECONNREFUSED {
   450  					time.Sleep(100 * time.Millisecond)
   451  					continue
   452  				}
   453  			}
   454  			return err
   455  		}
   456  		return c.Close()
   457  	}
   458  }
   459