Cosmos Nicolaou | 6261384 | 2014-08-25 21:57:37 -0700 | [diff] [blame] | 1 | package modules_test |
| 2 | |
| 3 | import ( |
| 4 | "bufio" |
Cosmos Nicolaou | bbae388 | 2014-10-02 22:58:19 -0700 | [diff] [blame] | 5 | "bytes" |
Cosmos Nicolaou | 6261384 | 2014-08-25 21:57:37 -0700 | [diff] [blame] | 6 | "fmt" |
| 7 | "io" |
Cosmos Nicolaou | 1e78ccc | 2014-10-09 08:10:26 -0700 | [diff] [blame] | 8 | "os" |
Cosmos Nicolaou | e664f3f | 2014-10-20 17:40:05 -0700 | [diff] [blame] | 9 | "strings" |
Cosmos Nicolaou | 6261384 | 2014-08-25 21:57:37 -0700 | [diff] [blame] | 10 | "testing" |
| 11 | "time" |
| 12 | |
Cosmos Nicolaou | e664f3f | 2014-10-20 17:40:05 -0700 | [diff] [blame] | 13 | "veyron.io/veyron/veyron/lib/exec" |
Jiri Simsa | 519c507 | 2014-09-17 21:37:57 -0700 | [diff] [blame] | 14 | "veyron.io/veyron/veyron/lib/modules" |
Cosmos Nicolaou | 6261384 | 2014-08-25 21:57:37 -0700 | [diff] [blame] | 15 | ) |
| 16 | |
| 17 | func init() { |
Cosmos Nicolaou | e664f3f | 2014-10-20 17:40:05 -0700 | [diff] [blame] | 18 | modules.RegisterChild("envtest", "envtest: <variables to print>...", PrintFromEnv) |
| 19 | modules.RegisterChild("printenv", "printenv", PrintEnv) |
Cosmos Nicolaou | 1e78ccc | 2014-10-09 08:10:26 -0700 | [diff] [blame] | 20 | modules.RegisterChild("echo", "[args]*", Echo) |
| 21 | modules.RegisterChild("errortest", "", ErrorMain) |
Cosmos Nicolaou | 6261384 | 2014-08-25 21:57:37 -0700 | [diff] [blame] | 22 | } |
| 23 | |
Cosmos Nicolaou | bbae388 | 2014-10-02 22:58:19 -0700 | [diff] [blame] | 24 | func Echo(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error { |
| 25 | for _, a := range args { |
| 26 | fmt.Fprintf(stdout, "stdout: %s\n", a) |
| 27 | fmt.Fprintf(stderr, "stderr: %s\n", a) |
| 28 | } |
| 29 | return nil |
| 30 | } |
| 31 | |
Cosmos Nicolaou | e664f3f | 2014-10-20 17:40:05 -0700 | [diff] [blame] | 32 | func PrintFromEnv(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error { |
| 33 | for _, a := range args[1:] { |
Cosmos Nicolaou | bbae388 | 2014-10-02 22:58:19 -0700 | [diff] [blame] | 34 | if v := env[a]; len(v) > 0 { |
| 35 | fmt.Fprintf(stdout, a+"="+v+"\n") |
| 36 | } else { |
| 37 | fmt.Fprintf(stderr, "missing %s\n", a) |
| 38 | } |
| 39 | } |
| 40 | modules.WaitForEOF(stdin) |
| 41 | fmt.Fprintf(stdout, "done\n") |
| 42 | return nil |
| 43 | } |
| 44 | |
Cosmos Nicolaou | e664f3f | 2014-10-20 17:40:05 -0700 | [diff] [blame] | 45 | const printEnvArgPrefix = "PRINTENV_ARG=" |
| 46 | |
Cosmos Nicolaou | 6261384 | 2014-08-25 21:57:37 -0700 | [diff] [blame] | 47 | func PrintEnv(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error { |
| 48 | for _, a := range args { |
Cosmos Nicolaou | e664f3f | 2014-10-20 17:40:05 -0700 | [diff] [blame] | 49 | fmt.Fprintf(stdout, "%s%s\n", printEnvArgPrefix, a) |
Cosmos Nicolaou | 6261384 | 2014-08-25 21:57:37 -0700 | [diff] [blame] | 50 | } |
Cosmos Nicolaou | e664f3f | 2014-10-20 17:40:05 -0700 | [diff] [blame] | 51 | for k, v := range env { |
Robin Thellend | 2eeae41 | 2014-10-21 14:30:27 -0700 | [diff] [blame] | 52 | fmt.Fprintln(stdout, k+"="+v) |
Cosmos Nicolaou | e664f3f | 2014-10-20 17:40:05 -0700 | [diff] [blame] | 53 | } |
Cosmos Nicolaou | 6261384 | 2014-08-25 21:57:37 -0700 | [diff] [blame] | 54 | return nil |
| 55 | } |
| 56 | |
Cosmos Nicolaou | 66afced | 2014-09-15 22:12:43 -0700 | [diff] [blame] | 57 | func ErrorMain(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error { |
| 58 | return fmt.Errorf("an error") |
| 59 | } |
| 60 | |
Cosmos Nicolaou | 6261384 | 2014-08-25 21:57:37 -0700 | [diff] [blame] | 61 | func waitForInput(scanner *bufio.Scanner) bool { |
| 62 | ch := make(chan struct{}) |
| 63 | go func(ch chan<- struct{}) { |
| 64 | scanner.Scan() |
| 65 | ch <- struct{}{} |
| 66 | }(ch) |
| 67 | select { |
| 68 | case <-ch: |
| 69 | return true |
| 70 | case <-time.After(10 * time.Second): |
| 71 | return false |
| 72 | } |
| 73 | } |
| 74 | |
| 75 | func testCommand(t *testing.T, sh *modules.Shell, name, key, val string) { |
| 76 | h, err := sh.Start(name, key) |
| 77 | if err != nil { |
| 78 | t.Fatalf("unexpected error: %s", err) |
| 79 | } |
| 80 | defer func() { |
Cosmos Nicolaou | bbae388 | 2014-10-02 22:58:19 -0700 | [diff] [blame] | 81 | var stdout, stderr bytes.Buffer |
| 82 | sh.Cleanup(&stdout, &stderr) |
| 83 | if len(stdout.String()) != 0 { |
| 84 | t.Errorf("unexpected stdout: %q", stdout.String()) |
| 85 | } |
| 86 | if len(stderr.String()) != 0 { |
| 87 | t.Errorf("unexpected stderr: %q", stderr.String()) |
| 88 | } |
Cosmos Nicolaou | 6261384 | 2014-08-25 21:57:37 -0700 | [diff] [blame] | 89 | }() |
| 90 | scanner := bufio.NewScanner(h.Stdout()) |
| 91 | if !waitForInput(scanner) { |
| 92 | t.Errorf("timeout") |
| 93 | return |
| 94 | } |
| 95 | if got, want := scanner.Text(), key+"="+val; got != want { |
| 96 | t.Errorf("got %q, want %q", got, want) |
| 97 | } |
Cosmos Nicolaou | 66afced | 2014-09-15 22:12:43 -0700 | [diff] [blame] | 98 | h.CloseStdin() |
Cosmos Nicolaou | 6261384 | 2014-08-25 21:57:37 -0700 | [diff] [blame] | 99 | if !waitForInput(scanner) { |
| 100 | t.Errorf("timeout") |
| 101 | return |
| 102 | } |
| 103 | if got, want := scanner.Text(), "done"; got != want { |
| 104 | t.Errorf("got %q, want %q", got, want) |
| 105 | } |
Cosmos Nicolaou | bbae388 | 2014-10-02 22:58:19 -0700 | [diff] [blame] | 106 | if err := h.Shutdown(nil, nil); err != nil { |
Cosmos Nicolaou | 66afced | 2014-09-15 22:12:43 -0700 | [diff] [blame] | 107 | t.Fatalf("unexpected error: %s", err) |
| 108 | } |
Cosmos Nicolaou | 6261384 | 2014-08-25 21:57:37 -0700 | [diff] [blame] | 109 | } |
| 110 | |
| 111 | func TestChild(t *testing.T) { |
Cosmos Nicolaou | 1e78ccc | 2014-10-09 08:10:26 -0700 | [diff] [blame] | 112 | sh := modules.NewShell("envtest") |
Cosmos Nicolaou | bbae388 | 2014-10-02 22:58:19 -0700 | [diff] [blame] | 113 | defer sh.Cleanup(nil, nil) |
Cosmos Nicolaou | 6261384 | 2014-08-25 21:57:37 -0700 | [diff] [blame] | 114 | key, val := "simpleVar", "foo & bar" |
| 115 | sh.SetVar(key, val) |
Cosmos Nicolaou | 6261384 | 2014-08-25 21:57:37 -0700 | [diff] [blame] | 116 | testCommand(t, sh, "envtest", key, val) |
| 117 | } |
| 118 | |
Cosmos Nicolaou | 1e78ccc | 2014-10-09 08:10:26 -0700 | [diff] [blame] | 119 | func TestChildNoRegistration(t *testing.T) { |
| 120 | sh := modules.NewShell() |
| 121 | defer sh.Cleanup(os.Stderr, os.Stderr) |
| 122 | key, val := "simpleVar", "foo & bar" |
| 123 | sh.SetVar(key, val) |
| 124 | testCommand(t, sh, "envtest", key, val) |
| 125 | _, err := sh.Start("non-existent-command", "random", "args") |
| 126 | if err == nil || err.Error() != `Shell command "non-existent-command" not registered` { |
| 127 | t.Fatalf("unexpected error %v", err) |
| 128 | } |
| 129 | } |
| 130 | |
Cosmos Nicolaou | 6261384 | 2014-08-25 21:57:37 -0700 | [diff] [blame] | 131 | func TestFunction(t *testing.T) { |
Cosmos Nicolaou | 1e78ccc | 2014-10-09 08:10:26 -0700 | [diff] [blame] | 132 | sh := modules.NewShell(".*") |
Cosmos Nicolaou | bbae388 | 2014-10-02 22:58:19 -0700 | [diff] [blame] | 133 | defer sh.Cleanup(nil, nil) |
Cosmos Nicolaou | 6261384 | 2014-08-25 21:57:37 -0700 | [diff] [blame] | 134 | key, val := "simpleVar", "foo & bar & baz" |
| 135 | sh.SetVar(key, val) |
Cosmos Nicolaou | e664f3f | 2014-10-20 17:40:05 -0700 | [diff] [blame] | 136 | sh.AddFunction("envtestf", PrintFromEnv, "envtest: <variables to print>...") |
Cosmos Nicolaou | 1e78ccc | 2014-10-09 08:10:26 -0700 | [diff] [blame] | 137 | testCommand(t, sh, "envtestf", key, val) |
Cosmos Nicolaou | 6261384 | 2014-08-25 21:57:37 -0700 | [diff] [blame] | 138 | } |
| 139 | |
Cosmos Nicolaou | 66afced | 2014-09-15 22:12:43 -0700 | [diff] [blame] | 140 | func TestErrorChild(t *testing.T) { |
Cosmos Nicolaou | 1e78ccc | 2014-10-09 08:10:26 -0700 | [diff] [blame] | 141 | sh := modules.NewShell("errortest") |
Cosmos Nicolaou | bbae388 | 2014-10-02 22:58:19 -0700 | [diff] [blame] | 142 | defer sh.Cleanup(nil, nil) |
Cosmos Nicolaou | 66afced | 2014-09-15 22:12:43 -0700 | [diff] [blame] | 143 | h, err := sh.Start("errortest") |
| 144 | if err != nil { |
| 145 | t.Fatalf("unexpected error: %s", err) |
| 146 | } |
Cosmos Nicolaou | bbae388 | 2014-10-02 22:58:19 -0700 | [diff] [blame] | 147 | if got, want := h.Shutdown(nil, nil), "exit status 1"; got == nil || got.Error() != want { |
Cosmos Nicolaou | 66afced | 2014-09-15 22:12:43 -0700 | [diff] [blame] | 148 | t.Errorf("got %q, want %q", got, want) |
| 149 | } |
| 150 | } |
| 151 | |
Cosmos Nicolaou | bbae388 | 2014-10-02 22:58:19 -0700 | [diff] [blame] | 152 | func testShutdown(t *testing.T, sh *modules.Shell) { |
| 153 | result := "" |
| 154 | args := []string{"a", "b c", "ddd"} |
| 155 | if _, err := sh.Start("echo", args...); err != nil { |
| 156 | t.Fatalf("unexpected error: %s", err) |
| 157 | } |
| 158 | var stdoutBuf bytes.Buffer |
| 159 | var stderrBuf bytes.Buffer |
| 160 | sh.Cleanup(&stdoutBuf, &stderrBuf) |
| 161 | stdoutOutput, stderrOutput := "stdout: echo\n", "stderr: echo\n" |
| 162 | for _, a := range args { |
| 163 | stdoutOutput += fmt.Sprintf("stdout: %s\n", a) |
| 164 | stderrOutput += fmt.Sprintf("stderr: %s\n", a) |
| 165 | } |
| 166 | if got, want := stdoutBuf.String(), stdoutOutput+result; got != want { |
| 167 | t.Errorf("got %q want %q", got, want) |
| 168 | } |
| 169 | if got, want := stderrBuf.String(), stderrOutput; got != want { |
| 170 | t.Errorf("got %q want %q", got, want) |
| 171 | } |
Cosmos Nicolaou | bbae388 | 2014-10-02 22:58:19 -0700 | [diff] [blame] | 172 | } |
| 173 | |
| 174 | func TestShutdownSubprocess(t *testing.T) { |
Cosmos Nicolaou | 1e78ccc | 2014-10-09 08:10:26 -0700 | [diff] [blame] | 175 | sh := modules.NewShell("echo") |
Cosmos Nicolaou | bbae388 | 2014-10-02 22:58:19 -0700 | [diff] [blame] | 176 | testShutdown(t, sh) |
| 177 | } |
| 178 | |
| 179 | func TestShutdownFunction(t *testing.T) { |
| 180 | sh := modules.NewShell() |
| 181 | sh.AddFunction("echo", Echo, "[args]*") |
| 182 | testShutdown(t, sh) |
| 183 | } |
| 184 | |
Cosmos Nicolaou | 66afced | 2014-09-15 22:12:43 -0700 | [diff] [blame] | 185 | func TestErrorFunc(t *testing.T) { |
| 186 | sh := modules.NewShell() |
Cosmos Nicolaou | bbae388 | 2014-10-02 22:58:19 -0700 | [diff] [blame] | 187 | defer sh.Cleanup(nil, nil) |
Cosmos Nicolaou | 66afced | 2014-09-15 22:12:43 -0700 | [diff] [blame] | 188 | sh.AddFunction("errortest", ErrorMain, "") |
| 189 | h, err := sh.Start("errortest") |
| 190 | if err != nil { |
| 191 | t.Fatalf("unexpected error: %s", err) |
| 192 | } |
Cosmos Nicolaou | bbae388 | 2014-10-02 22:58:19 -0700 | [diff] [blame] | 193 | if got, want := h.Shutdown(nil, nil), "an error"; got != nil && got.Error() != want { |
Cosmos Nicolaou | 66afced | 2014-09-15 22:12:43 -0700 | [diff] [blame] | 194 | t.Errorf("got %q, want %q", got, want) |
| 195 | } |
| 196 | } |
| 197 | |
Cosmos Nicolaou | e664f3f | 2014-10-20 17:40:05 -0700 | [diff] [blame] | 198 | func find(want string, in []string) bool { |
| 199 | for _, a := range in { |
| 200 | if a == want { |
| 201 | return true |
| 202 | } |
| 203 | } |
| 204 | return false |
| 205 | } |
| 206 | |
| 207 | func TestEnvelope(t *testing.T) { |
| 208 | sh := modules.NewShell("printenv") |
| 209 | defer sh.Cleanup(nil, nil) |
| 210 | sh.SetVar("a", "1") |
| 211 | sh.SetVar("b", "2") |
| 212 | args := []string{"oh", "ah"} |
| 213 | h, err := sh.Start("printenv", args...) |
| 214 | if err != nil { |
| 215 | t.Fatalf("unexpected error: %s", err) |
| 216 | } |
| 217 | scanner := bufio.NewScanner(h.Stdout()) |
| 218 | childArgs, childEnv := []string{}, []string{} |
| 219 | for scanner.Scan() { |
| 220 | o := scanner.Text() |
| 221 | if strings.HasPrefix(o, printEnvArgPrefix) { |
| 222 | childArgs = append(childArgs, strings.TrimPrefix(o, printEnvArgPrefix)) |
| 223 | } else { |
| 224 | childEnv = append(childEnv, o) |
| 225 | } |
| 226 | } |
| 227 | shArgs, shEnv := sh.CommandEnvelope("printenv", args...) |
| 228 | for _, want := range args { |
| 229 | if !find(want, childArgs) { |
| 230 | t.Errorf("failed to find %q in %s", want, childArgs) |
| 231 | } |
| 232 | if !find(want, shArgs) { |
| 233 | t.Errorf("failed to find %q in %s", want, shArgs) |
| 234 | } |
| 235 | } |
| 236 | |
| 237 | for _, want := range shEnv { |
| 238 | if !find(want, childEnv) { |
| 239 | t.Errorf("failed to find %q in %s", want, shEnv) |
| 240 | } |
| 241 | } |
| 242 | for _, want := range childEnv { |
| 243 | if want == exec.VersionVariable+"=" { |
| 244 | continue |
| 245 | } |
| 246 | if !find(want, shEnv) { |
| 247 | t.Errorf("failed to find %q in %s", want, childEnv) |
| 248 | } |
| 249 | } |
| 250 | } |
| 251 | |
| 252 | func TestEnvMerge(t *testing.T) { |
| 253 | sh := modules.NewShell("printenv") |
| 254 | defer sh.Cleanup(nil, nil) |
| 255 | sh.SetVar("a", "1") |
| 256 | os.Setenv("a", "wrong, should be 1") |
| 257 | h, err := sh.Start("printenv") |
| 258 | if err != nil { |
| 259 | t.Fatalf("unexpected error: %s", err) |
| 260 | } |
| 261 | scanner := bufio.NewScanner(h.Stdout()) |
| 262 | for scanner.Scan() { |
| 263 | o := scanner.Text() |
| 264 | if strings.HasPrefix(o, "a=") { |
| 265 | if got, want := o, "a=1"; got != want { |
| 266 | t.Errorf("got: %s, want %s", got, want) |
| 267 | } |
| 268 | } |
| 269 | } |
| 270 | } |
| 271 | |
Cosmos Nicolaou | 6261384 | 2014-08-25 21:57:37 -0700 | [diff] [blame] | 272 | func TestHelperProcess(t *testing.T) { |
Cosmos Nicolaou | bbae388 | 2014-10-02 22:58:19 -0700 | [diff] [blame] | 273 | modules.DispatchInTest() |
Cosmos Nicolaou | 6261384 | 2014-08-25 21:57:37 -0700 | [diff] [blame] | 274 | } |
| 275 | |
| 276 | // TODO(cnicolaou): more complete tests for environment variables, |
| 277 | // OS being overridden by Shell for example. |
Cosmos Nicolaou | e664f3f | 2014-10-20 17:40:05 -0700 | [diff] [blame] | 278 | // |
| 279 | // TODO(cnicolaou): test for one or either of the io.Writers being nil |
| 280 | // on calls to Shutdown |
| 281 | // |
| 282 | // TODO(cnicolaou): test for error return from cleanup |