hivesim

package
v0.0.0-...-7abf313 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 7, 2024 License: GPL-3.0 Imports: 25 Imported by: 22

Documentation

Overview

Package hivesim is a Go wrapper for the Hive Simulation API. You can use this package to write simulations for Hive in Go.

The hivesim API wrapper contains a few components that are important for interacting with the hive simulation API:

  • test suites
  • test cases
  • client(s)
  • networks (if the simulation calls for a more complex network topology

Test Suites and Test Cases

A test suite represents a single run of a simulator. A test suite can contain several test cases. Test cases represent an individual test against one or more clients.

In order to execute a test against a client, it is necessary to create a test suite first and add one or more test cases to that suite. This can be done by creating `Suite` object, as such:

suite := hivesim.Suite{
	Name:        	"MyTest",
	Description: 	"This simulation test does XYZ.",
}

The `Suite` has an additional field, `Tests`, which represents all the test cases to be executed by the test suite. Test cases can be added to the suite using the `Add()` method.

A test case can be represented in either of the following formats:

// TestSpec is the description of a test. Using this test type doesn't launch any clients by default.
// To interact with clients, you can launch them using the t.Client method:
//
//    c := t.Client()
//    c.RPC().Call(...)
//
// or run a subtest using t.RunClientTest():
//
//    t.RunClientTest(hivesim.ClientTestSpec{...})
//
type TestSpec struct {
	Name        string
	Description string
	Run         func(*T) // this is the function that will be executed by the test suite
}

// ClientTestSpec is a test against a single client. You can either put this in your suite
// directly, or launch it using RunClient or RunAllClients from another test.
//
// When used as a test in a suite, the test runs against all available client types.
//
// If the Name of the test includes "CLIENT", it is replaced by the client name being tested.
type ClientTestSpec struct {
	Name        string
	Description string
	Parameters  Params
	Files       map[string]string
	Run         func(*T, *Client) // this is the function that will be executed by the test suite
}

It is also possible to add a test case to the test suite without using the two above structs, so long as it implements the following interface:

type AnyTest interface {
	runTest(*Simulation, SuiteID) error
}

Creating a Test Run

A test run can make use of the resources granted to it by the `T` object at runtime. `T` represents a running test and behaves a lot like testing.T, but has some additional methods for launching clients.

type T struct {
	Sim     *Simulation
	TestID  TestID
	SuiteID SuiteID
	mu      sync.Mutex
	result  TestResult
}

The `T` object can start a client using the `StartClient()` method. `StartClient()` returns an object `Client` with information about the client container. `Client` also offers two methods: `EnodeURL()`, which returns the enode URL of the client, and `RPC()`, which returns an RPC client connected to the client's RPC server.

`T` can also run a test against a client using any of the `Run__()` methods. It can also pipe logs and test failures through to the simulation log file, among other methods.

The `Sim` field (which is a pointer to an instance of `Simulation`) in the `T` object is especially useful as it provides several methods for communicating with the hive simulation API, such as:

  • starting / ending test suites and tests
  • starting / stopping / getting information about a client
  • creating / removing networks
  • connecting / disconnecting containers to/from a network
  • getting the IP address of a container on a specific network

Running a Test Suite

It is possible to call either `RunSuite()` or `MustRunSuite()` on the `Suite`, the only difference being the error handling.

`RunSuite()` will run all tests in the `Suite`, returning an error upon failure.

`MustRunSuite()` will run all tests in the `Suite`, exiting the process if there is a problem executing
a test.

Both functions take a pointer to an instance of `Simulation` as well as a `Suite`.

To get an instance of `Simulation`, call the constructor function `New()`. This will look up the hive host server URI and return an instance of `Simulation` that will be able to access the running hive host server.

Index

Constants

This section is empty.

Variables

View Source
var ENGINEAPI_JWT_SECRET = [32]byte{0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x65}

This is the static secret configured for all execution-layer clients.

Functions

func MustRun

func MustRun(host *Simulation, suites ...Suite)

MustRun executes all given test suites, exiting the process if there is a problem reaching the simulation API.

func MustRunSuite

func MustRunSuite(host *Simulation, suite Suite)

MustRunSuite runs the given suite, exiting the process if there is a problem reaching the simulation API.

func NewDocsCollector

func NewDocsCollector() *docsCollector

NewDocsCollector creates a new docs collector object. Tries to parse the simulator name and also the output directory from the environment variables.

func Run

func Run(host *Simulation, suites ...Suite) error

Run executes all given test suites.

func RunSuite

func RunSuite(host *Simulation, suite Suite) error

RunSuite runs all tests in a suite.

Types

type AnyTest

type AnyTest interface {
	// contains filtered or unexported methods
}

AnyTest is a TestSpec or ClientTestSpec.

type Client

type Client struct {
	Type      string
	Container string
	IP        net.IP
	// contains filtered or unexported fields
}

Client represents a running client.

func (*Client) EngineAPI

func (c *Client) EngineAPI() *rpc.Client

EngineAPI returns an RPC client connected to an execution-layer client's engine API server.

func (*Client) EnodeURL

func (c *Client) EnodeURL() (string, error)

EnodeURL returns the default peer-to-peer endpoint of the client.

func (*Client) EnodeURLNetwork

func (c *Client) EnodeURLNetwork(network string) (string, error)

EnodeURL returns the peer-to-peer endpoint of the client on a specific network.

func (*Client) Exec

func (c *Client) Exec(command ...string) (*ExecInfo, error)

Exec runs a script in the client container.

func (*Client) Pause

func (c *Client) Pause() error

Pauses the client container.

func (*Client) RPC

func (c *Client) RPC() *rpc.Client

RPC returns an RPC client connected to the client's RPC server.

func (*Client) Unpause

func (c *Client) Unpause() error

Unpauses the client container.

type ClientDefinition

type ClientDefinition struct {
	Name    string         `json:"name"`
	Version string         `json:"version"`
	Meta    ClientMetadata `json:"meta"`
}

ClientDefinition is served by the /clients API endpoint to list the available clients

func (*ClientDefinition) HasRole

func (m *ClientDefinition) HasRole(role string) bool

HasRole reports whether the client has the given role.

type ClientMetadata

type ClientMetadata struct {
	Roles []string `yaml:"roles" json:"roles"`
}

ClientMetadata is part of the ClientDefinition and lists metadata

type ClientTestSpec

type ClientTestSpec struct {
	// These fields are displayed in the UI. Be sure to add
	// a meaningful description here.
	Name        string // Name is the unique identifier for the test [Mandatory]
	DisplayName string // Display name for the test (Name will be used if unset) [Optional]
	Description string // Description of the test (if empty, test won't appear in documentation) [Optional]
	Category    string // Category of the test [Optional]

	// If AlwaysRun is true, the test will run even if Name does not match the test
	// pattern. This option is useful for tests that launch a client instance and
	// then perform further tests against it.
	AlwaysRun bool

	// This filters client types by role.
	// If no role is specified, the test runs for all available client types.
	Role string

	// Parameters and Files are launch options for client instances.
	Parameters Params
	Files      map[string]string

	// The Run function is invoked when the test executes.
	Run func(*T, *Client)
}

ClientTestSpec is a test against a single client. You can either put this in your suite directly, or launch it using RunClient or RunAllClients from another test.

When used as a test in a suite, the test runs against all available client types, with the specified Role. If no Role is specified, the test runs with all available clients.

If the Name of the test includes "CLIENT", it is replaced by the client name being tested.

type ExecInfo

type ExecInfo struct {
	Stdout   string `json:"stdout"`
	Stderr   string `json:"stderr"`
	ExitCode int    `json:"exitCode"`
}

ExecInfo is the result of running a command in a client container.

type FileWriter

type FileWriter interface {
	CreateWriter(path string) (io.WriteCloser, error)
}

FileWriter interface. Used to create the markdown files.

func NewFileWriter

func NewFileWriter(basePath string) FileWriter

NewFileWriter creates a new file writer with the given base path.

type Params

type Params map[string]string

Params contains client launch parameters. This exists because tests usually want to define common parameters as a global variable and then customize them for specific clients.

func (Params) Copy

func (p Params) Copy() Params

Copy returns a copy of the parameters.

func (Params) Set

func (p Params) Set(key, value string) Params

Set returns a copy of the parameters with 'key' set to 'value'.

type Simulation

type Simulation struct {
	// contains filtered or unexported fields
}

Simulation wraps the simulation HTTP API provided by hive.

func New

func New() *Simulation

New looks up the hive host URI using the HIVE_SIMULATOR environment variable and connects to it. It will panic if HIVE_SIMULATOR is not set. If HIVE_DOCS_MODE is set to "true", it will inhibit most of the functionality in order to simplify execution for documentation generation.

func NewAt

func NewAt(url string) *Simulation

NewAt creates a simulation connected to the given API endpoint. You'll will rarely need to use this. In simulations launched by hive, use New() instead.

func (*Simulation) ClientEnodeURL

func (sim *Simulation) ClientEnodeURL(testSuite SuiteID, test TestID, node string) (string, error)

ClientEnodeURL returns the enode URL of a running client.

func (*Simulation) ClientEnodeURLNetwork

func (sim *Simulation) ClientEnodeURLNetwork(testSuite SuiteID, test TestID, node string, network string) (string, error)

ClientEnodeURLCustomNetwork returns the enode URL of a running client in a custom network.

func (*Simulation) ClientExec

func (sim *Simulation) ClientExec(testSuite SuiteID, test TestID, nodeid string, cmd []string) (*ExecInfo, error)

ClientExec runs a command in a running client.

func (*Simulation) ClientTypes

func (sim *Simulation) ClientTypes() ([]*ClientDefinition, error)

ClientTypes returns all client types available to this simulator run. This depends on both the available client set and the command line filters.

func (*Simulation) CollectTestsOnly

func (sim *Simulation) CollectTestsOnly() bool

CollectTestsOnly returns true if the simulation is running in collect-tests-only mode.

func (*Simulation) ConnectContainer

func (sim *Simulation) ConnectContainer(testSuite SuiteID, network, containerID string) error

ConnectContainer sends a request to the hive server to connect the given container to the given network.

func (*Simulation) ContainerNetworkIP

func (sim *Simulation) ContainerNetworkIP(testSuite SuiteID, network, containerID string) (string, error)

ContainerNetworkIP returns the IP address of a container on the given network. If the container ID is "simulation", it returns the IP address of the simulator container.

func (*Simulation) CreateNetwork

func (sim *Simulation) CreateNetwork(testSuite SuiteID, networkName string) error

CreateNetwork sends a request to the hive server to create a docker network by the given name.

func (*Simulation) DisconnectContainer

func (sim *Simulation) DisconnectContainer(testSuite SuiteID, network, containerID string) error

DisconnectContainer sends a request to the hive server to disconnect the given container from the given network.

func (*Simulation) EndSuite

func (sim *Simulation) EndSuite(testSuite SuiteID) error

EndSuite signals the end of a test suite.

func (*Simulation) EndTest

func (sim *Simulation) EndTest(testSuite SuiteID, test TestID, testResult TestResult) error

EndTest finishes the test case, cleaning up everything, logging results, and returning an error if the process could not be completed.

func (*Simulation) PauseClient

func (sim *Simulation) PauseClient(testSuite SuiteID, test TestID, nodeid string) error

PauseClient signals to the host that the node needs to be paused.

func (*Simulation) RemoveNetwork

func (sim *Simulation) RemoveNetwork(testSuite SuiteID, network string) error

RemoveNetwork sends a request to the hive server to remove the given network.

func (*Simulation) SetTestPattern

func (sim *Simulation) SetTestPattern(p string)

SetTestPattern sets the regular expression that enables/skips suites and test cases. This method is provided for use in unit tests. For simulator runs launched by hive, the test pattern is set automatically in New().

func (*Simulation) StartClient

func (sim *Simulation) StartClient(testSuite SuiteID, test TestID, parameters map[string]string, initFiles map[string]string) (string, net.IP, error)

StartClient starts a new node (or other container) with the specified parameters. One parameter must be named CLIENT and should contain one of the client types from GetClientTypes. The input is used as environment variables in the new container. Returns container id and ip.

func (*Simulation) StartClientWithOptions

func (sim *Simulation) StartClientWithOptions(testSuite SuiteID, test TestID, clientType string, options ...StartOption) (string, net.IP, error)

StartClientWithOptions starts a new node (or other container) with specified options. Returns container id and ip.

func (*Simulation) StartSuite

func (sim *Simulation) StartSuite(suite *simapi.TestRequest, simlog string) (SuiteID, error)

StartSuite signals the start of a test suite.

func (*Simulation) StartTest

func (sim *Simulation) StartTest(testSuite SuiteID, test *simapi.TestRequest) (TestID, error)

StartTest starts a new test case, returning the testcase id as a context identifier.

func (*Simulation) StopClient

func (sim *Simulation) StopClient(testSuite SuiteID, test TestID, nodeid string) error

StopClient signals to the host that the node is no longer required.

func (*Simulation) TestPattern

func (sim *Simulation) TestPattern() (suiteExpr string, testNameExpr string)

TestPattern returns the regular expressions used to enable/skip suite and test names.

func (*Simulation) UnpauseClient

func (sim *Simulation) UnpauseClient(testSuite SuiteID, test TestID, nodeid string) error

UnpauseClient signals to the host that the node needs to be unpaused.

type StartOption

type StartOption interface {
	// contains filtered or unexported methods
}

StartOption is a parameter for starting a client.

func Bundle

func Bundle(option ...StartOption) StartOption

Bundle combines start options, e.g. to bundle files together as option.

func WithDynamicFile

func WithDynamicFile(dstPath string, src func() (io.ReadCloser, error)) StartOption

WithDynamicFile adds a file to a client, sourced dynamically from the given src function, called upon usage of the returned StartOption.

A StartOption, and thus the src function, should be reusable and safe to use in parallel. Dynamic files can override static file sources (see WithStaticFiles) and vice-versa.

func WithInitialNetworks

func WithInitialNetworks(networks []string) StartOption

WithInitialNetworks configures networks that the client is initially connected to.

func WithStaticFiles

func WithStaticFiles(initFiles map[string]string) StartOption

WithStaticFiles adds files from the local filesystem to the client. Map: destination file path -> source file path.

type Suite

type Suite struct {
	Name        string // Name is the unique identifier for the suite [Mandatory]
	DisplayName string // Display name for the suite (Name will be used if unset) [Optional]
	Location    string // Documentation output location for the test suite [Optional]
	Category    string // Category of the test suite [Optional]
	Description string // Description of the test suite (if empty, suite won't appear in documentation) [Optional]
	Tests       []AnyTest
}

Suite is the description of a test suite.

func (*Suite) Add

func (s *Suite) Add(test AnyTest) *Suite

Add adds a test to the suite.

type SuiteID

type SuiteID uint32

SuiteID identifies a test suite context.

type T

type T struct {
	// Test case info.
	Sim     *Simulation
	TestID  TestID
	SuiteID SuiteID
	// contains filtered or unexported fields
}

T is a running test. This is a lot like testing.T, but has some additional methods for launching clients.

All test log output (via t.Log, t.Logf) goes to the 'details' section of the test report.

func (*T) Error

func (t *T) Error(values ...interface{})

Error is like testing.T.Error.

func (*T) Errorf

func (t *T) Errorf(format string, values ...interface{})

Errorf is like testing.T.Errorf.

func (*T) Fail

func (t *T) Fail()

Fail signals that the test has failed.

func (*T) FailNow

func (t *T) FailNow()

FailNow signals that the test has failed and exits the test immediately. As with testing.T.FailNow(), this should only be called from the main test goroutine.

func (*T) Failed

func (t *T) Failed() bool

Failed reports whether the test has already failed.

func (*T) Fatal

func (t *T) Fatal(values ...interface{})

Fatal is like testing.T.Fatal. It fails the test immediately.

func (*T) Fatalf

func (t *T) Fatalf(format string, values ...interface{})

Fatalf is like testing.T.Fatalf. It fails the test immediately.

func (*T) Log

func (t *T) Log(values ...interface{})

Log prints to standard output, which goes to the simulation log file.

func (*T) Logf

func (t *T) Logf(format string, values ...interface{})

Logf prints to standard output, which goes to the simulation log file.

func (*T) Run

func (t *T) Run(spec TestSpec)

Run runs a subtest of this test. It waits for the subtest to complete before continuing. It is safe to call this from multiple goroutines concurrently, just be sure to wait for all your tests to finish until returning from the parent test.

func (*T) RunAllClients

func (t *T) RunAllClients(spec ClientTestSpec)

RunAllClients runs the given client test against all available client types. It waits for all subtests to complete.

func (*T) RunClient

func (t *T) RunClient(clientType string, spec ClientTestSpec)

RunClient runs the given client test against a single client type. It waits for the subtest to complete.

func (*T) StartClient

func (t *T) StartClient(clientType string, option ...StartOption) *Client

StartClient starts a client instance. If the client cannot by started, the test fails immediately.

type TestID

type TestID uint32

TestID identifies a test case context.

type TestResult

type TestResult struct {
	Pass    bool   `json:"pass"`
	Details string `json:"details"`
}

TestResult describes the outcome of a test.

type TestSpec

type TestSpec struct {
	// These fields are displayed in the UI. Be sure to add
	// a meaningful description here.
	Name        string // Name is the unique identifier for the test [Mandatory]
	DisplayName string // Display name for the test (Name will be used if unset) [Optional]
	Description string // Description of the test (if empty, test won't appear in documentation) [Optional]
	Category    string // Category of the test [Optional]

	// If AlwaysRun is true, the test will run even if Name does not match the test
	// pattern. This option is useful for tests that launch a client instance and
	// then perform further tests against it.
	AlwaysRun bool

	// The Run function is invoked when the test executes.
	Run func(*T)
}

TestSpec is the description of a test.

Using this test type doesn't launch any clients by default. To interact with clients, you can launch them using the t.Client method:

c := t.Client()
c.RPC().Call(...)

or run a subtest using t.RunClientTest():

t.RunClientTest(hivesim.ClientTestSpec{...})

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL