// Copyright 2017 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // The go-ethereum library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . // Package simulations simulates p2p networks. // A mocker simulates starting and stopping real nodes in a network. package simulations import ( "encoding/json" "net/http" "net/url" "strconv" "sync" "testing" "time" "github.com/ledgerwatch/turbo-geth/p2p/enode" ) func TestMocker(t *testing.T) { //start the simulation HTTP server _, s := testHTTPServer(t) defer s.Close() //create a client client := NewClient(s.URL) //start the network err := client.StartNetwork() if err != nil { t.Fatalf("Could not start test network: %s", err) } //stop the network to terminate defer func() { err = client.StopNetwork() if err != nil { t.Fatalf("Could not stop test network: %s", err) } }() //get the list of available mocker types resp, err := http.Get(s.URL + "/mocker") if err != nil { t.Fatalf("Could not get mocker list: %s", err) } defer resp.Body.Close() if resp.StatusCode != 200 { t.Fatalf("Invalid Status Code received, expected 200, got %d", resp.StatusCode) } //check the list is at least 1 in size var mockerlist []string err = json.NewDecoder(resp.Body).Decode(&mockerlist) if err != nil { t.Fatalf("Error decoding JSON mockerlist: %s", err) } if len(mockerlist) < 1 { t.Fatalf("No mockers available") } nodeCount := 10 var wg sync.WaitGroup events := make(chan *Event, 10) var opts SubscribeOpts sub, err := client.SubscribeNetwork(events, opts) defer sub.Unsubscribe() // wait until all nodes are started and connected // store every node up event in a map (value is irrelevant, mimic Set datatype) nodemap := make(map[enode.ID]bool) nodesComplete := false connCount := 0 wg.Add(1) go func() { defer wg.Done() for connCount < (nodeCount-1)*2 { select { case event := <-events: if isNodeUp(event) { //add the correspondent node ID to the map nodemap[event.Node.Config.ID] = true //this means all nodes got a nodeUp event, so we can continue the test if len(nodemap) == nodeCount { nodesComplete = true } } else if event.Conn != nil && nodesComplete { connCount += 1 } case <-time.After(30 * time.Second): t.Errorf("Timeout waiting for nodes being started up!") return } } }() //take the last element of the mockerlist as the default mocker-type to ensure one is enabled mockertype := mockerlist[len(mockerlist)-1] //still, use hardcoded "probabilistic" one if available ;) for _, m := range mockerlist { if m == "probabilistic" { mockertype = m break } } //start the mocker with nodeCount number of nodes resp, err = http.PostForm(s.URL+"/mocker/start", url.Values{"mocker-type": {mockertype}, "node-count": {strconv.Itoa(nodeCount)}}) if err != nil { t.Fatalf("Could not start mocker: %s", err) } if resp.StatusCode != 200 { t.Fatalf("Invalid Status Code received for starting mocker, expected 200, got %d", resp.StatusCode) } wg.Wait() //check there are nodeCount number of nodes in the network nodesInfo, err := client.GetNodes() if err != nil { t.Fatalf("Could not get nodes list: %s", err) } if len(nodesInfo) != nodeCount { t.Fatalf("Expected %d number of nodes, got: %d", nodeCount, len(nodesInfo)) } //stop the mocker resp, err = http.Post(s.URL+"/mocker/stop", "", nil) if err != nil { t.Fatalf("Could not stop mocker: %s", err) } if resp.StatusCode != 200 { t.Fatalf("Invalid Status Code received for stopping mocker, expected 200, got %d", resp.StatusCode) } //reset the network _, err = http.Post(s.URL+"/reset", "", nil) if err != nil { t.Fatalf("Could not reset network: %s", err) } //now the number of nodes in the network should be zero nodesInfo, err = client.GetNodes() if err != nil { t.Fatalf("Could not get nodes list: %s", err) } if len(nodesInfo) != 0 { t.Fatalf("Expected empty list of nodes, got: %d", len(nodesInfo)) } } func isNodeUp(event *Event) bool { return event.Node != nil && event.Node.Up() }