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 }