Parcourir la source

Change to use c.Assert() instead of result.Assert()

Fix delete containers and make sure it prints errors correctly.
Rename Result.Fails to Result.Assert()
Create a constant for the default expected.

Signed-off-by: Daniel Nephin <dnephin@docker.com>
Daniel Nephin il y a 9 ans
Parent
commit
243885808f

+ 4 - 4
integration-cli/docker_api_test.go

@@ -90,11 +90,11 @@ func (s *DockerSuite) TestApiDockerApiVersion(c *check.C) {
 
 
 	// Test using the env var first
 	// Test using the env var first
 	result := icmd.RunCmd(icmd.Cmd{
 	result := icmd.RunCmd(icmd.Cmd{
-		Command: binaryWithArgs([]string{"-H", server.URL[7:], "version"}),
-		Env:     []string{"DOCKER_API_VERSION=xxx"},
+		Command: binaryWithArgs("-H="+server.URL[7:], "version"),
+		Env:     appendBaseEnv(false, "DOCKER_API_VERSION=xxx"),
 	})
 	})
-	result.Assert(c, icmd.Expected{Out: "API version:  xxx", ExitCode: 1})
-	c.Assert(svrVersion, check.Equals, "/vxxx/version")
+	c.Assert(result, icmd.Matches, icmd.Expected{Out: "API version:  xxx", ExitCode: 1})
+	c.Assert(svrVersion, check.Equals, "/vxxx/version", check.Commentf("%s", result.Compare(icmd.Success)))
 }
 }
 
 
 func (s *DockerSuite) TestApiErrorJSON(c *check.C) {
 func (s *DockerSuite) TestApiErrorJSON(c *check.C) {

+ 1 - 1
integration-cli/docker_cli_attach_test.go

@@ -163,7 +163,7 @@ func (s *DockerSuite) TestAttachPausedContainer(c *check.C) {
 	dockerCmd(c, "pause", "test")
 	dockerCmd(c, "pause", "test")
 
 
 	result := dockerCmdWithResult("attach", "test")
 	result := dockerCmdWithResult("attach", "test")
-	result.Assert(c, icmd.Expected{
+	c.Assert(result, icmd.Matches, icmd.Expected{
 		Error:    "exit status 1",
 		Error:    "exit status 1",
 		ExitCode: 1,
 		ExitCode: 1,
 		Err:      "You cannot attach to a paused container, unpause it first",
 		Err:      "You cannot attach to a paused container, unpause it first",

+ 1 - 1
integration-cli/docker_cli_build_test.go

@@ -5065,7 +5065,7 @@ func (s *DockerSuite) TestBuildDockerfileOutsideContext(c *check.C) {
 		filepath.Join(ctx, "dockerfile2"),
 		filepath.Join(ctx, "dockerfile2"),
 	} {
 	} {
 		result := dockerCmdWithResult("build", "-t", name, "--no-cache", "-f", dockerfilePath, ".")
 		result := dockerCmdWithResult("build", "-t", name, "--no-cache", "-f", dockerfilePath, ".")
-		result.Assert(c, icmd.Expected{
+		c.Assert(result, icmd.Matches, icmd.Expected{
 			Err:      "must be within the build context",
 			Err:      "must be within the build context",
 			ExitCode: 1,
 			ExitCode: 1,
 		})
 		})

+ 2 - 2
integration-cli/docker_cli_daemon_test.go

@@ -910,7 +910,7 @@ func (s *DockerDaemonSuite) TestDaemonDefaultNetworkInvalidClusterConfig(c *chec
 
 
 	// Start daemon with docker0 bridge
 	// Start daemon with docker0 bridge
 	result := icmd.RunCommand("ifconfig", defaultNetworkBridge)
 	result := icmd.RunCommand("ifconfig", defaultNetworkBridge)
-	result.Assert(c, icmd.Expected{})
+	c.Assert(result, icmd.Matches, icmd.Success)
 
 
 	err = d.Restart(fmt.Sprintf("--cluster-store=%s", discoveryBackend))
 	err = d.Restart(fmt.Sprintf("--cluster-store=%s", discoveryBackend))
 	c.Assert(err, checker.IsNil)
 	c.Assert(err, checker.IsNil)
@@ -2251,7 +2251,7 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithUnpausedRunningContainer(t *che
 		ctrBinary,
 		ctrBinary,
 		"--address", "unix:///var/run/docker/libcontainerd/docker-containerd.sock",
 		"--address", "unix:///var/run/docker/libcontainerd/docker-containerd.sock",
 		"containers", "resume", cid)
 		"containers", "resume", cid)
-	result.Assert(t, icmd.Expected{})
+	t.Assert(result, icmd.Matches, icmd.Success)
 
 
 	// Give time to containerd to process the command if we don't
 	// Give time to containerd to process the command if we don't
 	// the resume event might be received after we do the inspect
 	// the resume event might be received after we do the inspect

+ 2 - 2
integration-cli/docker_cli_events_test.go

@@ -63,7 +63,7 @@ func (s *DockerSuite) TestEventsUntag(c *check.C) {
 		Command: []string{dockerBinary, "events", "--since=1"},
 		Command: []string{dockerBinary, "events", "--since=1"},
 		Timeout: time.Millisecond * 2500,
 		Timeout: time.Millisecond * 2500,
 	})
 	})
-	result.Assert(c, icmd.Expected{Timeout: true})
+	c.Assert(result, icmd.Matches, icmd.Expected{Timeout: true})
 
 
 	events := strings.Split(result.Stdout(), "\n")
 	events := strings.Split(result.Stdout(), "\n")
 	nEvents := len(events)
 	nEvents := len(events)
@@ -280,7 +280,7 @@ func (s *DockerSuite) TestEventsImageLoad(c *check.C) {
 	dockerCmd(c, "load", "-i", "saveimg.tar")
 	dockerCmd(c, "load", "-i", "saveimg.tar")
 
 
 	result := icmd.RunCommand("rm", "-rf", "saveimg.tar")
 	result := icmd.RunCommand("rm", "-rf", "saveimg.tar")
-	result.Assert(c, icmd.Expected{})
+	c.Assert(result, icmd.Matches, icmd.Success)
 
 
 	out, _ = dockerCmd(c, "images", "-q", "--no-trunc", myImageName)
 	out, _ = dockerCmd(c, "images", "-q", "--no-trunc", myImageName)
 	imageID := strings.TrimSpace(out)
 	imageID := strings.TrimSpace(out)

+ 1 - 1
integration-cli/docker_cli_exec_test.go

@@ -124,7 +124,7 @@ func (s *DockerSuite) TestExecExitStatus(c *check.C) {
 	runSleepingContainer(c, "-d", "--name", "top")
 	runSleepingContainer(c, "-d", "--name", "top")
 
 
 	result := icmd.RunCommand(dockerBinary, "exec", "top", "sh", "-c", "exit 23")
 	result := icmd.RunCommand(dockerBinary, "exec", "top", "sh", "-c", "exit 23")
-	result.Assert(c, icmd.Expected{ExitCode: 23, Error: "exit status 23"})
+	c.Assert(result, icmd.Matches, icmd.Expected{ExitCode: 23, Error: "exit status 23"})
 }
 }
 
 
 func (s *DockerSuite) TestExecPausedContainer(c *check.C) {
 func (s *DockerSuite) TestExecPausedContainer(c *check.C) {

+ 3 - 3
integration-cli/docker_cli_network_unix_test.go

@@ -479,7 +479,7 @@ func (s *DockerSuite) TestDockerNetworkInspectWithID(c *check.C) {
 
 
 func (s *DockerSuite) TestDockerInspectMultipleNetwork(c *check.C) {
 func (s *DockerSuite) TestDockerInspectMultipleNetwork(c *check.C) {
 	result := dockerCmdWithResult("network", "inspect", "host", "none")
 	result := dockerCmdWithResult("network", "inspect", "host", "none")
-	result.Assert(c, icmd.Expected{})
+	c.Assert(result, icmd.Matches, icmd.Success)
 
 
 	networkResources := []types.NetworkResource{}
 	networkResources := []types.NetworkResource{}
 	err := json.Unmarshal([]byte(result.Stdout()), &networkResources)
 	err := json.Unmarshal([]byte(result.Stdout()), &networkResources)
@@ -488,7 +488,7 @@ func (s *DockerSuite) TestDockerInspectMultipleNetwork(c *check.C) {
 
 
 	// Should print an error, return an exitCode 1 *but* should print the host network
 	// Should print an error, return an exitCode 1 *but* should print the host network
 	result = dockerCmdWithResult("network", "inspect", "host", "nonexistent")
 	result = dockerCmdWithResult("network", "inspect", "host", "nonexistent")
-	result.Assert(c, icmd.Expected{
+	c.Assert(result, icmd.Matches, icmd.Expected{
 		ExitCode: 1,
 		ExitCode: 1,
 		Err:      "Error: No such network: nonexistent",
 		Err:      "Error: No such network: nonexistent",
 		Out:      "host",
 		Out:      "host",
@@ -500,7 +500,7 @@ func (s *DockerSuite) TestDockerInspectMultipleNetwork(c *check.C) {
 
 
 	// Should print an error and return an exitCode, nothing else
 	// Should print an error and return an exitCode, nothing else
 	result = dockerCmdWithResult("network", "inspect", "nonexistent")
 	result = dockerCmdWithResult("network", "inspect", "nonexistent")
-	result.Assert(c, icmd.Expected{
+	c.Assert(result, icmd.Matches, icmd.Expected{
 		ExitCode: 1,
 		ExitCode: 1,
 		Err:      "Error: No such network: nonexistent",
 		Err:      "Error: No such network: nonexistent",
 		Out:      "[]",
 		Out:      "[]",

+ 1 - 1
integration-cli/docker_cli_ps_test.go

@@ -206,7 +206,7 @@ func (s *DockerSuite) TestPsListContainersFilterStatus(c *check.C) {
 	c.Assert(containerOut, checker.Equals, secondID)
 	c.Assert(containerOut, checker.Equals, secondID)
 
 
 	result := dockerCmdWithTimeout(time.Second*60, "ps", "-a", "-q", "--filter=status=rubbish")
 	result := dockerCmdWithTimeout(time.Second*60, "ps", "-a", "-q", "--filter=status=rubbish")
-	result.Assert(c, icmd.Expected{
+	c.Assert(result, icmd.Matches, icmd.Expected{
 		ExitCode: 1,
 		ExitCode: 1,
 		Err:      "Unrecognised filter value for status",
 		Err:      "Unrecognised filter value for status",
 	})
 	})

+ 1 - 1
integration-cli/docker_cli_rename_test.go

@@ -63,7 +63,7 @@ func (s *DockerSuite) TestRenameCheckNames(c *check.C) {
 	c.Assert(name, checker.Equals, "/"+newName, check.Commentf("Failed to rename container %s", name))
 	c.Assert(name, checker.Equals, "/"+newName, check.Commentf("Failed to rename container %s", name))
 
 
 	result := dockerCmdWithResult("inspect", "-f={{.Name}}", "first_name")
 	result := dockerCmdWithResult("inspect", "-f={{.Name}}", "first_name")
-	result.Assert(c, icmd.Expected{
+	c.Assert(result, icmd.Matches, icmd.Expected{
 		ExitCode: 1,
 		ExitCode: 1,
 		Err:      "No such container, image or task: first_name",
 		Err:      "No such container, image or task: first_name",
 	})
 	})

+ 2 - 2
integration-cli/docker_cli_run_test.go

@@ -1864,11 +1864,11 @@ func (s *DockerSuite) TestRunInteractiveWithRestartPolicy(c *check.C) {
 	})
 	})
 	c.Assert(result.Error, checker.IsNil)
 	c.Assert(result.Error, checker.IsNil)
 	defer func() {
 	defer func() {
-		dockerCmdWithResult("stop", name).Assert(c, icmd.Expected{})
+		dockerCmdWithResult("stop", name).Assert(c, icmd.Success)
 	}()
 	}()
 
 
 	result = icmd.WaitOnCmd(10*time.Second, result)
 	result = icmd.WaitOnCmd(10*time.Second, result)
-	result.Assert(c, icmd.Expected{ExitCode: 11})
+	c.Assert(result, icmd.Matches, icmd.Expected{ExitCode: 11})
 }
 }
 
 
 // Test for #2267
 // Test for #2267

+ 1 - 1
integration-cli/docker_cli_volume_test.go

@@ -57,7 +57,7 @@ func (s *DockerSuite) TestVolumeCliInspectMulti(c *check.C) {
 	dockerCmd(c, "volume", "create", "--name", "not-shown")
 	dockerCmd(c, "volume", "create", "--name", "not-shown")
 
 
 	result := dockerCmdWithResult("volume", "inspect", "--format={{ .Name }}", "test1", "test2", "doesntexist", "not-shown")
 	result := dockerCmdWithResult("volume", "inspect", "--format={{ .Name }}", "test1", "test2", "doesntexist", "not-shown")
-	result.Assert(c, icmd.Expected{
+	c.Assert(result, icmd.Matches, icmd.Expected{
 		ExitCode: 1,
 		ExitCode: 1,
 		Err:      "No such volume: doesntexist",
 		Err:      "No such volume: doesntexist",
 	})
 	})

+ 3 - 3
integration-cli/docker_experimental_network_test.go

@@ -403,7 +403,7 @@ func (s *DockerSuite) TestDockerNetworkMacVlanBridgeInternalMode(c *check.C) {
 
 
 	// access outside of the network should fail
 	// access outside of the network should fail
 	result := dockerCmdWithTimeout(time.Second, "exec", "first", "ping", "-c", "1", "-w", "1", "8.8.8.8")
 	result := dockerCmdWithTimeout(time.Second, "exec", "first", "ping", "-c", "1", "-w", "1", "8.8.8.8")
-	result.Assert(c, icmd.Expected{Timeout: true})
+	c.Assert(result, icmd.Matches, icmd.Expected{Timeout: true})
 
 
 	// intra-network communications should succeed
 	// intra-network communications should succeed
 	_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
 	_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
@@ -443,7 +443,7 @@ func (s *DockerSuite) TestDockerNetworkIpvlanL2InternalMode(c *check.C) {
 
 
 	// access outside of the network should fail
 	// access outside of the network should fail
 	result := dockerCmdWithTimeout(time.Second, "exec", "first", "ping", "-c", "1", "-w", "1", "8.8.8.8")
 	result := dockerCmdWithTimeout(time.Second, "exec", "first", "ping", "-c", "1", "-w", "1", "8.8.8.8")
-	c.Assert(result.Error, check.NotNil)
+	c.Assert(result, icmd.Matches, icmd.Expected{Timeout: true})
 	// intra-network communications should succeed
 	// intra-network communications should succeed
 	_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
 	_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
 	c.Assert(err, check.IsNil)
 	c.Assert(err, check.IsNil)
@@ -484,7 +484,7 @@ func (s *DockerSuite) TestDockerNetworkIpvlanL3InternalMode(c *check.C) {
 
 
 	// access outside of the network should fail
 	// access outside of the network should fail
 	result := dockerCmdWithTimeout(time.Second, "exec", "first", "ping", "-c", "1", "-w", "1", "8.8.8.8")
 	result := dockerCmdWithTimeout(time.Second, "exec", "first", "ping", "-c", "1", "-w", "1", "8.8.8.8")
-	c.Assert(result.Error, check.NotNil)
+	c.Assert(result, icmd.Matches, icmd.Expected{Timeout: true})
 	// intra-network communications should succeed
 	// intra-network communications should succeed
 	_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
 	_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
 	c.Assert(err, check.IsNil)
 	c.Assert(err, check.IsNil)

+ 17 - 14
integration-cli/docker_utils.go

@@ -228,8 +228,9 @@ func readBody(b io.ReadCloser) ([]byte, error) {
 	return ioutil.ReadAll(b)
 	return ioutil.ReadAll(b)
 }
 }
 
 
-func deleteContainer(container string) error {
-	return icmd.RunCommand(dockerBinary, "rm", "-fv", container).Error
+func deleteContainer(container ...string) error {
+	result := icmd.RunCommand(dockerBinary, append([]string{"rm", "-fv"}, container...)...)
+	return result.Compare(icmd.Success)
 }
 }
 
 
 func getAllContainers() (string, error) {
 func getAllContainers() (string, error) {
@@ -248,13 +249,15 @@ func deleteAllContainers() error {
 		fmt.Println(containers)
 		fmt.Println(containers)
 		return err
 		return err
 	}
 	}
+	if containers == "" {
+		return nil
+	}
 
 
-	if containers != "" {
-		if err = deleteContainer(containers); err != nil {
-			return err
-		}
+	err = deleteContainer(strings.Split(strings.TrimSpace(containers), "\n")...)
+	if err != nil {
+		fmt.Println(err.Error())
 	}
 	}
-	return nil
+	return err
 }
 }
 
 
 func deleteAllNetworks() error {
 func deleteAllNetworks() error {
@@ -440,7 +443,7 @@ func dockerCmdWithError(args ...string) (string, int, error) {
 	}
 	}
 	result := icmd.RunCommand(dockerBinary, args...)
 	result := icmd.RunCommand(dockerBinary, args...)
 	if result.Error != nil {
 	if result.Error != nil {
-		return result.Combined(), result.ExitCode, fmt.Errorf(result.Fails(icmd.Expected{}))
+		return result.Combined(), result.ExitCode, result.Compare(icmd.Success)
 	}
 	}
 	return result.Combined(), result.ExitCode, result.Error
 	return result.Combined(), result.ExitCode, result.Error
 }
 }
@@ -453,7 +456,7 @@ func dockerCmdWithStdoutStderr(c *check.C, args ...string) (string, string, int)
 	result := icmd.RunCommand(dockerBinary, args...)
 	result := icmd.RunCommand(dockerBinary, args...)
 	// TODO: why is c ever nil?
 	// TODO: why is c ever nil?
 	if c != nil {
 	if c != nil {
-		result.Assert(c, icmd.Expected{})
+		c.Assert(result, icmd.Matches, icmd.Success)
 	}
 	}
 	return result.Stdout(), result.Stderr(), result.ExitCode
 	return result.Stdout(), result.Stderr(), result.ExitCode
 }
 }
@@ -463,7 +466,7 @@ func dockerCmd(c *check.C, args ...string) (string, int) {
 		c.Fatalf(err.Error())
 		c.Fatalf(err.Error())
 	}
 	}
 	result := icmd.RunCommand(dockerBinary, args...)
 	result := icmd.RunCommand(dockerBinary, args...)
-	result.Assert(c, icmd.Expected{})
+	c.Assert(result, icmd.Matches, icmd.Success)
 	return result.Combined(), result.ExitCode
 	return result.Combined(), result.ExitCode
 }
 }
 
 
@@ -471,7 +474,7 @@ func dockerCmdWithResult(args ...string) *icmd.Result {
 	return icmd.RunCommand(dockerBinary, args...)
 	return icmd.RunCommand(dockerBinary, args...)
 }
 }
 
 
-func binaryWithArgs(args []string) []string {
+func binaryWithArgs(args ...string) []string {
 	return append([]string{dockerBinary}, args...)
 	return append([]string{dockerBinary}, args...)
 }
 }
 
 
@@ -480,7 +483,7 @@ func dockerCmdWithTimeout(timeout time.Duration, args ...string) *icmd.Result {
 	if err := validateArgs(args...); err != nil {
 	if err := validateArgs(args...); err != nil {
 		return &icmd.Result{Error: err}
 		return &icmd.Result{Error: err}
 	}
 	}
-	return icmd.RunCmd(icmd.Cmd{Command: binaryWithArgs(args), Timeout: timeout})
+	return icmd.RunCmd(icmd.Cmd{Command: binaryWithArgs(args...), Timeout: timeout})
 }
 }
 
 
 // execute a docker command in a directory
 // execute a docker command in a directory
@@ -488,7 +491,7 @@ func dockerCmdInDir(c *check.C, path string, args ...string) (string, int, error
 	if err := validateArgs(args...); err != nil {
 	if err := validateArgs(args...); err != nil {
 		c.Fatalf(err.Error())
 		c.Fatalf(err.Error())
 	}
 	}
-	result := icmd.RunCmd(icmd.Cmd{Command: binaryWithArgs(args), Dir: path})
+	result := icmd.RunCmd(icmd.Cmd{Command: binaryWithArgs(args...), Dir: path})
 	return result.Combined(), result.ExitCode, result.Error
 	return result.Combined(), result.ExitCode, result.Error
 }
 }
 
 
@@ -498,7 +501,7 @@ func dockerCmdInDirWithTimeout(timeout time.Duration, path string, args ...strin
 		return &icmd.Result{Error: err}
 		return &icmd.Result{Error: err}
 	}
 	}
 	return icmd.RunCmd(icmd.Cmd{
 	return icmd.RunCmd(icmd.Cmd{
-		Command: binaryWithArgs(args),
+		Command: binaryWithArgs(args...),
 		Timeout: timeout,
 		Timeout: timeout,
 		Dir:     path,
 		Dir:     path,
 	})
 	})

+ 46 - 10
pkg/integration/cmd/command.go

@@ -10,6 +10,8 @@ import (
 	"strings"
 	"strings"
 	"syscall"
 	"syscall"
 	"time"
 	"time"
+
+	"github.com/go-check/check"
 )
 )
 
 
 type testingT interface {
 type testingT interface {
@@ -61,17 +63,18 @@ type Result struct {
 // Assert compares the Result against the Expected struct, and fails the test if
 // Assert compares the Result against the Expected struct, and fails the test if
 // any of the expcetations are not met.
 // any of the expcetations are not met.
 func (r *Result) Assert(t testingT, exp Expected) {
 func (r *Result) Assert(t testingT, exp Expected) {
-	fails := r.Fails(exp)
-	if fails == "" {
+	err := r.Compare(exp)
+	if err == nil {
 		return
 		return
 	}
 	}
 
 
 	_, file, line, _ := runtime.Caller(1)
 	_, file, line, _ := runtime.Caller(1)
-	t.Fatalf("at %s:%d\n%s", filepath.Base(file), line, fails)
+	t.Fatalf("at %s:%d\n%s", filepath.Base(file), line, err.Error())
 }
 }
 
 
-// Fails returns a formatted string which reports on any failed expectations
-func (r *Result) Fails(exp Expected) string {
+// Compare returns an formatted error with the command, stdout, stderr, exit
+// code, and any failed expectations
+func (r *Result) Compare(exp Expected) error {
 	errors := []string{}
 	errors := []string{}
 	add := func(format string, args ...interface{}) {
 	add := func(format string, args ...interface{}) {
 		errors = append(errors, fmt.Sprintf(format, args...))
 		errors = append(errors, fmt.Sprintf(format, args...))
@@ -107,9 +110,9 @@ func (r *Result) Fails(exp Expected) string {
 	}
 	}
 
 
 	if len(errors) == 0 {
 	if len(errors) == 0 {
-		return ""
+		return nil
 	}
 	}
-	return fmt.Sprintf("%s\nFailures:\n%s\n", r, strings.Join(errors, "\n"))
+	return fmt.Errorf("%s\nFailures:\n%s\n", r, strings.Join(errors, "\n"))
 }
 }
 
 
 func matchOutput(expected string, actual string) bool {
 func matchOutput(expected string, actual string) bool {
@@ -151,6 +154,9 @@ type Expected struct {
 	Err      string
 	Err      string
 }
 }
 
 
+// Success is the default expected result
+var Success = Expected{}
+
 // Stdout returns the stdout of the process as a string
 // Stdout returns the stdout of the process as a string
 func (r *Result) Stdout() string {
 func (r *Result) Stdout() string {
 	return r.outBuffer.String()
 	return r.outBuffer.String()
@@ -175,9 +181,39 @@ func (r *Result) SetExitError(err error) {
 	r.ExitCode = ProcessExitCode(err)
 	r.ExitCode = ProcessExitCode(err)
 }
 }
 
 
-// Cmd is a command to run. One of Command or CommandArgs can be used to set the
-// comand line. Command will be paased to shlex and split into a string slice.
-// CommandArgs is an already split command line.
+type matches struct{}
+
+// Info returns the CheckerInfo
+func (m *matches) Info() *check.CheckerInfo {
+	return &check.CheckerInfo{
+		Name:   "CommandMatches",
+		Params: []string{"result", "expected"},
+	}
+}
+
+// Check compares a result against the expected
+func (m *matches) Check(params []interface{}, names []string) (bool, string) {
+	result, ok := params[0].(*Result)
+	if !ok {
+		return false, fmt.Sprintf("result must be a *Result, not %T", params[0])
+	}
+	expected, ok := params[1].(Expected)
+	if !ok {
+		return false, fmt.Sprintf("expected must be an Expected, not %T", params[1])
+	}
+
+	err := result.Compare(expected)
+	if err == nil {
+		return true, ""
+	}
+	return false, err.Error()
+}
+
+// Matches is a gocheck.Checker for comparing a Result against an Expected
+var Matches = &matches{}
+
+// Cmd contains the arguments and options for a process to run as part of a test
+// suite.
 type Cmd struct {
 type Cmd struct {
 	Command []string
 	Command []string
 	Timeout time.Duration
 	Timeout time.Duration