fuse: bazil.org/fuse/fs/fstestutil/spawntest Index | Examples | Files | Directories

package spawntest

import "bazil.org/fuse/fs/fstestutil/spawntest"

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.

Code:

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

Index

Examples

Package Files

spawntest.go

type Control Uses

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

Control an instance of a helper running as a subprocess.

func (*Control) Close Uses

func (c *Control) Close()

Close kills the helper and frees resources.

func (*Control) HTTP Uses

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 Uses

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 Uses

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

Signal send a signal to the helper process.

type Helper Uses

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 Uses

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 Uses

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

Registry keeps track of helpers.

The zero value is ready to use.

func (*Registry) AddFlag Uses

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 Uses

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 Uses

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

PathSynopsis
httpjsonPackage httpjson helps transporting JSON over HTTP.

Package spawntest imports 15 packages (graph). Updated 2020-02-19. Refresh now. Tools for package owners.