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