blob: 025a4c0b22753a01757e53992b54b33a88b9e1e8 [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
Cosmos Nicolaou62613842014-08-25 21:57:37 -07005package modules_test
6
7import (
8 "bufio"
Cosmos Nicolaoubbae3882014-10-02 22:58:19 -07009 "bytes"
Cosmos Nicolaou62613842014-08-25 21:57:37 -070010 "fmt"
11 "io"
Asim Shankar4a698282015-03-21 21:59:18 -070012 "io/ioutil"
Cosmos Nicolaou42a17362015-03-10 16:40:18 -070013 "math/rand"
Cosmos Nicolaou1e78ccc2014-10-09 08:10:26 -070014 "os"
Cosmos Nicolaoud4f00562014-11-17 20:35:48 -080015 "reflect"
Cosmos Nicolaou5339a7b2014-11-18 17:28:00 -080016 "sort"
Cosmos Nicolaou42a17362015-03-10 16:40:18 -070017 "strconv"
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -070018 "strings"
Bogdan Caprita490a4512014-11-20 21:12:19 -080019 "syscall"
Cosmos Nicolaou62613842014-08-25 21:57:37 -070020 "testing"
21 "time"
22
Cosmos Nicolaou1381f8a2015-03-13 09:40:34 -070023 "v.io/v23"
Mike Burrowsccca2f42015-03-27 13:57:29 -070024 "v.io/v23/verror"
Cosmos Nicolaou1381f8a2015-03-13 09:40:34 -070025
Asim Shankar59b8b692015-03-30 01:23:36 -070026 "v.io/x/ref/envvar"
Jiri Simsaffceefa2015-02-28 11:03:34 -080027 "v.io/x/ref/lib/exec"
28 execconsts "v.io/x/ref/lib/exec/consts"
Jiri Simsaffceefa2015-02-28 11:03:34 -080029 _ "v.io/x/ref/profiles"
Asim Shankar4a698282015-03-21 21:59:18 -070030 vsecurity "v.io/x/ref/security"
Cosmos Nicolaou1381f8a2015-03-13 09:40:34 -070031 "v.io/x/ref/test"
32 "v.io/x/ref/test/modules"
Asim Shankar4a698282015-03-21 21:59:18 -070033 "v.io/x/ref/test/testutil"
Cosmos Nicolaou62613842014-08-25 21:57:37 -070034)
35
36func init() {
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -070037 modules.RegisterChild("envtest", "envtest: <variables to print>...", PrintFromEnv)
38 modules.RegisterChild("printenv", "printenv", PrintEnv)
Ryan Browna08a2212015-01-15 15:40:10 -080039 modules.RegisterChild("printblessing", "printblessing", PrintBlessing)
Cosmos Nicolaoud4f00562014-11-17 20:35:48 -080040 modules.RegisterChild("echos", "[args]*", Echo)
41 modules.RegisterChild("errortestChild", "", ErrorMain)
Bogdan Caprita490a4512014-11-20 21:12:19 -080042 modules.RegisterChild("ignores_stdin", "", ignoresStdin)
Cosmos Nicolaou42a17362015-03-10 16:40:18 -070043 modules.RegisterChild("pipeProc", "", pipeEcho)
44 modules.RegisterChild("lifo", "", lifo)
Cosmos Nicolaoud4f00562014-11-17 20:35:48 -080045
46 modules.RegisterFunction("envtestf", "envtest: <variables to print>...", PrintFromEnv)
47 modules.RegisterFunction("echof", "[args]*", Echo)
48 modules.RegisterFunction("errortestFunc", "", ErrorMain)
Cosmos Nicolaou42a17362015-03-10 16:40:18 -070049 modules.RegisterFunction("pipeFunc", "", pipeEcho)
Bogdan Caprita490a4512014-11-20 21:12:19 -080050}
Cosmos Nicolaoud4f00562014-11-17 20:35:48 -080051
Suharsh Sivakumard19c95d2015-02-19 14:44:50 -080052// We must call Testmain ourselves because using v23 test generate
53// creates an import cycle for this package.
54func TestMain(m *testing.M) {
Cosmos Nicolaou1381f8a2015-03-13 09:40:34 -070055 test.Init()
Cosmos Nicolaou42a17362015-03-10 16:40:18 -070056 if modules.IsModulesChildProcess() {
Suharsh Sivakumard19c95d2015-02-19 14:44:50 -080057 if err := modules.Dispatch(); err != nil {
58 fmt.Fprintf(os.Stderr, "modules.Dispatch failed: %v\n", err)
59 os.Exit(1)
60 }
61 return
62 }
63 os.Exit(m.Run())
64}
65
Bogdan Caprita490a4512014-11-20 21:12:19 -080066func ignoresStdin(io.Reader, io.Writer, io.Writer, map[string]string, ...string) error {
67 <-time.After(time.Minute)
68 return nil
Cosmos Nicolaou62613842014-08-25 21:57:37 -070069}
70
Cosmos Nicolaoubbae3882014-10-02 22:58:19 -070071func Echo(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
72 for _, a := range args {
73 fmt.Fprintf(stdout, "stdout: %s\n", a)
74 fmt.Fprintf(stderr, "stderr: %s\n", a)
75 }
76 return nil
77}
78
Cosmos Nicolaou42a17362015-03-10 16:40:18 -070079func pipeEcho(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
80 scanner := bufio.NewScanner(stdin)
81 for scanner.Scan() {
82 fmt.Fprintf(stdout, "%p: %s\n", pipeEcho, scanner.Text())
83 }
84 return nil
85}
86
87func lifo(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
88 scanner := bufio.NewScanner(stdin)
89 scanner.Scan()
90 msg := scanner.Text()
91 modules.WaitForEOF(stdin)
92 fmt.Fprintf(stdout, "%p: %s\n", lifo, msg)
93 return nil
94}
95
Ryan Browna08a2212015-01-15 15:40:10 -080096func PrintBlessing(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
Cosmos Nicolaou1381f8a2015-03-13 09:40:34 -070097 ctx, shutdown := test.InitForTest()
Suharsh Sivakumar19fbf992015-01-23 11:02:27 -080098 defer shutdown()
99
Jiri Simsa6ac95222015-02-23 16:11:49 -0800100 blessing := v23.GetPrincipal(ctx).BlessingStore().Default()
Ryan Browna08a2212015-01-15 15:40:10 -0800101 fmt.Fprintf(stdout, "%s", blessing)
102 return nil
103}
104
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -0700105func PrintFromEnv(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -0800106 for _, a := range args {
Cosmos Nicolaoubbae3882014-10-02 22:58:19 -0700107 if v := env[a]; len(v) > 0 {
Cosmos Nicolaou74cc9b22014-10-23 15:25:35 -0700108 fmt.Fprintf(stdout, "%s\n", a+"="+v)
Cosmos Nicolaoubbae3882014-10-02 22:58:19 -0700109 } else {
110 fmt.Fprintf(stderr, "missing %s\n", a)
111 }
112 }
113 modules.WaitForEOF(stdin)
114 fmt.Fprintf(stdout, "done\n")
115 return nil
116}
117
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -0700118const printEnvArgPrefix = "PRINTENV_ARG="
119
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700120func PrintEnv(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
121 for _, a := range args {
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -0700122 fmt.Fprintf(stdout, "%s%s\n", printEnvArgPrefix, a)
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700123 }
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -0700124 for k, v := range env {
Cosmos Nicolaou74cc9b22014-10-23 15:25:35 -0700125 fmt.Fprintf(stdout, "%q\n", k+"="+v)
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -0700126 }
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700127 return nil
128}
129
Cosmos Nicolaou66afced2014-09-15 22:12:43 -0700130func ErrorMain(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
131 return fmt.Errorf("an error")
132}
133
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700134func waitForInput(scanner *bufio.Scanner) bool {
135 ch := make(chan struct{})
136 go func(ch chan<- struct{}) {
137 scanner.Scan()
138 ch <- struct{}{}
139 }(ch)
140 select {
141 case <-ch:
142 return true
143 case <-time.After(10 * time.Second):
144 return false
145 }
146}
147
148func testCommand(t *testing.T, sh *modules.Shell, name, key, val string) {
Cosmos Nicolaou612ad382014-10-29 19:41:35 -0700149 h, err := sh.Start(name, nil, key)
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700150 if err != nil {
151 t.Fatalf("unexpected error: %s", err)
152 }
153 defer func() {
Cosmos Nicolaoubbae3882014-10-02 22:58:19 -0700154 var stdout, stderr bytes.Buffer
155 sh.Cleanup(&stdout, &stderr)
Cosmos Nicolaou9e909842015-03-17 11:58:59 -0700156 want := ""
157 if testing.Verbose() {
158 want = "---- Shell Cleanup ----\n"
Cosmos Nicolaoubbae3882014-10-02 22:58:19 -0700159 }
Cosmos Nicolaou9e909842015-03-17 11:58:59 -0700160 if got := stdout.String(); got != "" && got != want {
161 t.Errorf("got %q, want %q", got, want)
162 }
163 if got := stderr.String(); got != "" && got != want {
164 t.Errorf("got %q, want %q", got, want)
Cosmos Nicolaoubbae3882014-10-02 22:58:19 -0700165 }
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700166 }()
167 scanner := bufio.NewScanner(h.Stdout())
168 if !waitForInput(scanner) {
169 t.Errorf("timeout")
170 return
171 }
172 if got, want := scanner.Text(), key+"="+val; got != want {
173 t.Errorf("got %q, want %q", got, want)
174 }
Cosmos Nicolaou66afced2014-09-15 22:12:43 -0700175 h.CloseStdin()
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700176 if !waitForInput(scanner) {
Cosmos Nicolaou2cb05fb2014-10-23 13:37:32 -0700177 t.Fatalf("timeout")
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700178 return
179 }
180 if got, want := scanner.Text(), "done"; got != want {
181 t.Errorf("got %q, want %q", got, want)
182 }
Cosmos Nicolaoubbae3882014-10-02 22:58:19 -0700183 if err := h.Shutdown(nil, nil); err != nil {
Cosmos Nicolaou66afced2014-09-15 22:12:43 -0700184 t.Fatalf("unexpected error: %s", err)
185 }
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700186}
187
Ryan Browna08a2212015-01-15 15:40:10 -0800188func getBlessing(t *testing.T, sh *modules.Shell, env ...string) string {
189 h, err := sh.Start("printblessing", env)
190 if err != nil {
191 t.Fatalf("unexpected error: %s", err)
192 }
193 scanner := bufio.NewScanner(h.Stdout())
194 if !waitForInput(scanner) {
195 t.Errorf("timeout")
196 return ""
197 }
198 return scanner.Text()
199}
200
Ryan Brown61d69382015-02-25 11:13:39 -0800201func getCustomBlessing(t *testing.T, sh *modules.Shell, creds *modules.CustomCredentials) string {
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700202 h, err := sh.StartWithOpts(sh.DefaultStartOpts().WithCustomCredentials(creds), nil, "printblessing")
Ryan Brown61d69382015-02-25 11:13:39 -0800203 if err != nil {
204 t.Fatalf("unexpected error: %s", err)
205 }
206 scanner := bufio.NewScanner(h.Stdout())
207 if !waitForInput(scanner) {
208 t.Errorf("timeout")
209 return ""
210 }
211 return scanner.Text()
212}
213
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700214func TestChild(t *testing.T) {
Cosmos Nicolaou1381f8a2015-03-13 09:40:34 -0700215 ctx, shutdown := test.InitForTest()
Suharsh Sivakumar19fbf992015-01-23 11:02:27 -0800216 defer shutdown()
217
Cosmos Nicolaou9e909842015-03-17 11:58:59 -0700218 sh, err := modules.NewShell(ctx, nil, testing.Verbose(), t)
Cosmos Nicolaou344cc4a2014-11-26 15:38:43 -0800219 if err != nil {
220 t.Fatalf("unexpected error: %s", err)
221 }
Cosmos Nicolaoubbae3882014-10-02 22:58:19 -0700222 defer sh.Cleanup(nil, nil)
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700223 key, val := "simpleVar", "foo & bar"
224 sh.SetVar(key, val)
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700225 testCommand(t, sh, "envtest", key, val)
226}
227
Ryan Browna08a2212015-01-15 15:40:10 -0800228func TestAgent(t *testing.T) {
Cosmos Nicolaou1381f8a2015-03-13 09:40:34 -0700229 ctx, shutdown := test.InitForTest()
Suharsh Sivakumar19fbf992015-01-23 11:02:27 -0800230 defer shutdown()
231
Cosmos Nicolaou9e909842015-03-17 11:58:59 -0700232 sh, err := modules.NewShell(ctx, nil, testing.Verbose(), t)
Ryan Browna08a2212015-01-15 15:40:10 -0800233 if err != nil {
234 t.Fatalf("unexpected error: %s", err)
235 }
236 defer sh.Cleanup(os.Stdout, os.Stderr)
237 a := getBlessing(t, sh)
238 b := getBlessing(t, sh)
239 if a != b {
240 t.Errorf("Expected same blessing for children, got %s and %s", a, b)
241 }
Ryan Browna08a2212015-01-15 15:40:10 -0800242}
243
244func TestCustomPrincipal(t *testing.T) {
Cosmos Nicolaou1381f8a2015-03-13 09:40:34 -0700245 ctx, shutdown := test.InitForTest()
Suharsh Sivakumar19fbf992015-01-23 11:02:27 -0800246 defer shutdown()
247
Asim Shankar4a698282015-03-21 21:59:18 -0700248 p, err := vsecurity.NewPrincipal()
249 if err != nil {
250 t.Fatal(err)
251 }
252 b, err := p.BlessSelf("myshell")
253 if err != nil {
254 t.Fatal(err)
255 }
256 if err := vsecurity.SetDefaultBlessings(p, b); err != nil {
257 t.Fatal(err)
258 }
Ryan Browna08a2212015-01-15 15:40:10 -0800259 cleanDebug := p.BlessingStore().DebugString()
Cosmos Nicolaou9e909842015-03-17 11:58:59 -0700260 sh, err := modules.NewShell(ctx, p, testing.Verbose(), t)
Ryan Browna08a2212015-01-15 15:40:10 -0800261 if err != nil {
262 t.Fatalf("unexpected error: %s", err)
263 }
264 defer sh.Cleanup(os.Stdout, os.Stderr)
Asim Shankar4a698282015-03-21 21:59:18 -0700265 if got, want := getBlessing(t, sh), "myshell/child"; got != want {
266 t.Errorf("Bad blessing. Got %q, want %q", got, want)
Ryan Browna08a2212015-01-15 15:40:10 -0800267 }
268 newDebug := p.BlessingStore().DebugString()
269 if cleanDebug != newDebug {
270 t.Errorf("Shell modified custom principal. Was:\n%q\nNow:\n%q", cleanDebug, newDebug)
271 }
272}
273
Ryan Brown61d69382015-02-25 11:13:39 -0800274func TestCustomCredentials(t *testing.T) {
Cosmos Nicolaou1381f8a2015-03-13 09:40:34 -0700275 ctx, shutdown := test.InitForTest()
Ryan Brown61d69382015-02-25 11:13:39 -0800276 defer shutdown()
277
Asim Shankar4a698282015-03-21 21:59:18 -0700278 root := testutil.NewIDProvider("myshell")
Cosmos Nicolaou9e909842015-03-17 11:58:59 -0700279 sh, err := modules.NewShell(ctx, nil, testing.Verbose(), t)
Ryan Brown61d69382015-02-25 11:13:39 -0800280 if err != nil {
281 t.Fatal(err)
282 }
283 defer sh.Cleanup(os.Stdout, os.Stderr)
284
285 newCreds := func(ext string) *modules.CustomCredentials {
286 p, err := sh.NewCustomCredentials()
287 if err != nil {
288 t.Fatal(err)
289 }
290 b, err := root.NewBlessings(p.Principal(), ext)
291 if err != nil {
292 t.Fatal(err)
293 }
Asim Shankar4a698282015-03-21 21:59:18 -0700294 if err := vsecurity.SetDefaultBlessings(p.Principal(), b); err != nil {
295 t.Fatal(err)
296 }
Ryan Brown61d69382015-02-25 11:13:39 -0800297 return p
298 }
299
300 a := newCreds("a")
301 cleanDebug := a.Principal().BlessingStore().DebugString()
302
303 blessing := getCustomBlessing(t, sh, a)
304 if blessing != "myshell/a" {
305 t.Errorf("Bad blessing. Expected myshell/a, go %q", blessing)
306 }
307
308 b := newCreds("bar")
309 blessing = getCustomBlessing(t, sh, b)
310 if blessing != "myshell/bar" {
311 t.Errorf("Bad blessing. Expected myshell/bar, go %q", blessing)
312 }
313
314 // Make sure we can re-use credentials
315 blessing = getCustomBlessing(t, sh, a)
316 if blessing != "myshell/a" {
317 t.Errorf("Bad blessing. Expected myshell/a, go %q", blessing)
318 }
319
320 newDebug := a.Principal().BlessingStore().DebugString()
321 if cleanDebug != newDebug {
322 t.Errorf("Shell modified custom principal. Was:\n%q\nNow:\n%q", cleanDebug, newDebug)
323 }
324}
325
Asim Shankar4a698282015-03-21 21:59:18 -0700326func createCredentials(blessing string) (string, error) {
327 dir, err := ioutil.TempDir("", "TestNoAgent_v23_credentials")
328 if err != nil {
329 return "", err
330 }
331 p, err := vsecurity.CreatePersistentPrincipal(dir, nil)
332 if err != nil {
333 os.RemoveAll(dir)
334 return "", err
335 }
336 b, err := p.BlessSelf(blessing)
337 if err != nil {
338 os.RemoveAll(dir)
339 return "", err
340 }
341 if err := vsecurity.SetDefaultBlessings(p, b); err != nil {
342 os.RemoveAll(dir)
343 return "", err
344 }
345 return dir, nil
346}
347
Ryan Browna08a2212015-01-15 15:40:10 -0800348func TestNoAgent(t *testing.T) {
Asim Shankar4a698282015-03-21 21:59:18 -0700349 const noagent = "noagent"
350 creds, err := createCredentials(noagent)
351 if err != nil {
352 t.Fatal(err)
353 }
Ryan Browna08a2212015-01-15 15:40:10 -0800354 defer os.RemoveAll(creds)
Cosmos Nicolaou9e909842015-03-17 11:58:59 -0700355 sh, err := modules.NewShell(nil, nil, testing.Verbose(), t)
Ryan Browna08a2212015-01-15 15:40:10 -0800356 if err != nil {
357 t.Fatalf("unexpected error: %s", err)
358 }
359 defer sh.Cleanup(os.Stdout, os.Stderr)
Asim Shankar59b8b692015-03-30 01:23:36 -0700360 if got, want := getBlessing(t, sh, fmt.Sprintf("%s=%s", envvar.Credentials, creds)), noagent; got != want {
Asim Shankar4a698282015-03-21 21:59:18 -0700361 t.Errorf("Bad blessing. Got %q, want %q", got, want)
Ryan Browna08a2212015-01-15 15:40:10 -0800362 }
363}
364
Cosmos Nicolaou1e78ccc2014-10-09 08:10:26 -0700365func TestChildNoRegistration(t *testing.T) {
Cosmos Nicolaou1381f8a2015-03-13 09:40:34 -0700366 ctx, shutdown := test.InitForTest()
Suharsh Sivakumar19fbf992015-01-23 11:02:27 -0800367 defer shutdown()
368
Cosmos Nicolaou9e909842015-03-17 11:58:59 -0700369 sh, err := modules.NewShell(ctx, nil, testing.Verbose(), t)
Cosmos Nicolaou344cc4a2014-11-26 15:38:43 -0800370 if err != nil {
371 t.Fatalf("unexpected error: %s", err)
372 }
Cosmos Nicolaou1e78ccc2014-10-09 08:10:26 -0700373 defer sh.Cleanup(os.Stderr, os.Stderr)
374 key, val := "simpleVar", "foo & bar"
375 sh.SetVar(key, val)
376 testCommand(t, sh, "envtest", key, val)
Cosmos Nicolaou344cc4a2014-11-26 15:38:43 -0800377 _, err = sh.Start("non-existent-command", nil, "random", "args")
Cosmos Nicolaoud4f00562014-11-17 20:35:48 -0800378 if err == nil {
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700379 fmt.Fprintf(os.Stderr, "Failed: %v\n", err)
Cosmos Nicolaoud4f00562014-11-17 20:35:48 -0800380 t.Fatalf("expected error")
Cosmos Nicolaou1e78ccc2014-10-09 08:10:26 -0700381 }
382}
383
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700384func TestFunction(t *testing.T) {
Cosmos Nicolaou1381f8a2015-03-13 09:40:34 -0700385 ctx, shutdown := test.InitForTest()
Suharsh Sivakumar19fbf992015-01-23 11:02:27 -0800386 defer shutdown()
387
Cosmos Nicolaou9e909842015-03-17 11:58:59 -0700388 sh, err := modules.NewShell(ctx, nil, testing.Verbose(), t)
Cosmos Nicolaou344cc4a2014-11-26 15:38:43 -0800389 if err != nil {
390 t.Fatalf("unexpected error: %s", err)
391 }
Cosmos Nicolaoubbae3882014-10-02 22:58:19 -0700392 defer sh.Cleanup(nil, nil)
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700393 key, val := "simpleVar", "foo & bar & baz"
394 sh.SetVar(key, val)
Cosmos Nicolaou1e78ccc2014-10-09 08:10:26 -0700395 testCommand(t, sh, "envtestf", key, val)
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700396}
397
Cosmos Nicolaou66afced2014-09-15 22:12:43 -0700398func TestErrorChild(t *testing.T) {
Cosmos Nicolaou1381f8a2015-03-13 09:40:34 -0700399 ctx, shutdown := test.InitForTest()
Suharsh Sivakumar19fbf992015-01-23 11:02:27 -0800400 defer shutdown()
401
Cosmos Nicolaou9e909842015-03-17 11:58:59 -0700402 sh, err := modules.NewShell(ctx, nil, testing.Verbose(), t)
Cosmos Nicolaou344cc4a2014-11-26 15:38:43 -0800403 if err != nil {
404 t.Fatalf("unexpected error: %s", err)
405 }
Cosmos Nicolaoubbae3882014-10-02 22:58:19 -0700406 defer sh.Cleanup(nil, nil)
Cosmos Nicolaoud4f00562014-11-17 20:35:48 -0800407 h, err := sh.Start("errortestChild", nil)
Cosmos Nicolaou66afced2014-09-15 22:12:43 -0700408 if err != nil {
409 t.Fatalf("unexpected error: %s", err)
410 }
Suharsh Sivakumard19c95d2015-02-19 14:44:50 -0800411 if got, want := h.Shutdown(nil, nil), "exit status 1"; got == nil || got.Error() != want {
Cosmos Nicolaou66afced2014-09-15 22:12:43 -0700412 t.Errorf("got %q, want %q", got, want)
413 }
414}
415
Cosmos Nicolaoud4f00562014-11-17 20:35:48 -0800416func testShutdown(t *testing.T, sh *modules.Shell, command string, isfunc bool) {
Cosmos Nicolaoubbae3882014-10-02 22:58:19 -0700417 result := ""
418 args := []string{"a", "b c", "ddd"}
Cosmos Nicolaoud4f00562014-11-17 20:35:48 -0800419 if _, err := sh.Start(command, nil, args...); err != nil {
Cosmos Nicolaoubbae3882014-10-02 22:58:19 -0700420 t.Fatalf("unexpected error: %s", err)
421 }
422 var stdoutBuf bytes.Buffer
423 var stderrBuf bytes.Buffer
424 sh.Cleanup(&stdoutBuf, &stderrBuf)
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -0800425 var stdoutOutput, stderrOutput string
Cosmos Nicolaoubbae3882014-10-02 22:58:19 -0700426 for _, a := range args {
427 stdoutOutput += fmt.Sprintf("stdout: %s\n", a)
428 stderrOutput += fmt.Sprintf("stderr: %s\n", a)
429 }
430 if got, want := stdoutBuf.String(), stdoutOutput+result; got != want {
431 t.Errorf("got %q want %q", got, want)
432 }
Cosmos Nicolaou2cb05fb2014-10-23 13:37:32 -0700433 if !isfunc {
434 stderrBuf.ReadString('\n') // Skip past the random # generator output
435 }
Cosmos Nicolaoubbae3882014-10-02 22:58:19 -0700436 if got, want := stderrBuf.String(), stderrOutput; got != want {
437 t.Errorf("got %q want %q", got, want)
438 }
Cosmos Nicolaoubbae3882014-10-02 22:58:19 -0700439}
440
441func TestShutdownSubprocess(t *testing.T) {
Cosmos Nicolaou1381f8a2015-03-13 09:40:34 -0700442 ctx, shutdown := test.InitForTest()
Suharsh Sivakumar19fbf992015-01-23 11:02:27 -0800443 defer shutdown()
444
Cosmos Nicolaou9e909842015-03-17 11:58:59 -0700445 sh, err := modules.NewShell(ctx, nil, false, t)
Cosmos Nicolaou344cc4a2014-11-26 15:38:43 -0800446 if err != nil {
447 t.Fatalf("unexpected error: %s", err)
448 }
Robin Thellendcf140c02014-12-08 14:56:24 -0800449 defer sh.Cleanup(nil, nil)
Cosmos Nicolaou344cc4a2014-11-26 15:38:43 -0800450 testShutdown(t, sh, "echos", false)
Cosmos Nicolaoubbae3882014-10-02 22:58:19 -0700451}
452
Bogdan Caprita490a4512014-11-20 21:12:19 -0800453// TestShutdownSubprocessIgnoresStdin verifies that Shutdown doesn't wait
454// forever if a child does not die upon closing stdin; but instead times out and
455// returns an appropriate error.
456func TestShutdownSubprocessIgnoresStdin(t *testing.T) {
Cosmos Nicolaou1381f8a2015-03-13 09:40:34 -0700457 ctx, shutdown := test.InitForTest()
Suharsh Sivakumar19fbf992015-01-23 11:02:27 -0800458 defer shutdown()
459
Cosmos Nicolaou9e909842015-03-17 11:58:59 -0700460 sh, err := modules.NewShell(ctx, nil, false, t)
Cosmos Nicolaou344cc4a2014-11-26 15:38:43 -0800461 if err != nil {
462 t.Fatalf("unexpected error: %s", err)
463 }
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700464 opts := sh.DefaultStartOpts()
465 opts.ShutdownTimeout = time.Second
466 h, err := sh.StartWithOpts(opts, nil, "ignores_stdin")
Bogdan Caprita490a4512014-11-20 21:12:19 -0800467 if err != nil {
468 t.Fatalf("unexpected error: %s", err)
469 }
470 var stdoutBuf, stderrBuf bytes.Buffer
Mike Burrowsccca2f42015-03-27 13:57:29 -0700471 if err := sh.Cleanup(&stdoutBuf, &stderrBuf); err == nil || verror.ErrorID(err) != exec.ErrTimeout.ID {
472 t.Errorf("unexpected error in Cleanup: got %v, want %v", err, exec.ErrTimeout.ID)
Bogdan Caprita490a4512014-11-20 21:12:19 -0800473 }
474 if err := syscall.Kill(h.Pid(), syscall.SIGINT); err != nil {
475 t.Errorf("Kill failed: %v", err)
476 }
477}
478
479// TestStdoutRace exemplifies a potential race between reading from child's
480// stdout and closing stdout in Wait (called by Shutdown).
481//
482// NOTE: triggering the actual --race failure is hard, even if the
483// implementation inappropriately sets stdout to the file that is to be closed
484// in Wait.
485func TestStdoutRace(t *testing.T) {
Cosmos Nicolaou1381f8a2015-03-13 09:40:34 -0700486 ctx, shutdown := test.InitForTest()
Suharsh Sivakumar19fbf992015-01-23 11:02:27 -0800487 defer shutdown()
488
Cosmos Nicolaou9e909842015-03-17 11:58:59 -0700489 sh, err := modules.NewShell(ctx, nil, testing.Verbose(), t)
Cosmos Nicolaou344cc4a2014-11-26 15:38:43 -0800490 if err != nil {
491 t.Fatalf("unexpected error: %s", err)
492 }
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700493 opts := sh.DefaultStartOpts()
494 opts.ShutdownTimeout = time.Second
495 h, err := sh.StartWithOpts(opts, nil, "ignores_stdin")
Bogdan Caprita490a4512014-11-20 21:12:19 -0800496 if err != nil {
497 t.Fatalf("unexpected error: %s", err)
498 }
499 ch := make(chan error, 1)
500 go func() {
501 buf := make([]byte, 5)
502 // This will block since the child is not writing anything on
503 // stdout.
504 _, err := h.Stdout().Read(buf)
505 ch <- err
506 }()
507 // Give the goroutine above a chance to run, so that we're blocked on
508 // stdout.Read.
509 <-time.After(time.Second)
510 // Cleanup should close stdout, and unblock the goroutine.
511 sh.Cleanup(nil, nil)
512 if got, want := <-ch, io.EOF; got != want {
513 t.Errorf("Expected %v, got %v instead", want, got)
514 }
515
516 if err := syscall.Kill(h.Pid(), syscall.SIGINT); err != nil {
517 t.Errorf("Kill failed: %v", err)
518 }
519}
520
Cosmos Nicolaoubbae3882014-10-02 22:58:19 -0700521func TestShutdownFunction(t *testing.T) {
Cosmos Nicolaou1381f8a2015-03-13 09:40:34 -0700522 ctx, shutdown := test.InitForTest()
Suharsh Sivakumar19fbf992015-01-23 11:02:27 -0800523 defer shutdown()
524
Cosmos Nicolaou9e909842015-03-17 11:58:59 -0700525 sh, err := modules.NewShell(ctx, nil, false, t)
Cosmos Nicolaou344cc4a2014-11-26 15:38:43 -0800526 if err != nil {
527 t.Fatalf("unexpected error: %s", err)
528 }
Robin Thellendcf140c02014-12-08 14:56:24 -0800529 defer sh.Cleanup(nil, nil)
Cosmos Nicolaou344cc4a2014-11-26 15:38:43 -0800530 testShutdown(t, sh, "echof", true)
Cosmos Nicolaoubbae3882014-10-02 22:58:19 -0700531}
532
Cosmos Nicolaou66afced2014-09-15 22:12:43 -0700533func TestErrorFunc(t *testing.T) {
Cosmos Nicolaou1381f8a2015-03-13 09:40:34 -0700534 ctx, shutdown := test.InitForTest()
Suharsh Sivakumar19fbf992015-01-23 11:02:27 -0800535 defer shutdown()
536
Cosmos Nicolaou9e909842015-03-17 11:58:59 -0700537 sh, err := modules.NewShell(ctx, nil, testing.Verbose(), t)
Cosmos Nicolaou344cc4a2014-11-26 15:38:43 -0800538 if err != nil {
539 t.Fatalf("unexpected error: %s", err)
540 }
Cosmos Nicolaoubbae3882014-10-02 22:58:19 -0700541 defer sh.Cleanup(nil, nil)
Cosmos Nicolaoud4f00562014-11-17 20:35:48 -0800542 h, err := sh.Start("errortestFunc", nil)
Cosmos Nicolaou66afced2014-09-15 22:12:43 -0700543 if err != nil {
544 t.Fatalf("unexpected error: %s", err)
545 }
Cosmos Nicolaoubbae3882014-10-02 22:58:19 -0700546 if got, want := h.Shutdown(nil, nil), "an error"; got != nil && got.Error() != want {
Cosmos Nicolaou66afced2014-09-15 22:12:43 -0700547 t.Errorf("got %q, want %q", got, want)
548 }
549}
550
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -0700551func find(want string, in []string) bool {
552 for _, a := range in {
553 if a == want {
554 return true
555 }
556 }
557 return false
558}
559
560func TestEnvelope(t *testing.T) {
Cosmos Nicolaou1381f8a2015-03-13 09:40:34 -0700561 ctx, shutdown := test.InitForTest()
Suharsh Sivakumar19fbf992015-01-23 11:02:27 -0800562 defer shutdown()
563
Cosmos Nicolaou9e909842015-03-17 11:58:59 -0700564 sh, err := modules.NewShell(ctx, nil, testing.Verbose(), t)
Cosmos Nicolaou344cc4a2014-11-26 15:38:43 -0800565 if err != nil {
566 t.Fatalf("unexpected error: %s", err)
567 }
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -0700568 defer sh.Cleanup(nil, nil)
569 sh.SetVar("a", "1")
570 sh.SetVar("b", "2")
571 args := []string{"oh", "ah"}
Cosmos Nicolaou612ad382014-10-29 19:41:35 -0700572 h, err := sh.Start("printenv", nil, args...)
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -0700573 if err != nil {
574 t.Fatalf("unexpected error: %s", err)
575 }
576 scanner := bufio.NewScanner(h.Stdout())
577 childArgs, childEnv := []string{}, []string{}
578 for scanner.Scan() {
579 o := scanner.Text()
580 if strings.HasPrefix(o, printEnvArgPrefix) {
581 childArgs = append(childArgs, strings.TrimPrefix(o, printEnvArgPrefix))
582 } else {
583 childEnv = append(childEnv, o)
584 }
585 }
Cosmos Nicolaou2cb05fb2014-10-23 13:37:32 -0700586 shArgs, shEnv := sh.CommandEnvelope("printenv", nil, args...)
Cosmos Nicolaou74cc9b22014-10-23 15:25:35 -0700587 for i, ev := range shEnv {
588 shEnv[i] = fmt.Sprintf("%q", ev)
589 }
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -0700590 for _, want := range args {
591 if !find(want, childArgs) {
592 t.Errorf("failed to find %q in %s", want, childArgs)
593 }
594 if !find(want, shArgs) {
595 t.Errorf("failed to find %q in %s", want, shArgs)
596 }
597 }
598
599 for _, want := range shEnv {
600 if !find(want, childEnv) {
Cosmos Nicolaou74cc9b22014-10-23 15:25:35 -0700601 t.Errorf("failed to find %s in %#v", want, childEnv)
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -0700602 }
603 }
Ankur9f957942014-11-24 16:34:18 -0800604
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -0700605 for _, want := range childEnv {
Cosmos Nicolaoua6fef892015-02-20 23:09:03 -0800606 if want == "\""+execconsts.ExecVersionVariable+"=\"" {
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -0700607 continue
608 }
609 if !find(want, shEnv) {
Cosmos Nicolaou74cc9b22014-10-23 15:25:35 -0700610 t.Errorf("failed to find %s in %#v", want, shEnv)
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -0700611 }
612 }
613}
614
615func TestEnvMerge(t *testing.T) {
Cosmos Nicolaou1381f8a2015-03-13 09:40:34 -0700616 ctx, shutdown := test.InitForTest()
Suharsh Sivakumar19fbf992015-01-23 11:02:27 -0800617 defer shutdown()
618
Cosmos Nicolaou9e909842015-03-17 11:58:59 -0700619 sh, err := modules.NewShell(ctx, nil, testing.Verbose(), t)
Cosmos Nicolaou344cc4a2014-11-26 15:38:43 -0800620 if err != nil {
621 t.Fatalf("unexpected error: %s", err)
622 }
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -0700623 defer sh.Cleanup(nil, nil)
624 sh.SetVar("a", "1")
625 os.Setenv("a", "wrong, should be 1")
Cosmos Nicolaou2cb05fb2014-10-23 13:37:32 -0700626 sh.SetVar("b", "2 also wrong")
627 os.Setenv("b", "wrong, should be 2")
Cosmos Nicolaou612ad382014-10-29 19:41:35 -0700628 h, err := sh.Start("printenv", []string{"b=2"})
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -0700629 if err != nil {
630 t.Fatalf("unexpected error: %s", err)
631 }
632 scanner := bufio.NewScanner(h.Stdout())
633 for scanner.Scan() {
634 o := scanner.Text()
635 if strings.HasPrefix(o, "a=") {
636 if got, want := o, "a=1"; got != want {
Cosmos Nicolaou2cb05fb2014-10-23 13:37:32 -0700637 t.Errorf("got: %q, want %q", got, want)
638 }
639 }
640 if strings.HasPrefix(o, "b=") {
641 if got, want := o, "b=2"; got != want {
642 t.Errorf("got: %q, want %q", got, want)
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -0700643 }
644 }
645 }
646}
647
Cosmos Nicolaoud4f00562014-11-17 20:35:48 -0800648func TestSetEntryPoint(t *testing.T) {
649 env := map[string]string{"a": "a", "b": "b"}
650 nenv := modules.SetEntryPoint(env, "eg1")
651 if got, want := len(nenv), 3; got != want {
652 t.Errorf("got %d, want %d", got, want)
653 }
654 nenv = modules.SetEntryPoint(env, "eg2")
655 if got, want := len(nenv), 3; got != want {
656 t.Errorf("got %d, want %d", got, want)
657 }
Cosmos Nicolaou5339a7b2014-11-18 17:28:00 -0800658 sort.Strings(nenv)
Asim Shankar59b8b692015-03-30 01:23:36 -0700659 if got, want := nenv, []string{"V23_SHELL_HELPER_PROCESS_ENTRY_POINT=eg2", "a=a", "b=b"}; !reflect.DeepEqual(got, want) {
Cosmos Nicolaoud4f00562014-11-17 20:35:48 -0800660 t.Errorf("got %d, want %d", got, want)
661 }
662}
663
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700664func TestNoExec(t *testing.T) {
Cosmos Nicolaou1381f8a2015-03-13 09:40:34 -0700665 ctx, shutdown := test.InitForTest()
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700666 defer shutdown()
667
Cosmos Nicolaou9e909842015-03-17 11:58:59 -0700668 sh, err := modules.NewShell(ctx, nil, testing.Verbose(), t)
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700669 if err != nil {
670 t.Fatalf("unexpected error: %s", err)
671 }
672 defer sh.Cleanup(nil, nil)
673 h, err := sh.StartWithOpts(sh.DefaultStartOpts().NoExecCommand(), nil, "/bin/echo", "hello", "world")
674 if err != nil {
675 t.Fatalf("unexpected error: %s", err)
676 }
677 scanner := bufio.NewScanner(h.Stdout())
678 scanner.Scan()
679 if got, want := scanner.Text(), "hello world"; got != want {
680 t.Fatalf("got %v, want %v", got, want)
681 }
682}
683
684func TestExternal(t *testing.T) {
Cosmos Nicolaou1381f8a2015-03-13 09:40:34 -0700685 ctx, shutdown := test.InitForTest()
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700686 defer shutdown()
687
Cosmos Nicolaou9e909842015-03-17 11:58:59 -0700688 sh, err := modules.NewShell(ctx, nil, testing.Verbose(), t)
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700689 if err != nil {
690 t.Fatalf("unexpected error: %s", err)
691 }
692 defer sh.Cleanup(nil, nil)
693 cookie := strconv.Itoa(rand.Int())
694 sh.SetConfigKey("cookie", cookie)
695 h, err := sh.StartWithOpts(sh.DefaultStartOpts().ExternalCommand(), nil, os.Args[0], "--test.run=TestExternalTestHelper")
696 if err != nil {
697 t.Fatalf("unexpected error: %s", err)
698 }
699 scanner := bufio.NewScanner(h.Stdout())
700 scanner.Scan()
701 if got, want := scanner.Text(), fmt.Sprintf("cookie: %s", cookie); got != want {
702 h.Shutdown(os.Stderr, os.Stderr)
703 t.Fatalf("got %v, want %v", got, want)
704 }
705}
706
707// TestExternalTestHelper is used by TestExternal above and has not utility
708// as a test in it's own right.
709func TestExternalTestHelper(t *testing.T) {
710 child, err := exec.GetChildHandle()
711 if err != nil {
712 return
713 }
714 child.SetReady()
715 val, err := child.Config.Get("cookie")
716 if err != nil {
717 t.Fatalf("failed to get child handle: %s", err)
718 }
719 fmt.Printf("cookie: %s\n", val)
720}
721
722func TestPipe(t *testing.T) {
Cosmos Nicolaou1381f8a2015-03-13 09:40:34 -0700723 ctx, shutdown := test.InitForTest()
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700724 defer shutdown()
725
Cosmos Nicolaou9e909842015-03-17 11:58:59 -0700726 sh, err := modules.NewShell(ctx, nil, testing.Verbose(), t)
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700727 if err != nil {
728 t.Fatalf("unexpected error: %s", err)
729 }
730 defer sh.Cleanup(nil, nil)
731
732 for _, cmd := range []string{"pipeProc", "pipeFunc"} {
733 r, w, err := os.Pipe()
734 if err != nil {
735 t.Fatal(err)
736 }
737 opts := sh.DefaultStartOpts()
738 opts.Stdin = r
739 h, err := sh.StartWithOpts(opts, nil, cmd)
740 if err != nil {
741 t.Fatal(err)
742 }
743 cookie := strconv.Itoa(rand.Int())
744 go func(w *os.File, s string) {
745 fmt.Fprintf(w, "hello world\n")
746 fmt.Fprintf(w, "%s\n", s)
747 w.Close()
748 }(w, cookie)
749
750 scanner := bufio.NewScanner(h.Stdout())
751 want := []string{
752 fmt.Sprintf("%p: hello world", pipeEcho),
753 fmt.Sprintf("%p: %s", pipeEcho, cookie),
754 }
755 i := 0
756 for scanner.Scan() {
757 if got, want := scanner.Text(), want[i]; got != want {
758 t.Fatalf("%s: got %v, want %v", cmd, got, want)
759 }
760 i++
761 }
762 if got, want := i, 2; got != want {
763 t.Fatalf("%s: got %v, want %v", cmd, got, want)
764 }
765 if err := h.Shutdown(os.Stderr, os.Stderr); err != nil {
766 t.Fatal(err)
767 }
768 r.Close()
769 }
770}
771
772func TestLIFO(t *testing.T) {
Cosmos Nicolaou1381f8a2015-03-13 09:40:34 -0700773 ctx, shutdown := test.InitForTest()
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700774 defer shutdown()
Cosmos Nicolaou9e909842015-03-17 11:58:59 -0700775 sh, err := modules.NewShell(ctx, nil, testing.Verbose(), t)
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700776 if err != nil {
777 t.Fatalf("unexpected error: %s", err)
778 }
779 defer sh.Cleanup(nil, nil)
780
781 cases := []string{"a", "b", "c"}
782 for _, msg := range cases {
783 h, err := sh.Start("lifo", nil)
784 if err != nil {
785 t.Fatal(err)
786 }
787 fmt.Fprintf(h.Stdin(), "%s\n", msg)
788 }
789 var buf bytes.Buffer
790 if err := sh.Cleanup(&buf, nil); err != nil {
791 t.Fatal(err)
792 }
793 lines := strings.Split(strings.TrimRight(buf.String(), "\n"), "\n")
794 if got, want := len(lines), len(cases); got != want {
795 t.Fatalf("got %v, want %v", got, want)
796 }
797 sort.Sort(sort.Reverse(sort.StringSlice(cases)))
798 for i, msg := range cases {
799 if got, want := lines[i], fmt.Sprintf("%p: %s", lifo, msg); got != want {
800 t.Fatalf("got %v, want %v", got, want)
801 }
802 }
803}
804
805func TestStartOpts(t *testing.T) {
Cosmos Nicolaou9e909842015-03-17 11:58:59 -0700806 sh, err := modules.NewShell(nil, nil, testing.Verbose(), t)
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700807 if err != nil {
808 t.Fatalf("unexpected error: %s", err)
809 }
810 opts := modules.StartOpts{
811 External: true,
812 }
813 sh.SetDefaultStartOpts(opts)
814 def := sh.DefaultStartOpts()
815 if got, want := def.External, opts.External; got != want {
816 t.Fatalf("got %v, want %v", got, want)
817 }
818 def.External = false
819 if got, want := def, (modules.StartOpts{}); !reflect.DeepEqual(got, want) {
820 t.Fatalf("got %v, want %v", got, want)
821 }
822
823 // Verify that the shell retains a copy.
824 opts.External = false
825 opts.ExecProtocol = true
826 def = sh.DefaultStartOpts()
827 if got, want := def.External, true; got != want {
828 t.Fatalf("got %v, want %v", got, want)
829 }
830 if got, want := def.ExecProtocol, false; got != want {
831 t.Fatalf("got %v, want %v", got, want)
832 }
833
834 sh.SetDefaultStartOpts(opts)
835 def = sh.DefaultStartOpts()
836 if got, want := def.ExecProtocol, true; got != want {
837 t.Fatalf("got %v, want %v", got, want)
838 }
839}
840
841func TestEmbeddedSession(t *testing.T) {
Cosmos Nicolaou9e909842015-03-17 11:58:59 -0700842 sh, err := modules.NewShell(nil, nil, testing.Verbose(), t)
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700843 if err != nil {
844 t.Fatalf("unexpected error: %s", err)
845 }
846 def := sh.DefaultStartOpts()
847 if def.ExpectTesting == nil {
848 t.Fatalf("ExpectTesting should be non nil")
849 }
850}
Cosmos Nicolaou52e9a222015-03-16 21:57:16 -0700851
852func TestCredentialsAndNoExec(t *testing.T) {
853 ctx, shutdown := test.InitForTest()
854 defer shutdown()
Cosmos Nicolaou9e909842015-03-17 11:58:59 -0700855 sh, err := modules.NewShell(ctx, nil, testing.Verbose(), t)
Cosmos Nicolaou52e9a222015-03-16 21:57:16 -0700856 if err != nil {
857 t.Fatalf("unexpected error: %s", err)
858 }
859 opts := sh.DefaultStartOpts()
860 opts = opts.NoExecCommand()
861 creds, err := sh.NewCustomCredentials()
862 if err != nil {
863 t.Fatalf("unexpected error: %s", err)
864 }
865 opts = opts.WithCustomCredentials(creds)
866 h, err := sh.StartWithOpts(opts, nil, "echos", "a")
867
868 if got, want := err, modules.ErrNoExecAndCustomCreds; got != want {
869 t.Fatalf("got %v, want %v", got, want)
870 }
871 if got, want := h, modules.Handle(nil); got != want {
872 t.Fatalf("got %v, want %v", got, want)
873 }
874}