github.com/cheng762/platon-go@v1.8.17-0.20190529111256-7deff2d7be26/cmd/swarm/run_test.go (about) 1 // Copyright 2017 The go-ethereum Authors 2 // This file is part of go-ethereum. 3 // 4 // go-ethereum is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU 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 // go-ethereum 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 General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. 16 17 package main 18 19 import ( 20 "context" 21 "crypto/ecdsa" 22 "fmt" 23 "io/ioutil" 24 "net" 25 "os" 26 "path" 27 "path/filepath" 28 "runtime" 29 "sync" 30 "syscall" 31 "testing" 32 "time" 33 34 "github.com/docker/docker/pkg/reexec" 35 "github.com/PlatONnetwork/PlatON-Go/accounts" 36 "github.com/PlatONnetwork/PlatON-Go/accounts/keystore" 37 "github.com/PlatONnetwork/PlatON-Go/internal/cmdtest" 38 "github.com/PlatONnetwork/PlatON-Go/node" 39 "github.com/PlatONnetwork/PlatON-Go/p2p" 40 "github.com/PlatONnetwork/PlatON-Go/rpc" 41 "github.com/PlatONnetwork/PlatON-Go/swarm" 42 ) 43 44 func init() { 45 // Run the app if we've been exec'd as "swarm-test" in runSwarm. 46 reexec.Register("swarm-test", func() { 47 if err := app.Run(os.Args); err != nil { 48 fmt.Fprintln(os.Stderr, err) 49 os.Exit(1) 50 } 51 os.Exit(0) 52 }) 53 } 54 55 func TestMain(m *testing.M) { 56 // check if we have been reexec'd 57 if reexec.Init() { 58 return 59 } 60 os.Exit(m.Run()) 61 } 62 63 func runSwarm(t *testing.T, args ...string) *cmdtest.TestCmd { 64 tt := cmdtest.NewTestCmd(t, nil) 65 66 // Boot "swarm". This actually runs the test binary but the TestMain 67 // function will prevent any tests from running. 68 tt.Run("swarm-test", args...) 69 70 return tt 71 } 72 73 type testCluster struct { 74 Nodes []*testNode 75 TmpDir string 76 } 77 78 // newTestCluster starts a test swarm cluster of the given size. 79 // 80 // A temporary directory is created and each node gets a data directory inside 81 // it. 82 // 83 // Each node listens on 127.0.0.1 with random ports for both the HTTP and p2p 84 // ports (assigned by first listening on 127.0.0.1:0 and then passing the ports 85 // as flags). 86 // 87 // When starting more than one node, they are connected together using the 88 // admin SetPeer RPC method. 89 90 func newTestCluster(t *testing.T, size int) *testCluster { 91 cluster := &testCluster{} 92 defer func() { 93 if t.Failed() { 94 cluster.Shutdown() 95 } 96 }() 97 98 tmpdir, err := ioutil.TempDir("", "swarm-test") 99 if err != nil { 100 t.Fatal(err) 101 } 102 cluster.TmpDir = tmpdir 103 104 // start the nodes 105 cluster.StartNewNodes(t, size) 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 func (c *testCluster) Stop() { 144 for _, node := range c.Nodes { 145 node.Shutdown() 146 } 147 } 148 149 func (c *testCluster) StartNewNodes(t *testing.T, size int) { 150 c.Nodes = make([]*testNode, 0, size) 151 for i := 0; i < size; i++ { 152 dir := filepath.Join(c.TmpDir, fmt.Sprintf("swarm%02d", i)) 153 if err := os.Mkdir(dir, 0700); err != nil { 154 t.Fatal(err) 155 } 156 157 node := newTestNode(t, dir) 158 node.Name = fmt.Sprintf("swarm%02d", i) 159 160 c.Nodes = append(c.Nodes, node) 161 } 162 } 163 164 func (c *testCluster) StartExistingNodes(t *testing.T, size int, bzzaccount string) { 165 c.Nodes = make([]*testNode, 0, size) 166 for i := 0; i < size; i++ { 167 dir := filepath.Join(c.TmpDir, fmt.Sprintf("swarm%02d", i)) 168 node := existingTestNode(t, dir, bzzaccount) 169 node.Name = fmt.Sprintf("swarm%02d", i) 170 171 c.Nodes = append(c.Nodes, node) 172 } 173 } 174 175 func (c *testCluster) Cleanup() { 176 os.RemoveAll(c.TmpDir) 177 } 178 179 type testNode struct { 180 Name string 181 Addr string 182 URL string 183 Enode string 184 Dir string 185 IpcPath string 186 PrivateKey *ecdsa.PrivateKey 187 Client *rpc.Client 188 Cmd *cmdtest.TestCmd 189 } 190 191 const testPassphrase = "swarm-test-passphrase" 192 193 func getTestAccount(t *testing.T, dir string) (conf *node.Config, account accounts.Account) { 194 // create key 195 conf = &node.Config{ 196 DataDir: dir, 197 IPCPath: "bzzd.ipc", 198 NoUSB: true, 199 } 200 n, err := node.New(conf) 201 if err != nil { 202 t.Fatal(err) 203 } 204 account, err = n.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore).NewAccount(testPassphrase) 205 if err != nil { 206 t.Fatal(err) 207 } 208 209 // use a unique IPCPath when running tests on Windows 210 if runtime.GOOS == "windows" { 211 conf.IPCPath = fmt.Sprintf("bzzd-%s.ipc", account.Address.String()) 212 } 213 214 return conf, account 215 } 216 217 func existingTestNode(t *testing.T, dir string, bzzaccount string) *testNode { 218 conf, _ := getTestAccount(t, dir) 219 node := &testNode{Dir: dir} 220 221 // use a unique IPCPath when running tests on Windows 222 if runtime.GOOS == "windows" { 223 conf.IPCPath = fmt.Sprintf("bzzd-%s.ipc", bzzaccount) 224 } 225 226 // assign ports 227 ports, err := getAvailableTCPPorts(2) 228 if err != nil { 229 t.Fatal(err) 230 } 231 p2pPort := ports[0] 232 httpPort := ports[1] 233 234 // start the node 235 node.Cmd = runSwarm(t, 236 "--port", p2pPort, 237 "--nodiscover", 238 "--datadir", dir, 239 "--ipcpath", conf.IPCPath, 240 "--ens-api", "", 241 "--bzzaccount", bzzaccount, 242 "--bzznetworkid", "321", 243 "--bzzport", httpPort, 244 "--verbosity", "6", 245 ) 246 node.Cmd.InputLine(testPassphrase) 247 defer func() { 248 if t.Failed() { 249 node.Shutdown() 250 } 251 }() 252 253 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) 254 defer cancel() 255 256 // ensure that all ports have active listeners 257 // so that the next node will not get the same 258 // when calling getAvailableTCPPorts 259 err = waitTCPPorts(ctx, ports...) 260 if err != nil { 261 t.Fatal(err) 262 } 263 264 // wait for the node to start 265 for start := time.Now(); time.Since(start) < 10*time.Second; time.Sleep(50 * time.Millisecond) { 266 node.Client, err = rpc.Dial(conf.IPCEndpoint()) 267 if err == nil { 268 break 269 } 270 } 271 if node.Client == nil { 272 t.Fatal(err) 273 } 274 275 // load info 276 var info swarm.Info 277 if err := node.Client.Call(&info, "bzz_info"); err != nil { 278 t.Fatal(err) 279 } 280 node.Addr = net.JoinHostPort("127.0.0.1", info.Port) 281 node.URL = "http://" + node.Addr 282 283 var nodeInfo p2p.NodeInfo 284 if err := node.Client.Call(&nodeInfo, "admin_nodeInfo"); err != nil { 285 t.Fatal(err) 286 } 287 node.Enode = fmt.Sprintf("enode://%s@127.0.0.1:%s", nodeInfo.ID, p2pPort) 288 289 return node 290 } 291 292 func newTestNode(t *testing.T, dir string) *testNode { 293 294 conf, account := getTestAccount(t, dir) 295 ks := keystore.NewKeyStore(path.Join(dir, "keystore"), 1<<18, 1) 296 297 pk := decryptStoreAccount(ks, account.Address.Hex(), []string{testPassphrase}) 298 299 node := &testNode{Dir: dir, PrivateKey: pk} 300 301 // assign ports 302 ports, err := getAvailableTCPPorts(2) 303 if err != nil { 304 t.Fatal(err) 305 } 306 p2pPort := ports[0] 307 httpPort := ports[1] 308 309 // start the node 310 node.Cmd = runSwarm(t, 311 "--port", p2pPort, 312 "--nodiscover", 313 "--datadir", dir, 314 "--ipcpath", conf.IPCPath, 315 "--ens-api", "", 316 "--bzzaccount", account.Address.String(), 317 "--bzznetworkid", "321", 318 "--bzzport", httpPort, 319 "--verbosity", "6", 320 ) 321 node.Cmd.InputLine(testPassphrase) 322 defer func() { 323 if t.Failed() { 324 node.Shutdown() 325 } 326 }() 327 328 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) 329 defer cancel() 330 331 // ensure that all ports have active listeners 332 // so that the next node will not get the same 333 // when calling getAvailableTCPPorts 334 err = waitTCPPorts(ctx, ports...) 335 if err != nil { 336 t.Fatal(err) 337 } 338 339 // wait for the node to start 340 for start := time.Now(); time.Since(start) < 10*time.Second; time.Sleep(50 * time.Millisecond) { 341 node.Client, err = rpc.Dial(conf.IPCEndpoint()) 342 if err == nil { 343 break 344 } 345 } 346 if node.Client == nil { 347 t.Fatal(err) 348 } 349 350 // load info 351 var info swarm.Info 352 if err := node.Client.Call(&info, "bzz_info"); err != nil { 353 t.Fatal(err) 354 } 355 node.Addr = net.JoinHostPort("127.0.0.1", info.Port) 356 node.URL = "http://" + node.Addr 357 358 var nodeInfo p2p.NodeInfo 359 if err := node.Client.Call(&nodeInfo, "admin_nodeInfo"); err != nil { 360 t.Fatal(err) 361 } 362 node.Enode = fmt.Sprintf("enode://%s@127.0.0.1:%s", nodeInfo.ID, p2pPort) 363 node.IpcPath = conf.IPCPath 364 365 return node 366 } 367 368 func (n *testNode) Shutdown() { 369 if n.Cmd != nil { 370 n.Cmd.Kill() 371 } 372 } 373 374 // getAvailableTCPPorts returns a set of ports that 375 // nothing is listening on at the time. 376 // 377 // Function assignTCPPort cannot be called in sequence 378 // and guardantee that the same port will be returned in 379 // different calls as the listener is closed within the function, 380 // not after all listeners are started and selected unique 381 // available ports. 382 func getAvailableTCPPorts(count int) (ports []string, err error) { 383 for i := 0; i < count; i++ { 384 l, err := net.Listen("tcp", "127.0.0.1:0") 385 if err != nil { 386 return nil, err 387 } 388 // defer close in the loop to be sure the same port will not 389 // be selected in the next iteration 390 defer l.Close() 391 392 _, port, err := net.SplitHostPort(l.Addr().String()) 393 if err != nil { 394 return nil, err 395 } 396 ports = append(ports, port) 397 } 398 return ports, nil 399 } 400 401 // waitTCPPorts blocks until tcp connections can be 402 // established on all provided ports. It runs all 403 // ports dialers in parallel, and returns the first 404 // encountered error. 405 // See waitTCPPort also. 406 func waitTCPPorts(ctx context.Context, ports ...string) error { 407 var err error 408 // mu locks err variable that is assigned in 409 // other goroutines 410 var mu sync.Mutex 411 412 // cancel is canceling all goroutines 413 // when the firs error is returned 414 // to prevent unnecessary waiting 415 ctx, cancel := context.WithCancel(ctx) 416 defer cancel() 417 418 var wg sync.WaitGroup 419 for _, port := range ports { 420 wg.Add(1) 421 go func(port string) { 422 defer wg.Done() 423 424 e := waitTCPPort(ctx, port) 425 426 mu.Lock() 427 defer mu.Unlock() 428 if e != nil && err == nil { 429 err = e 430 cancel() 431 } 432 }(port) 433 } 434 wg.Wait() 435 436 return err 437 } 438 439 // waitTCPPort blocks until tcp connection can be established 440 // ona provided port. It has a 3 minute timeout as maximum, 441 // to prevent long waiting, but it can be shortened with 442 // a provided context instance. Dialer has a 10 second timeout 443 // in every iteration, and connection refused error will be 444 // retried in 100 milliseconds periods. 445 func waitTCPPort(ctx context.Context, port string) error { 446 ctx, cancel := context.WithTimeout(ctx, 3*time.Minute) 447 defer cancel() 448 449 for { 450 c, err := (&net.Dialer{Timeout: 10 * time.Second}).DialContext(ctx, "tcp", "127.0.0.1:"+port) 451 if err != nil { 452 if operr, ok := err.(*net.OpError); ok { 453 if syserr, ok := operr.Err.(*os.SyscallError); ok && syserr.Err == syscall.ECONNREFUSED { 454 time.Sleep(100 * time.Millisecond) 455 continue 456 } 457 } 458 return err 459 } 460 return c.Close() 461 } 462 }