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
 }