x.ref/test: improved documentation and testing.
Change-Id: I12598ee69f82e4af4c16cd3322329ed0768b6d06
diff --git a/lib/exec/doc.go b/lib/exec/doc.go
index 953965a..ce69e22 100644
--- a/lib/exec/doc.go
+++ b/lib/exec/doc.go
@@ -1,13 +1,15 @@
-// Package exec implements simple process creation and rendezvous, including
+// Package exec implements process creation and rendezvous, including
// sharing a secret with, and passing arbitrary configuration to, the newly
-// created process.
+// created process via an anoymous pipe. An anonymous pipe is used since
+// it is the most secure communication channel available.
//
// Once a parent starts a child process it can use WaitForReady to wait
// for the child to reach its 'Ready' state. Operations are provided to wait
-// for the child to terminate, and to terminate the child cleaning up any state
+// for the child to terminate, and to terminate the child, cleaning up any state
// associated with it.
//
// A child process uses the GetChildHandle function to complete the initial
// authentication handshake. The child must call SetReady to indicate that it is
// fully initialized and ready for whatever purpose it is intended to fulfill.
+// This handshake is referred as the 'exec protocol'.
package exec
diff --git a/lib/exec/parent.go b/lib/exec/parent.go
index 326d45b..e85fcbd 100644
--- a/lib/exec/parent.go
+++ b/lib/exec/parent.go
@@ -75,7 +75,9 @@
// UseExecProtocolOpt can be used to control whether parent/child handshake
// protocol is used. WaitForReady will return immediately with an error if
-// this option is set to false.
+// this option is set to false. The defaults behaviour is to assume that
+// the exec protocol is used. If it is not used, then the Start method
+// will not create shared file descriptors to use for the exec protocol.
type UseExecProtocolOpt bool
func (UseExecProtocolOpt) ExecParentHandleOpt() {}
diff --git a/test/doc.go b/test/doc.go
index a2bbe20..82b585d 100644
--- a/test/doc.go
+++ b/test/doc.go
@@ -1,9 +1,10 @@
-// Package testutil provides initalization for unit and integration tests.
+// Package test provides initalization for unit and integration tests.
//
-// Configures logging, random number generators and other global state.
+// Init configures logging, random number generators and other global state.
// Typical usage in _test.go files:
//
// import "v.io/x/ref/test"
+//
// func TestMain(m *testing.M) {
// test.Init()
// os.Exit(m.Run())
@@ -17,4 +18,43 @@
// defer shutdown()
// ...
// }
+//
+// This package also defines flags for enabling and controlling
+// the v23 integration tests in package v23tests:
+// --v23.tests - run the integration tests
+// --v23.tests.shell-on-fail - drop into a debug shell if the test fails.
+//
+// Typical usage is:
+// $ v23 go test . --v23.tests
+//
+// Note that, like all flags not recognised by the go testing package, the
+// v23.tests flags must follow the package spec.
+//
+// The sub-directories of this package provide either functionality that
+// can be used within traditional go tests, or support for the v23 integration
+// test framework. The v23 command is able to generate boilerplate code
+// to support these tests. In summary, v23 test generate will generate
+// go files to be checked in that include appropriate TestMain functions,
+// registration calls for modules commands and wrapper functions for v23test
+// tests. More detailed documentation is available via:
+//
+// $ v23 test generate --help
+//
+// Vanadium tests often need to run subprocesses to provide either common
+// services that they depend (e.g. a mount table) and/or services that are
+// specific to the tests. The modules and v23tests subdirectories contain
+// packages that simplify this process.
+//
+// The subdirectories are:
+// benchmark - support for writing benchmarks.
+// testutil - utility functions used in tests.
+// security - security related utility functions used in tests.
+// timekeeper - an implementation of the timekeeper interface for use within
+// tests.
+// modules - support for running subprocesses using a single binary
+// v23tests - support for integration tests, including compiling and running
+// arbirtrary go code and pre-existing system commands.
+// expect - support for testing the contents of input streams. The methods
+// provided are embedded in the types used by modules and v23tests
+// so this package is generally not used directly.
package test
diff --git a/test/modules/modules_test.go b/test/modules/modules_test.go
index 181687a..1aece64 100644
--- a/test/modules/modules_test.go
+++ b/test/modules/modules_test.go
@@ -804,3 +804,27 @@
t.Fatalf("ExpectTesting should be non nil")
}
}
+
+func TestCredentialsAndNoExec(t *testing.T) {
+ ctx, shutdown := test.InitForTest()
+ defer shutdown()
+ sh, err := modules.NewExpectShell(ctx, nil, t, testing.Verbose())
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ opts := sh.DefaultStartOpts()
+ opts = opts.NoExecCommand()
+ creds, err := sh.NewCustomCredentials()
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ opts = opts.WithCustomCredentials(creds)
+ h, err := sh.StartWithOpts(opts, nil, "echos", "a")
+
+ if got, want := err, modules.ErrNoExecAndCustomCreds; got != want {
+ t.Fatalf("got %v, want %v", got, want)
+ }
+ if got, want := h, modules.Handle(nil); got != want {
+ t.Fatalf("got %v, want %v", got, want)
+ }
+}
diff --git a/test/modules/shell.go b/test/modules/shell.go
index 08ccdab..4dc7e43 100644
--- a/test/modules/shell.go
+++ b/test/modules/shell.go
@@ -28,7 +28,13 @@
// Vanadium command implements the protocol used by v.io/x/ref/lib/exec
// package to synchronise between the parent and child processes and to share
// information such as the ConfigKey key,value store supported by the Shell,
-// a shared secret etc.
+// a shared secret, shared file descriptors etc.
+//
+// When the exec protocol is not used the only form of communication with the
+// child processes are environment variables and command line flags and any
+// shared file descriptors explicitly created by the parent process and expected
+// by the child process; the Start method will not create any additional
+// file descriptors.
//
// The registry provides the following functions:
// - RegisterChild: generally called from an init function to register a
@@ -134,6 +140,7 @@
package modules
import (
+ "errors"
"fmt"
"io"
"io/ioutil"
@@ -458,6 +465,11 @@
return opts
}
+var (
+ ErrNotRegistered = errors.New("command not registered")
+ ErrNoExecAndCustomCreds = errors.New("ExecProtocol set to false but this invocation is attempting to use custome credentials")
+)
+
// StartWithOpts starts the specified command according to the supplied
// StartOpts and returns a Handle which can be used for interacting with
// that command.
@@ -495,13 +507,19 @@
if opts.Error != nil {
return nil, opts.Error
}
+
var desc *commandDesc
if opts.External {
desc = registry.getExternalCommand(name)
} else if desc = registry.getCommand(name); desc == nil {
- return nil, fmt.Errorf("%s: not registered", name)
+ return nil, ErrNotRegistered
}
- if opts.Credentials == nil {
+
+ if !opts.ExecProtocol && opts.Credentials != nil {
+ return nil, ErrNoExecAndCustomCreds
+ }
+
+ if sh.ctx != nil && opts.ExecProtocol && opts.Credentials == nil {
var err error
opts.Credentials, err = sh.NewChildCredentials("child")
if err != nil {
@@ -516,12 +534,10 @@
}
var p *os.File
- if opts.ExecProtocol {
- if opts.Credentials != nil {
- p, err = opts.Credentials.File()
- if err != nil {
- return nil, err
- }
+ if opts.Credentials != nil {
+ p, err = opts.Credentials.File()
+ if err != nil {
+ return nil, err
}
}
diff --git a/test/v23tests/binary.go b/test/v23tests/binary.go
index 92ed21e..7902612 100644
--- a/test/v23tests/binary.go
+++ b/test/v23tests/binary.go
@@ -60,7 +60,7 @@
func (b *Binary) start(skip int, args ...string) *Invocation {
vlog.Infof("%s: starting %s %s", Caller(skip+1), b.Path(), strings.Join(args, " "))
opts := b.opts
- if opts.Credentials == nil {
+ if opts.ExecProtocol && opts.Credentials == nil {
opts.Credentials, opts.Error = b.env.shell.NewChildCredentials("child")
}
handle, err := b.env.shell.StartWithOpts(opts, b.envVars, b.Path(), args...)
diff --git a/test/v23tests/doc.go b/test/v23tests/doc.go
new file mode 100644
index 0000000..4bd0fb3
--- /dev/null
+++ b/test/v23tests/doc.go
@@ -0,0 +1,71 @@
+// Package v23tests provides support for writing end-to-end style integration
+// tests. In particular, support is provided for building binaries, running
+// processes, making assertions about their output/state and ensuring that
+// no processes or files are left behind on exit. Since such tests are often
+// difficult to debug facilities are provided to help do so.
+//
+// The preferred usage of this integration test framework is via the v23
+// tool which generates supporting code. The primary reason for doing so is
+// to cleanly separate integration tests, which can be very expensive to run,
+// from normal unit tests which are intended to be fast and used constantly.
+// However, it still beneficial to be able to always compile the integration
+// test code with the normal test code, just not to run it. Similarly, it
+// is beneficial to share as much of the existing go test infrastructure as
+// possible, so the generated code uses a flag and a naming convention to
+// separate the tests. Integration tests may be run in addition to unit tests
+// by supplying the --v23.tests flag; the -run flag can be used
+// to avoid running unit tests by specifying a prefix of TestV23 since
+// the generated test functions names always start with TestV23. Thus:
+//
+// v23 go test -v <pkgs> --v23.tests // runs both unit and integration tests
+// v23 go test -v -run=TestV23 <pkgs> --v23.tests // runs just integration tests
+//
+// The go generate mechanism is used to generate the test code, thus the
+// comment:
+//
+// //go:generate v23 test generate
+//
+// will generate the files v23_test.go and internal_v23_test.go for the
+// package in which it occurs. Run v23 test generate --help for full
+// details and options. In short, any function in an external
+// (i.e. <pgk>_test) test package of the following form:
+//
+// V23Test<x>(t *v23tests.T)
+//
+// will be invoked as integration test if the --v23.tests flag is used.
+//
+// The generated code makes use of the RunTest function.
+//
+// The test environment is implemented by an instance of T.
+// It is constructed with an instance of another interface TB, a mirror
+// of testing.TB. Thus, the integration test environment can be used
+// directly as follows:
+//
+// func TestFoo(t *testing.T) {
+// env := v23tests.New(t)
+// defer env.Cleanup()
+//
+// ...
+// }
+//
+// The methods in this API typically do not return error in the case of
+// failure. Instead, the current test will fail with an appropriate error
+// message. This avoids the need to handle errors inline in the test.
+//
+// The test environment manages all built packages, subprocesses and a
+// set of environment variables that are passed to subprocesses.
+//
+// Debugging is supported as follows:
+// 1. The DebugShell method creates an interative shell at that point in
+// the tests execution that has access to all of the running processes
+// and environment of those processes. The developer can interact with
+// those processes to determine the state of the test.
+// 2. Calls to methods on Test (e.g. FailNow, Fatalf) that fail the test
+// cause the Cleanup method to print out the status of all invocations.
+// 3. Similarly, if the --v23.tests.shell-on-error flag is set then the
+// cleanup method will invoke a DebugShell on a test failure allowing
+// the developer to inspect the state of the test.
+// 4. The implementation of this package uses filenames that start with v23test
+// to allow for easy tracing with --vmodule=v23test*=2 for example.
+//
+package v23tests
diff --git a/test/v23tests/v23tests.go b/test/v23tests/v23tests.go
index c56737b..cac131c 100644
--- a/test/v23tests/v23tests.go
+++ b/test/v23tests/v23tests.go
@@ -1,73 +1,3 @@
-// Package v23tests provides support for writing end-to-end style integration
-// tests. In particular, support is provided for building binaries, running
-// processes, making assertions about their output/state and ensuring that
-// no processes or files are left behind on exit. Since such tests are often
-// difficult to debug facilities are provided to help do so.
-//
-// The preferred usage of this integration test framework is via the v23
-// tool which generates supporting code. The primary reason for doing so is
-// to cleanly separate integration tests, which can be very expensive to run,
-// from normal unit tests which are intended to be fast and used constantly.
-// However, it still beneficial to be able to always compile the integration
-// test code with the normal test code, just not to run it. Similarly, it
-// is beneficial to share as much of the existing go test infrastructure as
-// possible, so the generated code uses a flag and a naming convention to
-// separate the tests. Integration tests may be run in addition to unit tests
-// by supplying the --v23.tests flag; the -run flag can be used
-// to avoid running unit tests by specifying a prefix of TestV23 since
-// the generate test functions always. Thus:
-//
-// v23 go test -v <pkgs> --v23.test // runs both unit and integration tests
-// v23 go test -v -run=TestV23 <pkgs> --v23.test // runs just integration tests
-//
-// The go generate mechanism is used to generate the test code, thus the
-// comment:
-//
-// //go:generate v23 integration generate
-//
-// will generate the files v23_test.go and internal_v23_test.go for the
-// package in which it occurs. Run v23 integration generate help for full
-// details and options. In short, any function in an external
-// (i.e. <pgk>_test) test package of the following form:
-//
-// V23Test<x>(t *v23tests.T)
-//
-// will be invoked as integration test if the --v23.tests flag is used.
-//
-// The generated code makes use of the RunTest function, documented below.
-//
-// The test environment is implemented by an instance of the interface T.
-// It is constructed with an instance of another interface Test, which is
-// generally implemented by testing.T. Thus, the integration test environment
-// directly as follows:
-//
-// func TestFoo(t *testing.T) {
-// env := v23tests.New(t)
-// defer env.Cleanup()
-//
-// ...
-// }
-//
-// The methods in this API typically do not return error in the case of
-// failure. Instead, the current test will fail with an appropriate error
-// message. This avoids the need to handle errors inline the test itself.
-//
-// The test environment manages all built packages, subprocesses and a
-// set of environment variables that are passed to subprocesses.
-//
-// Debugging is supported as follows:
-// 1. The DebugShell method creates an interative shell at that point in
-// the tests execution that has access to all of the running processes
-// and environment of those processes. The developer can interact with
-// those processes to determine the state of the test.
-// 2. Calls to methods on Test (e.g. FailNow, Fatalf) that fail the test
-// cause the Cleanup method to print out the status of all invocations.
-// 3. Similarly, if the --v23.tests.shell-on-error flag is set then the
-// cleanup method will invoke a DebugShell on a test failure allowing
-// the developer to inspect the state of the test.
-// 4. The implementation of this package uses filenames that start with v23test
-// to allow for easy tracing with --vmodule=v23test*=2 for example.
-//
package v23tests
import (
@@ -468,15 +398,23 @@
// BuildGoPkg expects a Go package path that identifies a "main" package and
// returns a Binary representing the newly built binary. This binary does not
-// use the exec protocol defined in v.io/x/ref/lib/exec. Use this for
-// non-Vanadium command-line tools and servers.
+// use the exec protocol defined in v.io/x/ref/lib/exec and in particular will
+// not have access to the security agent or any other shared file descriptors.
+// Environment variables and command line arguments are the only means of
+// communicating with the invocations of this binary.
+// Use this for non-Vanadium command-line tools and servers.
func (t *T) BuildGoPkg(pkg string) *Binary {
return t.buildPkg(pkg)
}
// BuildV23 is like BuildGoPkg, but instead assumes that the resulting
// binary is a Vanadium application and does implement the exec protocol
-// defined in v.io/x/ref/lib/exec. Use this for Vanadium servers.
+// defined in v.io/x/ref/lib/exec. The invocations of this binary will
+// have access to the security agent configured for the parent process, to
+// shared file descriptors, the config shared by the exec package.
+// Use this for Vanadium servers. Note that some vanadium client only binaries,
+// that do not call v23.Init and hence do not implement the exec protocol
+// cannot be used via BuildV23Pkg.
func (t *T) BuildV23Pkg(pkg string) *Binary {
b := t.buildPkg(pkg)
b.opts = t.shell.DefaultStartOpts().ExternalCommand()
@@ -593,6 +531,7 @@
return e
}
+// Shell returns the underlying modules.Shell used by v23tests.
func (t *T) Shell() *modules.Shell {
return t.shell
}