blob: ae44db11649919db7feef279710df6e29bcf0e75 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package servicetest
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"testing"
"v.io/v23"
"v.io/v23/context"
"v.io/v23/options"
"v.io/v23/security"
"v.io/x/ref"
"v.io/x/ref/internal/logger"
"v.io/x/ref/services/mounttable/mounttablelib"
"v.io/x/ref/test/modules"
"v.io/x/ref/test/testutil"
)
const (
// Setting this environment variable to any non-empty value avoids
// removing the generated workspace for successful test runs (for
// failed test runs, this is already the case). This is useful when
// developing test cases.
preserveWorkspaceEnv = "V23_TEST_PRESERVE_WORKSPACE"
)
var rootMT = modules.Register(func(env *modules.Env, args ...string) error {
ctx, shutdown := v23.Init()
defer shutdown()
mt, err := mounttablelib.NewMountTableDispatcher(ctx, "", "", "mounttable")
if err != nil {
return fmt.Errorf("mounttablelib.NewMountTableDispatcher failed: %s", err)
}
ctx, server, err := v23.WithNewDispatchingServer(ctx, "", mt, options.ServesMountTable(true))
if err != nil {
return fmt.Errorf("root failed: %v", err)
}
fmt.Fprintf(env.Stdout, "PID=%d\n", os.Getpid())
for _, ep := range server.Status().Endpoints {
fmt.Fprintf(env.Stdout, "MT_NAME=%s\n", ep.Name())
}
modules.WaitForEOF(env.Stdin)
return nil
}, "rootMT")
// startRootMT sets up a root mount table for tests.
func startRootMT(t *testing.T, sh *modules.Shell) (string, modules.Handle) {
h, err := sh.Start(nil, rootMT, "--v23.tcp.address=127.0.0.1:0")
if err != nil {
t.Fatalf("failed to start root mount table: %s", err)
}
h.ExpectVar("PID")
rootName := h.ExpectVar("MT_NAME")
if t.Failed() {
t.Fatalf("failed to read mt name: %s", h.Error())
}
return rootName, h
}
// setNSRoots sets the roots for the local runtime's namespace.
func setNSRoots(t *testing.T, ctx *context.T, roots ...string) {
ns := v23.GetNamespace(ctx)
if err := ns.SetRoots(roots...); err != nil {
t.Fatalf(testutil.FormatLogLine(3, "SetRoots(%v) failed with %v", roots, err))
}
}
// CreateShellAndMountTable builds a new modules shell and its
// associated mount table.
func CreateShellAndMountTable(t *testing.T, ctx *context.T, p security.Principal) (*modules.Shell, func()) {
sh, err := modules.NewShell(ctx, p, testing.Verbose(), t)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
opts := sh.DefaultStartOpts()
opts.ExpectTimeout = ExpectTimeout
sh.SetDefaultStartOpts(opts)
// The shell, will, by default share credentials with its children.
sh.ClearVar(ref.EnvCredentials)
mtName, _ := startRootMT(t, sh)
ctx.VI(1).Infof("Started shell mounttable with name %v", mtName)
// TODO(caprita): Define a GetNamespaceRootsCommand in modules/core and
// use that?
oldNamespaceRoots := v23.GetNamespace(ctx).Roots()
fn := func() {
ctx.VI(1).Info("------------ CLEANUP ------------")
ctx.VI(1).Info("---------------------------------")
ctx.VI(1).Info("--(cleaning up shell)------------")
if err := sh.Cleanup(os.Stdout, os.Stderr); err != nil {
t.Errorf(testutil.FormatLogLine(2, "sh.Cleanup failed with %v", err))
}
ctx.VI(1).Info("--(done cleaning up shell)-------")
setNSRoots(t, ctx, oldNamespaceRoots...)
}
setNSRoots(t, ctx, mtName)
sh.SetVar(ref.EnvNamespacePrefix, mtName)
return sh, fn
}
// CreateShell builds a new modules shell.
func CreateShell(t *testing.T, ctx *context.T, p security.Principal) (*modules.Shell, func()) {
sh, err := modules.NewShell(ctx, p, testing.Verbose(), t)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
opts := sh.DefaultStartOpts()
opts.ExpectTimeout = ExpectTimeout
sh.SetDefaultStartOpts(opts)
// The shell, will, by default share credentials with its children.
sh.ClearVar(ref.EnvCredentials)
fn := func() {
ctx.VI(1).Info("------------ CLEANUP ------------")
ctx.VI(1).Info("---------------------------------")
ctx.VI(1).Info("--(cleaning up shell)------------")
if err := sh.Cleanup(os.Stdout, os.Stderr); err != nil {
t.Errorf(testutil.FormatLogLine(2, "sh.Cleanup failed with %v", err))
}
ctx.VI(1).Info("--(done cleaning up shell)-------")
}
nsRoots := v23.GetNamespace(ctx).Roots()
if len(nsRoots) == 0 {
t.Fatalf("shell context has no namespace roots")
}
sh.SetVar(ref.EnvNamespacePrefix, nsRoots[0])
return sh, fn
}
// RunCommand runs a modules command.
func RunCommand(t *testing.T, sh *modules.Shell, env []string, prog modules.Program, args ...string) modules.Handle {
h, err := sh.StartWithOpts(sh.DefaultStartOpts(), env, prog, args...)
if err != nil {
t.Fatalf(testutil.FormatLogLine(2, "failed to start %q: %s", prog, err))
return nil
}
h.SetVerbosity(testing.Verbose())
return h
}
// ReadPID waits for the "ready:<PID>" line from the child and parses out the
// PID of the child.
func ReadPID(t *testing.T, h modules.ExpectSession) int {
m := h.ExpectRE("ready:([0-9]+)", -1)
if len(m) == 1 && len(m[0]) == 2 {
pid, err := strconv.Atoi(m[0][1])
if err != nil {
t.Fatalf(testutil.FormatLogLine(2, "Atoi(%q) failed: %v", m[0][1], err))
}
return pid
}
t.Fatalf(testutil.FormatLogLine(2, "failed to extract pid: %v", m))
return 0
}
// SetupRootDir sets up and returns a directory for the root and returns
// a cleanup function.
func SetupRootDir(t *testing.T, prefix string) (string, func()) {
rootDir, err := ioutil.TempDir("", prefix)
if err != nil {
t.Fatalf("Failed to set up temporary dir for test: %v", err)
}
// On some operating systems (e.g. darwin) os.TempDir() can return a
// symlink. To avoid having to account for this eventuality later,
// evaluate the symlink.
rootDir, err = filepath.EvalSymlinks(rootDir)
if err != nil {
logger.Global().Fatalf("EvalSymlinks(%v) failed: %v", rootDir, err)
}
return rootDir, func() {
if t.Failed() || os.Getenv(preserveWorkspaceEnv) != "" {
t.Logf("You can examine the %s workspace at %v", prefix, rootDir)
} else {
os.RemoveAll(rootDir)
}
}
}