blob: ae44db11649919db7feef279710df6e29bcf0e75 [file] [log] [blame]
Jiri Simsad7616c92015-03-24 23:44:30 -07001// 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 Wang5fc36442015-04-07 15:15:27 -07005package servicetest
Robert Kroegerebfb62a2014-12-10 14:42:09 -08006
7import (
Matt Rosencrantz2d024632015-03-19 15:00:34 -07008 "fmt"
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -08009 "io/ioutil"
Robert Kroegerebfb62a2014-12-10 14:42:09 -080010 "os"
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -080011 "path/filepath"
Robert Kroegerebfb62a2014-12-10 14:42:09 -080012 "strconv"
13 "testing"
14
Jiri Simsa6ac95222015-02-23 16:11:49 -080015 "v.io/v23"
16 "v.io/v23/context"
Matt Rosencrantz2d024632015-03-19 15:00:34 -070017 "v.io/v23/options"
Jiri Simsa6ac95222015-02-23 16:11:49 -080018 "v.io/v23/security"
Todd Wang8123b5e2015-05-14 18:44:43 -070019 "v.io/x/ref"
Cosmos Nicolaou7a4221f2015-06-21 08:02:23 -070020 "v.io/x/ref/internal/logger"
Todd Wang5987a942015-04-06 11:06:17 -070021 "v.io/x/ref/services/mounttable/mounttablelib"
Cosmos Nicolaou1381f8a2015-03-13 09:40:34 -070022 "v.io/x/ref/test/modules"
Cosmos Nicolaou1381f8a2015-03-13 09:40:34 -070023 "v.io/x/ref/test/testutil"
Robert Kroegerebfb62a2014-12-10 14:42:09 -080024)
25
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -080026const (
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 Shankar3a6ca472015-03-31 00:35:37 -070031 preserveWorkspaceEnv = "V23_TEST_PRESERVE_WORKSPACE"
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -080032)
33
Todd Wang95873902015-05-22 14:21:30 -070034var rootMT = modules.Register(func(env *modules.Env, args ...string) error {
Matt Rosencrantz2d024632015-03-19 15:00:34 -070035 ctx, shutdown := v23.Init()
36 defer shutdown()
37
Cosmos Nicolaou7a4221f2015-06-21 08:02:23 -070038 mt, err := mounttablelib.NewMountTableDispatcher(ctx, "", "", "mounttable")
Matt Rosencrantz2d024632015-03-19 15:00:34 -070039 if err != nil {
Todd Wang5987a942015-04-06 11:06:17 -070040 return fmt.Errorf("mounttablelib.NewMountTableDispatcher failed: %s", err)
Matt Rosencrantz2d024632015-03-19 15:00:34 -070041 }
Matt Rosencrantz53ac5852015-09-04 15:14:54 -070042 ctx, server, err := v23.WithNewDispatchingServer(ctx, "", mt, options.ServesMountTable(true))
Matt Rosencrantz2d024632015-03-19 15:00:34 -070043 if err != nil {
Matt Rosencrantzbb6295d2015-06-19 15:13:58 -070044 return fmt.Errorf("root failed: %v", err)
Matt Rosencrantz2d024632015-03-19 15:00:34 -070045 }
Todd Wang95873902015-05-22 14:21:30 -070046 fmt.Fprintf(env.Stdout, "PID=%d\n", os.Getpid())
Matt Rosencrantzbb6295d2015-06-19 15:13:58 -070047 for _, ep := range server.Status().Endpoints {
Todd Wang95873902015-05-22 14:21:30 -070048 fmt.Fprintf(env.Stdout, "MT_NAME=%s\n", ep.Name())
Matt Rosencrantz2d024632015-03-19 15:00:34 -070049 }
Todd Wang95873902015-05-22 14:21:30 -070050 modules.WaitForEOF(env.Stdin)
Matt Rosencrantz2d024632015-03-19 15:00:34 -070051 return nil
Todd Wang95873902015-05-22 14:21:30 -070052}, "rootMT")
Matt Rosencrantz2d024632015-03-19 15:00:34 -070053
Asim Shankar11b530a2015-03-11 22:40:52 -070054// startRootMT sets up a root mount table for tests.
55func startRootMT(t *testing.T, sh *modules.Shell) (string, modules.Handle) {
Todd Wang95873902015-05-22 14:21:30 -070056 h, err := sh.Start(nil, rootMT, "--v23.tcp.address=127.0.0.1:0")
Robert Kroegerebfb62a2014-12-10 14:42:09 -080057 if err != nil {
58 t.Fatalf("failed to start root mount table: %s", err)
59 }
Cosmos Nicolaou42a17362015-03-10 16:40:18 -070060 h.ExpectVar("PID")
61 rootName := h.ExpectVar("MT_NAME")
Robert Kroegerebfb62a2014-12-10 14:42:09 -080062 if t.Failed() {
Cosmos Nicolaou42a17362015-03-10 16:40:18 -070063 t.Fatalf("failed to read mt name: %s", h.Error())
Robert Kroegerebfb62a2014-12-10 14:42:09 -080064 }
65 return rootName, h
66}
67
Asim Shankar11b530a2015-03-11 22:40:52 -070068// setNSRoots sets the roots for the local runtime's namespace.
69func setNSRoots(t *testing.T, ctx *context.T, roots ...string) {
Jiri Simsa6ac95222015-02-23 16:11:49 -080070 ns := v23.GetNamespace(ctx)
Matt Rosencrantz6edab562015-01-12 11:07:55 -080071 if err := ns.SetRoots(roots...); err != nil {
Robert Kroegerebfb62a2014-12-10 14:42:09 -080072 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 Browna08a2212015-01-15 15:40:10 -080078func CreateShellAndMountTable(t *testing.T, ctx *context.T, p security.Principal) (*modules.Shell, func()) {
Cosmos Nicolaou9e909842015-03-17 11:58:59 -070079 sh, err := modules.NewShell(ctx, p, testing.Verbose(), t)
Robert Kroegerebfb62a2014-12-10 14:42:09 -080080 if err != nil {
81 t.Fatalf("unexpected error: %s", err)
82 }
Cosmos Nicolaou42a17362015-03-10 16:40:18 -070083 opts := sh.DefaultStartOpts()
84 opts.ExpectTimeout = ExpectTimeout
85 sh.SetDefaultStartOpts(opts)
Robert Kroegerebfb62a2014-12-10 14:42:09 -080086 // The shell, will, by default share credentials with its children.
Todd Wang8123b5e2015-05-14 18:44:43 -070087 sh.ClearVar(ref.EnvCredentials)
Robert Kroegerebfb62a2014-12-10 14:42:09 -080088
Cosmos Nicolaouaa87e292015-04-21 22:15:50 -070089 mtName, _ := startRootMT(t, sh)
Cosmos Nicolaou7a4221f2015-06-21 08:02:23 -070090 ctx.VI(1).Infof("Started shell mounttable with name %v", mtName)
Robert Kroegerebfb62a2014-12-10 14:42:09 -080091
92 // TODO(caprita): Define a GetNamespaceRootsCommand in modules/core and
93 // use that?
Matt Rosencrantz6edab562015-01-12 11:07:55 -080094
Jiri Simsa6ac95222015-02-23 16:11:49 -080095 oldNamespaceRoots := v23.GetNamespace(ctx).Roots()
Robert Kroegerebfb62a2014-12-10 14:42:09 -080096 fn := func() {
Cosmos Nicolaou7a4221f2015-06-21 08:02:23 -070097 ctx.VI(1).Info("------------ CLEANUP ------------")
98 ctx.VI(1).Info("---------------------------------")
99 ctx.VI(1).Info("--(cleaning up shell)------------")
Robert Kroegerebfb62a2014-12-10 14:42:09 -0800100 if err := sh.Cleanup(os.Stdout, os.Stderr); err != nil {
Cosmos Nicolaouaa87e292015-04-21 22:15:50 -0700101 t.Errorf(testutil.FormatLogLine(2, "sh.Cleanup failed with %v", err))
Robert Kroegerebfb62a2014-12-10 14:42:09 -0800102 }
Cosmos Nicolaou7a4221f2015-06-21 08:02:23 -0700103 ctx.VI(1).Info("--(done cleaning up shell)-------")
Asim Shankar11b530a2015-03-11 22:40:52 -0700104 setNSRoots(t, ctx, oldNamespaceRoots...)
Robert Kroegerebfb62a2014-12-10 14:42:09 -0800105 }
Asim Shankar11b530a2015-03-11 22:40:52 -0700106 setNSRoots(t, ctx, mtName)
Todd Wang8123b5e2015-05-14 18:44:43 -0700107 sh.SetVar(ref.EnvNamespacePrefix, mtName)
Robert Kroegerebfb62a2014-12-10 14:42:09 -0800108 return sh, fn
109}
110
Bogdan Caprita13562772015-06-26 15:40:27 -0700111// CreateShell builds a new modules shell.
112func 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 Nicolaou42a17362015-03-10 16:40:18 -0700140// RunCommand runs a modules command.
Todd Wang95873902015-05-22 14:21:30 -0700141func 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 Kroegerebfb62a2014-12-10 14:42:09 -0800143 if err != nil {
Todd Wang95873902015-05-22 14:21:30 -0700144 t.Fatalf(testutil.FormatLogLine(2, "failed to start %q: %s", prog, err))
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700145 return nil
Robert Kroegerebfb62a2014-12-10 14:42:09 -0800146 }
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700147 h.SetVerbosity(testing.Verbose())
148 return h
Robert Kroegerebfb62a2014-12-10 14:42:09 -0800149}
150
Robert Kroegerebfb62a2014-12-10 14:42:09 -0800151// ReadPID waits for the "ready:<PID>" line from the child and parses out the
152// PID of the child.
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700153func ReadPID(t *testing.T, h modules.ExpectSession) int {
154 m := h.ExpectRE("ready:([0-9]+)", -1)
Robert Kroegerebfb62a2014-12-10 14:42:09 -0800155 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 Kroegerd6e1d1a2014-12-10 15:08:45 -0800165
166// SetupRootDir sets up and returns a directory for the root and returns
167// a cleanup function.
168func 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 Nicolaou7a4221f2015-06-21 08:02:23 -0700178 logger.Global().Fatalf("EvalSymlinks(%v) failed: %v", rootDir, err)
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800179 }
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}