github.com/cgcardona/r-subnet-evm@v0.1.5/tests/utils/command.go (about)

     1  // Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package utils
     5  
     6  import (
     7  	"context"
     8  	"encoding/json"
     9  	"fmt"
    10  	"os"
    11  	"path/filepath"
    12  	"strings"
    13  	"time"
    14  
    15  	"github.com/ava-labs/avalanchego/api/health"
    16  	"github.com/ethereum/go-ethereum/log"
    17  	"github.com/go-cmd/cmd"
    18  	"github.com/onsi/ginkgo/v2"
    19  	"github.com/onsi/gomega"
    20  )
    21  
    22  // RunCommand starts the command [bin] with the given [args] and returns the command to the caller
    23  // TODO cmd package mentions we can do this more efficiently with cmd.NewCmdOptions rather than looping
    24  // and calling Status().
    25  func RunCommand(bin string, args ...string) (*cmd.Cmd, error) {
    26  	log.Info("Executing", "cmd", fmt.Sprintf("%s %s", bin, strings.Join(args, " ")))
    27  
    28  	curCmd := cmd.NewCmd(bin, args...)
    29  	_ = curCmd.Start()
    30  
    31  	// to stream outputs
    32  	ticker := time.NewTicker(10 * time.Millisecond)
    33  	go func() {
    34  		prevLine := ""
    35  		for range ticker.C {
    36  			status := curCmd.Status()
    37  			n := len(status.Stdout)
    38  			if n == 0 {
    39  				continue
    40  			}
    41  
    42  			line := status.Stdout[n-1]
    43  			if prevLine != line && line != "" {
    44  				fmt.Println("[streaming output]", line)
    45  			}
    46  
    47  			prevLine = line
    48  		}
    49  	}()
    50  
    51  	return curCmd, nil
    52  }
    53  
    54  func RegisterPingTest() {
    55  	ginkgo.It("ping the network", ginkgo.Label("ping"), func() {
    56  		client := health.NewClient(DefaultLocalNodeURI)
    57  		healthy, err := client.Readiness(context.Background(), nil)
    58  		gomega.Expect(err).Should(gomega.BeNil())
    59  		gomega.Expect(healthy.Healthy).Should(gomega.BeTrue())
    60  	})
    61  }
    62  
    63  // At boot time subnets are created, one for each test suite. This global
    64  // variable has all the subnets IDs that can be used.
    65  //
    66  // One process creates the AvalancheGo node and all the subnets, and these
    67  // subnets IDs are passed to all other processes and stored in this global
    68  // variable
    69  var BlockchainIDs map[string]string
    70  
    71  // Timeout to boot the AvalancheGo node
    72  var bootAvalancheNodeTimeout = 5 * time.Minute
    73  
    74  // Timeout for the health API to check the AvalancheGo is ready
    75  var healthCheckTimeout = 5 * time.Second
    76  
    77  func RegisterNodeRun() {
    78  	// Keep track of the AvalancheGo external bash script, it is null for most
    79  	// processes except the first process that starts AvalancheGo
    80  	var startCmd *cmd.Cmd
    81  
    82  	// Our test suite runs in a separated processes, ginkgo hasI
    83  	// SynchronizedBeforeSuite() which is promised to run once, and its output is
    84  	// passed over to each worker.
    85  	//
    86  	// In here an AvalancheGo node instance is started, and subnets are created for
    87  	// each test case. Each test case has its own subnet, therefore all tests can
    88  	// run in parallel without any issue.
    89  	//
    90  	// This function also compiles all the solidity contracts
    91  	var _ = ginkgo.SynchronizedBeforeSuite(func() []byte {
    92  		ctx, cancel := context.WithTimeout(context.Background(), bootAvalancheNodeTimeout)
    93  		defer cancel()
    94  
    95  		wd, err := os.Getwd()
    96  		gomega.Expect(err).Should(gomega.BeNil())
    97  		log.Info("Starting AvalancheGo node", "wd", wd)
    98  		cmd, err := RunCommand("./scripts/run.sh")
    99  		startCmd = cmd
   100  		gomega.Expect(err).Should(gomega.BeNil())
   101  
   102  		// Assumes that startCmd will launch a node with HTTP Port at [utils.DefaultLocalNodeURI]
   103  		healthClient := health.NewClient(DefaultLocalNodeURI)
   104  		healthy, err := health.AwaitReady(ctx, healthClient, healthCheckTimeout, nil)
   105  		gomega.Expect(err).Should(gomega.BeNil())
   106  		gomega.Expect(healthy).Should(gomega.BeTrue())
   107  		log.Info("AvalancheGo node is healthy")
   108  
   109  		blockchainIds := make(map[string]string)
   110  		files, err := filepath.Glob("./tests/precompile/genesis/*.json")
   111  		gomega.Expect(err).NotTo(gomega.HaveOccurred())
   112  
   113  		for _, file := range files {
   114  			basename := filepath.Base(file)
   115  			index := basename[:len(basename)-5]
   116  			blockchainIds[index] = CreateNewSubnet(ctx, file)
   117  		}
   118  
   119  		blockchainIDsBytes, err := json.Marshal(blockchainIds)
   120  		gomega.Expect(err).NotTo(gomega.HaveOccurred())
   121  		return blockchainIDsBytes
   122  	}, func(data []byte) {
   123  		err := json.Unmarshal(data, &BlockchainIDs)
   124  		gomega.Expect(err).NotTo(gomega.HaveOccurred())
   125  	})
   126  
   127  	// SynchronizedAfterSuite() takes two functions, the first runs after each test suite is done and the second
   128  	// function is executed once when all the tests are done. This function is used
   129  	// to gracefully shutdown the AvalancheGo node.
   130  	var _ = ginkgo.SynchronizedAfterSuite(func() {}, func() {
   131  		gomega.Expect(startCmd).ShouldNot(gomega.BeNil())
   132  		gomega.Expect(startCmd.Stop()).Should(gomega.BeNil())
   133  	})
   134  }