Jiri Simsa | d7616c9 | 2015-03-24 23:44:30 -0700 | [diff] [blame] | 1 | // Copyright 2015 The Vanadium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style |
| 3 | // license that can be found in the LICENSE file. |
| 4 | |
Todd Wang | 5fc3644 | 2015-04-07 15:15:27 -0700 | [diff] [blame] | 5 | package servicetest |
Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 6 | |
| 7 | import ( |
Matt Rosencrantz | 2d02463 | 2015-03-19 15:00:34 -0700 | [diff] [blame] | 8 | "fmt" |
Robert Kroeger | d6e1d1a | 2014-12-10 15:08:45 -0800 | [diff] [blame] | 9 | "io/ioutil" |
Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 10 | "os" |
Robert Kroeger | d6e1d1a | 2014-12-10 15:08:45 -0800 | [diff] [blame] | 11 | "path/filepath" |
Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 12 | "strconv" |
| 13 | "testing" |
| 14 | |
Jiri Simsa | 6ac9522 | 2015-02-23 16:11:49 -0800 | [diff] [blame] | 15 | "v.io/v23" |
| 16 | "v.io/v23/context" |
Matt Rosencrantz | 2d02463 | 2015-03-19 15:00:34 -0700 | [diff] [blame] | 17 | "v.io/v23/options" |
Jiri Simsa | 6ac9522 | 2015-02-23 16:11:49 -0800 | [diff] [blame] | 18 | "v.io/v23/security" |
Todd Wang | 8123b5e | 2015-05-14 18:44:43 -0700 | [diff] [blame] | 19 | "v.io/x/ref" |
Cosmos Nicolaou | 7a4221f | 2015-06-21 08:02:23 -0700 | [diff] [blame] | 20 | "v.io/x/ref/internal/logger" |
Todd Wang | 5987a94 | 2015-04-06 11:06:17 -0700 | [diff] [blame] | 21 | "v.io/x/ref/services/mounttable/mounttablelib" |
Cosmos Nicolaou | 1381f8a | 2015-03-13 09:40:34 -0700 | [diff] [blame] | 22 | "v.io/x/ref/test/modules" |
Cosmos Nicolaou | 1381f8a | 2015-03-13 09:40:34 -0700 | [diff] [blame] | 23 | "v.io/x/ref/test/testutil" |
Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 24 | ) |
| 25 | |
Robert Kroeger | d6e1d1a | 2014-12-10 15:08:45 -0800 | [diff] [blame] | 26 | const ( |
| 27 | // Setting this environment variable to any non-empty value avoids |
| 28 | // removing the generated workspace for successful test runs (for |
| 29 | // failed test runs, this is already the case). This is useful when |
| 30 | // developing test cases. |
Asim Shankar | 3a6ca47 | 2015-03-31 00:35:37 -0700 | [diff] [blame] | 31 | preserveWorkspaceEnv = "V23_TEST_PRESERVE_WORKSPACE" |
Robert Kroeger | d6e1d1a | 2014-12-10 15:08:45 -0800 | [diff] [blame] | 32 | ) |
| 33 | |
Todd Wang | 9587390 | 2015-05-22 14:21:30 -0700 | [diff] [blame] | 34 | var rootMT = modules.Register(func(env *modules.Env, args ...string) error { |
Matt Rosencrantz | 2d02463 | 2015-03-19 15:00:34 -0700 | [diff] [blame] | 35 | ctx, shutdown := v23.Init() |
| 36 | defer shutdown() |
| 37 | |
Cosmos Nicolaou | 7a4221f | 2015-06-21 08:02:23 -0700 | [diff] [blame] | 38 | mt, err := mounttablelib.NewMountTableDispatcher(ctx, "", "", "mounttable") |
Matt Rosencrantz | 2d02463 | 2015-03-19 15:00:34 -0700 | [diff] [blame] | 39 | if err != nil { |
Todd Wang | 5987a94 | 2015-04-06 11:06:17 -0700 | [diff] [blame] | 40 | return fmt.Errorf("mounttablelib.NewMountTableDispatcher failed: %s", err) |
Matt Rosencrantz | 2d02463 | 2015-03-19 15:00:34 -0700 | [diff] [blame] | 41 | } |
Matt Rosencrantz | 53ac585 | 2015-09-04 15:14:54 -0700 | [diff] [blame] | 42 | ctx, server, err := v23.WithNewDispatchingServer(ctx, "", mt, options.ServesMountTable(true)) |
Matt Rosencrantz | 2d02463 | 2015-03-19 15:00:34 -0700 | [diff] [blame] | 43 | if err != nil { |
Matt Rosencrantz | bb6295d | 2015-06-19 15:13:58 -0700 | [diff] [blame] | 44 | return fmt.Errorf("root failed: %v", err) |
Matt Rosencrantz | 2d02463 | 2015-03-19 15:00:34 -0700 | [diff] [blame] | 45 | } |
Todd Wang | 9587390 | 2015-05-22 14:21:30 -0700 | [diff] [blame] | 46 | fmt.Fprintf(env.Stdout, "PID=%d\n", os.Getpid()) |
Matt Rosencrantz | bb6295d | 2015-06-19 15:13:58 -0700 | [diff] [blame] | 47 | for _, ep := range server.Status().Endpoints { |
Todd Wang | 9587390 | 2015-05-22 14:21:30 -0700 | [diff] [blame] | 48 | fmt.Fprintf(env.Stdout, "MT_NAME=%s\n", ep.Name()) |
Matt Rosencrantz | 2d02463 | 2015-03-19 15:00:34 -0700 | [diff] [blame] | 49 | } |
Todd Wang | 9587390 | 2015-05-22 14:21:30 -0700 | [diff] [blame] | 50 | modules.WaitForEOF(env.Stdin) |
Matt Rosencrantz | 2d02463 | 2015-03-19 15:00:34 -0700 | [diff] [blame] | 51 | return nil |
Todd Wang | 9587390 | 2015-05-22 14:21:30 -0700 | [diff] [blame] | 52 | }, "rootMT") |
Matt Rosencrantz | 2d02463 | 2015-03-19 15:00:34 -0700 | [diff] [blame] | 53 | |
Asim Shankar | 11b530a | 2015-03-11 22:40:52 -0700 | [diff] [blame] | 54 | // startRootMT sets up a root mount table for tests. |
| 55 | func startRootMT(t *testing.T, sh *modules.Shell) (string, modules.Handle) { |
Todd Wang | 9587390 | 2015-05-22 14:21:30 -0700 | [diff] [blame] | 56 | h, err := sh.Start(nil, rootMT, "--v23.tcp.address=127.0.0.1:0") |
Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 57 | if err != nil { |
| 58 | t.Fatalf("failed to start root mount table: %s", err) |
| 59 | } |
Cosmos Nicolaou | 42a1736 | 2015-03-10 16:40:18 -0700 | [diff] [blame] | 60 | h.ExpectVar("PID") |
| 61 | rootName := h.ExpectVar("MT_NAME") |
Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 62 | if t.Failed() { |
Cosmos Nicolaou | 42a1736 | 2015-03-10 16:40:18 -0700 | [diff] [blame] | 63 | t.Fatalf("failed to read mt name: %s", h.Error()) |
Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 64 | } |
| 65 | return rootName, h |
| 66 | } |
| 67 | |
Asim Shankar | 11b530a | 2015-03-11 22:40:52 -0700 | [diff] [blame] | 68 | // setNSRoots sets the roots for the local runtime's namespace. |
| 69 | func setNSRoots(t *testing.T, ctx *context.T, roots ...string) { |
Jiri Simsa | 6ac9522 | 2015-02-23 16:11:49 -0800 | [diff] [blame] | 70 | ns := v23.GetNamespace(ctx) |
Matt Rosencrantz | 6edab56 | 2015-01-12 11:07:55 -0800 | [diff] [blame] | 71 | if err := ns.SetRoots(roots...); err != nil { |
Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 72 | t.Fatalf(testutil.FormatLogLine(3, "SetRoots(%v) failed with %v", roots, err)) |
| 73 | } |
| 74 | } |
| 75 | |
| 76 | // CreateShellAndMountTable builds a new modules shell and its |
| 77 | // associated mount table. |
Ryan Brown | a08a221 | 2015-01-15 15:40:10 -0800 | [diff] [blame] | 78 | func CreateShellAndMountTable(t *testing.T, ctx *context.T, p security.Principal) (*modules.Shell, func()) { |
Cosmos Nicolaou | 9e90984 | 2015-03-17 11:58:59 -0700 | [diff] [blame] | 79 | sh, err := modules.NewShell(ctx, p, testing.Verbose(), t) |
Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 80 | if err != nil { |
| 81 | t.Fatalf("unexpected error: %s", err) |
| 82 | } |
Cosmos Nicolaou | 42a1736 | 2015-03-10 16:40:18 -0700 | [diff] [blame] | 83 | opts := sh.DefaultStartOpts() |
| 84 | opts.ExpectTimeout = ExpectTimeout |
| 85 | sh.SetDefaultStartOpts(opts) |
Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 86 | // The shell, will, by default share credentials with its children. |
Todd Wang | 8123b5e | 2015-05-14 18:44:43 -0700 | [diff] [blame] | 87 | sh.ClearVar(ref.EnvCredentials) |
Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 88 | |
Cosmos Nicolaou | aa87e29 | 2015-04-21 22:15:50 -0700 | [diff] [blame] | 89 | mtName, _ := startRootMT(t, sh) |
Cosmos Nicolaou | 7a4221f | 2015-06-21 08:02:23 -0700 | [diff] [blame] | 90 | ctx.VI(1).Infof("Started shell mounttable with name %v", mtName) |
Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 91 | |
| 92 | // TODO(caprita): Define a GetNamespaceRootsCommand in modules/core and |
| 93 | // use that? |
Matt Rosencrantz | 6edab56 | 2015-01-12 11:07:55 -0800 | [diff] [blame] | 94 | |
Jiri Simsa | 6ac9522 | 2015-02-23 16:11:49 -0800 | [diff] [blame] | 95 | oldNamespaceRoots := v23.GetNamespace(ctx).Roots() |
Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 96 | fn := func() { |
Cosmos Nicolaou | 7a4221f | 2015-06-21 08:02:23 -0700 | [diff] [blame] | 97 | ctx.VI(1).Info("------------ CLEANUP ------------") |
| 98 | ctx.VI(1).Info("---------------------------------") |
| 99 | ctx.VI(1).Info("--(cleaning up shell)------------") |
Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 100 | if err := sh.Cleanup(os.Stdout, os.Stderr); err != nil { |
Cosmos Nicolaou | aa87e29 | 2015-04-21 22:15:50 -0700 | [diff] [blame] | 101 | t.Errorf(testutil.FormatLogLine(2, "sh.Cleanup failed with %v", err)) |
Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 102 | } |
Cosmos Nicolaou | 7a4221f | 2015-06-21 08:02:23 -0700 | [diff] [blame] | 103 | ctx.VI(1).Info("--(done cleaning up shell)-------") |
Asim Shankar | 11b530a | 2015-03-11 22:40:52 -0700 | [diff] [blame] | 104 | setNSRoots(t, ctx, oldNamespaceRoots...) |
Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 105 | } |
Asim Shankar | 11b530a | 2015-03-11 22:40:52 -0700 | [diff] [blame] | 106 | setNSRoots(t, ctx, mtName) |
Todd Wang | 8123b5e | 2015-05-14 18:44:43 -0700 | [diff] [blame] | 107 | sh.SetVar(ref.EnvNamespacePrefix, mtName) |
Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 108 | return sh, fn |
| 109 | } |
| 110 | |
Bogdan Caprita | 1356277 | 2015-06-26 15:40:27 -0700 | [diff] [blame] | 111 | // CreateShell builds a new modules shell. |
| 112 | func CreateShell(t *testing.T, ctx *context.T, p security.Principal) (*modules.Shell, func()) { |
| 113 | sh, err := modules.NewShell(ctx, p, testing.Verbose(), t) |
| 114 | if err != nil { |
| 115 | t.Fatalf("unexpected error: %s", err) |
| 116 | } |
| 117 | opts := sh.DefaultStartOpts() |
| 118 | opts.ExpectTimeout = ExpectTimeout |
| 119 | sh.SetDefaultStartOpts(opts) |
| 120 | // The shell, will, by default share credentials with its children. |
| 121 | sh.ClearVar(ref.EnvCredentials) |
| 122 | |
| 123 | fn := func() { |
| 124 | ctx.VI(1).Info("------------ CLEANUP ------------") |
| 125 | ctx.VI(1).Info("---------------------------------") |
| 126 | ctx.VI(1).Info("--(cleaning up shell)------------") |
| 127 | if err := sh.Cleanup(os.Stdout, os.Stderr); err != nil { |
| 128 | t.Errorf(testutil.FormatLogLine(2, "sh.Cleanup failed with %v", err)) |
| 129 | } |
| 130 | ctx.VI(1).Info("--(done cleaning up shell)-------") |
| 131 | } |
| 132 | nsRoots := v23.GetNamespace(ctx).Roots() |
| 133 | if len(nsRoots) == 0 { |
| 134 | t.Fatalf("shell context has no namespace roots") |
| 135 | } |
| 136 | sh.SetVar(ref.EnvNamespacePrefix, nsRoots[0]) |
| 137 | return sh, fn |
| 138 | } |
| 139 | |
Cosmos Nicolaou | 42a1736 | 2015-03-10 16:40:18 -0700 | [diff] [blame] | 140 | // RunCommand runs a modules command. |
Todd Wang | 9587390 | 2015-05-22 14:21:30 -0700 | [diff] [blame] | 141 | func RunCommand(t *testing.T, sh *modules.Shell, env []string, prog modules.Program, args ...string) modules.Handle { |
| 142 | h, err := sh.StartWithOpts(sh.DefaultStartOpts(), env, prog, args...) |
Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 143 | if err != nil { |
Todd Wang | 9587390 | 2015-05-22 14:21:30 -0700 | [diff] [blame] | 144 | t.Fatalf(testutil.FormatLogLine(2, "failed to start %q: %s", prog, err)) |
Cosmos Nicolaou | 42a1736 | 2015-03-10 16:40:18 -0700 | [diff] [blame] | 145 | return nil |
Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 146 | } |
Cosmos Nicolaou | 42a1736 | 2015-03-10 16:40:18 -0700 | [diff] [blame] | 147 | h.SetVerbosity(testing.Verbose()) |
| 148 | return h |
Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 149 | } |
| 150 | |
Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 151 | // ReadPID waits for the "ready:<PID>" line from the child and parses out the |
| 152 | // PID of the child. |
Cosmos Nicolaou | 42a1736 | 2015-03-10 16:40:18 -0700 | [diff] [blame] | 153 | func ReadPID(t *testing.T, h modules.ExpectSession) int { |
| 154 | m := h.ExpectRE("ready:([0-9]+)", -1) |
Robert Kroeger | ebfb62a | 2014-12-10 14:42:09 -0800 | [diff] [blame] | 155 | if len(m) == 1 && len(m[0]) == 2 { |
| 156 | pid, err := strconv.Atoi(m[0][1]) |
| 157 | if err != nil { |
| 158 | t.Fatalf(testutil.FormatLogLine(2, "Atoi(%q) failed: %v", m[0][1], err)) |
| 159 | } |
| 160 | return pid |
| 161 | } |
| 162 | t.Fatalf(testutil.FormatLogLine(2, "failed to extract pid: %v", m)) |
| 163 | return 0 |
| 164 | } |
Robert Kroeger | d6e1d1a | 2014-12-10 15:08:45 -0800 | [diff] [blame] | 165 | |
| 166 | // SetupRootDir sets up and returns a directory for the root and returns |
| 167 | // a cleanup function. |
| 168 | func SetupRootDir(t *testing.T, prefix string) (string, func()) { |
| 169 | rootDir, err := ioutil.TempDir("", prefix) |
| 170 | if err != nil { |
| 171 | t.Fatalf("Failed to set up temporary dir for test: %v", err) |
| 172 | } |
| 173 | // On some operating systems (e.g. darwin) os.TempDir() can return a |
| 174 | // symlink. To avoid having to account for this eventuality later, |
| 175 | // evaluate the symlink. |
| 176 | rootDir, err = filepath.EvalSymlinks(rootDir) |
| 177 | if err != nil { |
Cosmos Nicolaou | 7a4221f | 2015-06-21 08:02:23 -0700 | [diff] [blame] | 178 | logger.Global().Fatalf("EvalSymlinks(%v) failed: %v", rootDir, err) |
Robert Kroeger | d6e1d1a | 2014-12-10 15:08:45 -0800 | [diff] [blame] | 179 | } |
| 180 | |
| 181 | return rootDir, func() { |
| 182 | if t.Failed() || os.Getenv(preserveWorkspaceEnv) != "" { |
| 183 | t.Logf("You can examine the %s workspace at %v", prefix, rootDir) |
| 184 | } else { |
| 185 | os.RemoveAll(rootDir) |
| 186 | } |
| 187 | } |
| 188 | } |