blob: 708a0b8d984913992c060c1883c0eda9790d563d [file] [log] [blame]
Robert Kroegerebfb62a2014-12-10 14:42:09 -08001package testutil
2
3import (
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -08004 "io/ioutil"
Robert Kroegerebfb62a2014-12-10 14:42:09 -08005 "os"
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -08006 "path/filepath"
Robert Kroegerebfb62a2014-12-10 14:42:09 -08007 "strconv"
8 "testing"
9
Jiri Simsa6ac95222015-02-23 16:11:49 -080010 "v.io/v23"
11 "v.io/v23/context"
12 "v.io/v23/ipc"
13 "v.io/v23/security"
14 "v.io/v23/vlog"
Robert Kroegerebfb62a2014-12-10 14:42:09 -080015
16 "v.io/core/veyron/lib/expect"
17 "v.io/core/veyron/lib/flags/consts"
18 "v.io/core/veyron/lib/modules"
19 "v.io/core/veyron/lib/modules/core"
20 "v.io/core/veyron/lib/testutil"
Ryan Browna08a2212015-01-15 15:40:10 -080021 tsecurity "v.io/core/veyron/lib/testutil/security"
Robert Kroegerebfb62a2014-12-10 14:42:09 -080022)
23
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -080024const (
25 // Setting this environment variable to any non-empty value avoids
26 // removing the generated workspace for successful test runs (for
27 // failed test runs, this is already the case). This is useful when
28 // developing test cases.
29 preserveWorkspaceEnv = "VEYRON_TEST_PRESERVE_WORKSPACE"
30)
31
Robert Kroegerebfb62a2014-12-10 14:42:09 -080032// StartRootMT sets up a root mount table for tests.
33func StartRootMT(t *testing.T, sh *modules.Shell) (string, modules.Handle) {
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -080034 h, err := sh.Start(core.RootMTCommand, nil, "--veyron.tcp.address=127.0.0.1:0")
Robert Kroegerebfb62a2014-12-10 14:42:09 -080035 if err != nil {
36 t.Fatalf("failed to start root mount table: %s", err)
37 }
38 s := expect.NewSession(t, h.Stdout(), ExpectTimeout)
39 s.ExpectVar("PID")
40 rootName := s.ExpectVar("MT_NAME")
41 if t.Failed() {
42 t.Fatalf("failed to read mt name: %s", s.Error())
43 }
44 return rootName, h
45}
46
47// CredentialsForChild creates credentials for a child process.
Matt Rosencrantz6edab562015-01-12 11:07:55 -080048func CredentialsForChild(ctx *context.T, blessing string) (string, []string) {
Jiri Simsa6ac95222015-02-23 16:11:49 -080049 creds, _ := tsecurity.ForkCredentials(v23.GetPrincipal(ctx), blessing)
Robert Kroegerebfb62a2014-12-10 14:42:09 -080050 return creds, []string{consts.VeyronCredentials + "=" + creds}
51}
52
53// SetNSRoots sets the roots for the local runtime's namespace.
Matt Rosencrantz6edab562015-01-12 11:07:55 -080054func SetNSRoots(t *testing.T, ctx *context.T, roots ...string) {
Jiri Simsa6ac95222015-02-23 16:11:49 -080055 ns := v23.GetNamespace(ctx)
Matt Rosencrantz6edab562015-01-12 11:07:55 -080056 if err := ns.SetRoots(roots...); err != nil {
Robert Kroegerebfb62a2014-12-10 14:42:09 -080057 t.Fatalf(testutil.FormatLogLine(3, "SetRoots(%v) failed with %v", roots, err))
58 }
59}
60
61// CreateShellAndMountTable builds a new modules shell and its
62// associated mount table.
Ryan Browna08a2212015-01-15 15:40:10 -080063func CreateShellAndMountTable(t *testing.T, ctx *context.T, p security.Principal) (*modules.Shell, func()) {
64 sh, err := modules.NewShell(ctx, p)
Robert Kroegerebfb62a2014-12-10 14:42:09 -080065 if err != nil {
66 t.Fatalf("unexpected error: %s", err)
67 }
68 // The shell, will, by default share credentials with its children.
69 sh.ClearVar(consts.VeyronCredentials)
70
71 mtName, mtHandle := StartRootMT(t, sh)
72 vlog.VI(1).Infof("Started shell mounttable with name %v", mtName)
73 // Make sure the root mount table is the last process to be shutdown
74 // since the others will likely want to communicate with it during
75 // their shutdown process
76 sh.Forget(mtHandle)
77
78 // TODO(caprita): Define a GetNamespaceRootsCommand in modules/core and
79 // use that?
Matt Rosencrantz6edab562015-01-12 11:07:55 -080080
Jiri Simsa6ac95222015-02-23 16:11:49 -080081 oldNamespaceRoots := v23.GetNamespace(ctx).Roots()
Robert Kroegerebfb62a2014-12-10 14:42:09 -080082 fn := func() {
83 vlog.VI(1).Info("------------ CLEANUP ------------")
84 vlog.VI(1).Info("---------------------------------")
85 vlog.VI(1).Info("--(cleaning up shell)------------")
86 if err := sh.Cleanup(os.Stdout, os.Stderr); err != nil {
Robert Kroeger74645172015-02-11 14:22:21 -080087 t.Fatalf(testutil.FormatLogLine(2, "sh.Cleanup failed with %v", err))
Robert Kroegerebfb62a2014-12-10 14:42:09 -080088 }
89 vlog.VI(1).Info("--(done cleaning up shell)-------")
90 vlog.VI(1).Info("--(shutting down root mt)--------")
91 if err := mtHandle.Shutdown(os.Stdout, os.Stderr); err != nil {
Robert Kroeger74645172015-02-11 14:22:21 -080092 t.Fatalf(testutil.FormatLogLine(2, "mtHandle.Shutdown failed with %v", err))
Robert Kroegerebfb62a2014-12-10 14:42:09 -080093 }
94 vlog.VI(1).Info("--(done shutting down root mt)---")
95 vlog.VI(1).Info("--------- DONE CLEANUP ----------")
Matt Rosencrantz6edab562015-01-12 11:07:55 -080096 SetNSRoots(t, ctx, oldNamespaceRoots...)
Robert Kroegerebfb62a2014-12-10 14:42:09 -080097 }
Matt Rosencrantz6edab562015-01-12 11:07:55 -080098 SetNSRoots(t, ctx, mtName)
Robert Kroegerebfb62a2014-12-10 14:42:09 -080099 sh.SetVar(consts.NamespaceRootPrefix, mtName)
100 return sh, fn
101}
102
103// RunShellCommand runs an external command using the modules system.
104func RunShellCommand(t *testing.T, sh *modules.Shell, env []string, cmd string, args ...string) (modules.Handle, *expect.Session) {
105 h, err := sh.Start(cmd, env, args...)
106 if err != nil {
107 t.Fatalf(testutil.FormatLogLine(2, "failed to start %q: %s", cmd, err))
108 return nil, nil
109 }
110 s := expect.NewSession(t, h.Stdout(), ExpectTimeout)
111 s.SetVerbosity(testing.Verbose())
112 return h, s
113}
114
115// NewServer creates a new server.
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800116func NewServer(ctx *context.T) (ipc.Server, string) {
Jiri Simsa6ac95222015-02-23 16:11:49 -0800117 server, err := v23.NewServer(ctx)
Robert Kroegerebfb62a2014-12-10 14:42:09 -0800118 if err != nil {
119 vlog.Fatalf("NewServer() failed: %v", err)
120 }
121 spec := ipc.ListenSpec{Addrs: ipc.ListenAddrs{{"tcp", "127.0.0.1:0"}}}
122 endpoints, err := server.Listen(spec)
123 if err != nil {
124 vlog.Fatalf("Listen(%s) failed: %v", spec, err)
125 }
126 return server, endpoints[0].String()
127}
128
Robert Kroegerebfb62a2014-12-10 14:42:09 -0800129// ReadPID waits for the "ready:<PID>" line from the child and parses out the
130// PID of the child.
131func ReadPID(t *testing.T, s *expect.Session) int {
132 m := s.ExpectRE("ready:([0-9]+)", -1)
133 if len(m) == 1 && len(m[0]) == 2 {
134 pid, err := strconv.Atoi(m[0][1])
135 if err != nil {
136 t.Fatalf(testutil.FormatLogLine(2, "Atoi(%q) failed: %v", m[0][1], err))
137 }
138 return pid
139 }
140 t.Fatalf(testutil.FormatLogLine(2, "failed to extract pid: %v", m))
141 return 0
142}
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800143
144// SetupRootDir sets up and returns a directory for the root and returns
145// a cleanup function.
146func SetupRootDir(t *testing.T, prefix string) (string, func()) {
147 rootDir, err := ioutil.TempDir("", prefix)
148 if err != nil {
149 t.Fatalf("Failed to set up temporary dir for test: %v", err)
150 }
151 // On some operating systems (e.g. darwin) os.TempDir() can return a
152 // symlink. To avoid having to account for this eventuality later,
153 // evaluate the symlink.
154 rootDir, err = filepath.EvalSymlinks(rootDir)
155 if err != nil {
156 vlog.Fatalf("EvalSymlinks(%v) failed: %v", rootDir, err)
157 }
158
159 return rootDir, func() {
160 if t.Failed() || os.Getenv(preserveWorkspaceEnv) != "" {
161 t.Logf("You can examine the %s workspace at %v", prefix, rootDir)
162 } else {
163 os.RemoveAll(rootDir)
164 }
165 }
166}