Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 1 | package testutil |
| 2 | |
| 3 | import ( |
Robert Kroeger | d6e1d1a | 2014-12-10 15:08:45 -0800 | [diff] [blame] | 4 | "io/ioutil" |
Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 5 | "os" |
Robert Kroeger | d6e1d1a | 2014-12-10 15:08:45 -0800 | [diff] [blame] | 6 | "path/filepath" |
Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 7 | "strconv" |
| 8 | "testing" |
| 9 | |
Jiri Simsa | 6ac9522 | 2015-02-23 16:11:49 -0800 | [diff] [blame^] | 10 | "v.io/v23" |
| 11 | "v.io/v23/context" |
| 12 | "v.io/v23/ipc" |
| 13 | "v.io/v23/security" |
| 14 | "v.io/v23/vlog" |
Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 15 | |
| 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 Brown | a08a221 | 2015-01-15 15:40:10 -0800 | [diff] [blame] | 21 | tsecurity "v.io/core/veyron/lib/testutil/security" |
Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 22 | ) |
| 23 | |
Robert Kroeger | d6e1d1a | 2014-12-10 15:08:45 -0800 | [diff] [blame] | 24 | const ( |
| 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 Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 32 | // StartRootMT sets up a root mount table for tests. |
| 33 | func StartRootMT(t *testing.T, sh *modules.Shell) (string, modules.Handle) { |
Suharsh Sivakumar | 9d17e4a | 2015-02-02 22:42:16 -0800 | [diff] [blame] | 34 | h, err := sh.Start(core.RootMTCommand, nil, "--veyron.tcp.address=127.0.0.1:0") |
Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 35 | 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 Rosencrantz | 6edab56 | 2015-01-12 11:07:55 -0800 | [diff] [blame] | 48 | func CredentialsForChild(ctx *context.T, blessing string) (string, []string) { |
Jiri Simsa | 6ac9522 | 2015-02-23 16:11:49 -0800 | [diff] [blame^] | 49 | creds, _ := tsecurity.ForkCredentials(v23.GetPrincipal(ctx), blessing) |
Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 50 | return creds, []string{consts.VeyronCredentials + "=" + creds} |
| 51 | } |
| 52 | |
| 53 | // SetNSRoots sets the roots for the local runtime's namespace. |
Matt Rosencrantz | 6edab56 | 2015-01-12 11:07:55 -0800 | [diff] [blame] | 54 | func SetNSRoots(t *testing.T, ctx *context.T, roots ...string) { |
Jiri Simsa | 6ac9522 | 2015-02-23 16:11:49 -0800 | [diff] [blame^] | 55 | ns := v23.GetNamespace(ctx) |
Matt Rosencrantz | 6edab56 | 2015-01-12 11:07:55 -0800 | [diff] [blame] | 56 | if err := ns.SetRoots(roots...); err != nil { |
Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 57 | 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 Brown | a08a221 | 2015-01-15 15:40:10 -0800 | [diff] [blame] | 63 | func CreateShellAndMountTable(t *testing.T, ctx *context.T, p security.Principal) (*modules.Shell, func()) { |
| 64 | sh, err := modules.NewShell(ctx, p) |
Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 65 | 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 Rosencrantz | 6edab56 | 2015-01-12 11:07:55 -0800 | [diff] [blame] | 80 | |
Jiri Simsa | 6ac9522 | 2015-02-23 16:11:49 -0800 | [diff] [blame^] | 81 | oldNamespaceRoots := v23.GetNamespace(ctx).Roots() |
Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 82 | 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 Kroeger | 7464517 | 2015-02-11 14:22:21 -0800 | [diff] [blame] | 87 | t.Fatalf(testutil.FormatLogLine(2, "sh.Cleanup failed with %v", err)) |
Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 88 | } |
| 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 Kroeger | 7464517 | 2015-02-11 14:22:21 -0800 | [diff] [blame] | 92 | t.Fatalf(testutil.FormatLogLine(2, "mtHandle.Shutdown failed with %v", err)) |
Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 93 | } |
| 94 | vlog.VI(1).Info("--(done shutting down root mt)---") |
| 95 | vlog.VI(1).Info("--------- DONE CLEANUP ----------") |
Matt Rosencrantz | 6edab56 | 2015-01-12 11:07:55 -0800 | [diff] [blame] | 96 | SetNSRoots(t, ctx, oldNamespaceRoots...) |
Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 97 | } |
Matt Rosencrantz | 6edab56 | 2015-01-12 11:07:55 -0800 | [diff] [blame] | 98 | SetNSRoots(t, ctx, mtName) |
Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 99 | sh.SetVar(consts.NamespaceRootPrefix, mtName) |
| 100 | return sh, fn |
| 101 | } |
| 102 | |
| 103 | // RunShellCommand runs an external command using the modules system. |
| 104 | func 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 Rosencrantz | 6edab56 | 2015-01-12 11:07:55 -0800 | [diff] [blame] | 116 | func NewServer(ctx *context.T) (ipc.Server, string) { |
Jiri Simsa | 6ac9522 | 2015-02-23 16:11:49 -0800 | [diff] [blame^] | 117 | server, err := v23.NewServer(ctx) |
Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 118 | 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 Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 129 | // ReadPID waits for the "ready:<PID>" line from the child and parses out the |
| 130 | // PID of the child. |
| 131 | func 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 Kroeger | d6e1d1a | 2014-12-10 15:08:45 -0800 | [diff] [blame] | 143 | |
| 144 | // SetupRootDir sets up and returns a directory for the root and returns |
| 145 | // a cleanup function. |
| 146 | func 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 | } |