spawntest

package
v0.0.0-...-62a210f Latest Latest
Warning

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

Go to latest
Published: Jan 20, 2023 License: BSD-2-Clause, BSD-3-Clause, HPND Imports: 14 Imported by: 0

Documentation

Overview

Package spawntest helps write tests that use subprocesses.

The subprocess runs a HTTP server on a UNIX domain socket, and the test can make HTTP requests to control the behavior of the helper subprocess.

Helpers are identified by names they pass to Registry.Register. This call should be placed in an init function. The test spawns the subprocess by executing the same test binary in a subprocess, passing it a special flag that is recognized by TestMain.

This might get extracted to a standalone repository, if it proves useful enough.

Example
package main

import (
	"context"
	"errors"
	"flag"
	"os"
	"testing"

	"bazil.org/fuse/fs/fstestutil/spawntest"
	"bazil.org/fuse/fs/fstestutil/spawntest/httpjson"
)

var helpers spawntest.Registry

type addRequest struct {
	A uint64
	B uint64
}

type addResult struct {
	X uint64
}

func add(ctx context.Context, req addRequest) (*addResult, error) {
	// In real tests, you'd instruct the helper to interact with the
	// system-under-test on behalf of the unit test process. For
	// brevity, we'll just do the action directly in this example.
	x := req.A + req.B
	if x < req.A {
		return nil, errors.New("overflow")
	}
	r := &addResult{
		X: x,
	}
	return r, nil
}

// The second argument to Register can be any http.Handler. To keep
// state in the helper between calls, you can create a custom type and
// delegate to methods based on http.Request.URL.Path.
var addHelper = helpers.Register("add", httpjson.ServePOST(add))

func name_me_TestAdd(t *testing.T) {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	control := addHelper.Spawn(ctx, t)
	defer control.Close()

	var got addResult
	if err := control.JSON("/").Call(ctx, addRequest{A: 42, B: 13}, &got); err != nil {
		t.Fatalf("calling helper: %v", err)
	}
	if g, e := got.X, uint64(55); g != e {
		t.Errorf("wrong add result: %v != %v", g, e)
	}
}

func name_me_TestMain(m *testing.M) {
	helpers.AddFlag(flag.CommandLine)
	flag.Parse()
	helpers.RunIfNeeded()
	os.Exit(m.Run())
}

func main() {}

// Quiet linters. See https://github.com/dominikh/go-tools/issues/675
var _ = name_me_TestAdd
var _ = name_me_TestMain
Output:

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Control

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

Control an instance of a helper running as a subprocess.

func (*Control) Close

func (c *Control) Close()

Close kills the helper and frees resources.

func (*Control) HTTP

func (c *Control) HTTP() *http.Client

HTTP returns a HTTP client that can be used to communicate with the helper. URLs passed to this helper should not include scheme or host.

func (*Control) JSON

func (c *Control) JSON(path string) *httpjson.Resource

JSON returns a helper to make HTTP requests that pass data as JSON to the resource identified by path. Path should not include scheme or host. Path can be empty to communicate with the root resource.

func (*Control) Signal

func (c *Control) Signal(sig os.Signal) error

Signal send a signal to the helper process.

type Helper

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

Helper is the result of registering a helper. It can be used by tests to spawn the helper.

func (*Helper) Spawn

func (h *Helper) Spawn(ctx context.Context, t testing.TB) *Control

Spawn the helper. All errors will be reported via t.Logf and fatal errors result in t.FailNow. The helper is killed after context cancels.

type Registry

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

Registry keeps track of helpers.

The zero value is ready to use.

func (*Registry) AddFlag

func (r *Registry) AddFlag(f *flag.FlagSet)

AddFlag adds the command-line flag used to communicate between Control and the helper to the flag set. Typically flag.CommandLine is used, and this should be called from TestMain before flag.Parse.

func (*Registry) Register

func (r *Registry) Register(name string, h http.Handler) *Helper

Register a helper in the registry.

This should be called from a top-level variable assignment.

Register will panic if the name is already registered.

func (*Registry) RunIfNeeded

func (r *Registry) RunIfNeeded()

RunIfNeeded passes execution to the helper if the right command-line flag was seen. This should be called from TestMain after flag.Parse. If running as the helper, the call will not return.

Directories

Path Synopsis
Package httpjson helps transporting JSON over HTTP.
Package httpjson helps transporting JSON over HTTP.

Jump to

Keyboard shortcuts

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