blob: aa74c2d0cb9a90d5a51331c395e5a9b3e4b7665f [file] [log] [blame]
Cosmos Nicolaoub07fa772014-05-16 08:07:02 -07001package rt_test
Jiri Simsa5293dcb2014-05-10 09:56:38 -07002
3import (
4 "fmt"
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -07005 "io"
Jiri Simsa5293dcb2014-05-10 09:56:38 -07006 "os"
Bogdan Caprita0a9f9982014-06-11 15:50:08 -07007 "reflect"
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -07008 "strings"
Jiri Simsa5293dcb2014-05-10 09:56:38 -07009 "testing"
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -070010 "time"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070011
Jiri Simsa519c5072014-09-17 21:37:57 -070012 "veyron.io/veyron/veyron2"
13 "veyron.io/veyron/veyron2/ipc"
14 "veyron.io/veyron/veyron2/mgmt"
15 "veyron.io/veyron/veyron2/naming"
Asim Shankarcc044212014-10-15 23:25:26 -070016 "veyron.io/veyron/veyron2/options"
Jiri Simsa519c5072014-09-17 21:37:57 -070017 "veyron.io/veyron/veyron2/services/mgmt/appcycle"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070018
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -070019 "veyron.io/veyron/veyron/lib/expect"
20 "veyron.io/veyron/veyron/lib/modules"
Asim Shankarc920db32014-10-16 19:18:21 -070021 "veyron.io/veyron/veyron/lib/testutil"
Jiri Simsa519c5072014-09-17 21:37:57 -070022 "veyron.io/veyron/veyron/lib/testutil/security"
Cosmos Nicolaoud6c3c9c2014-09-30 15:42:53 -070023 "veyron.io/veyron/veyron/profiles"
Jiri Simsa519c5072014-09-17 21:37:57 -070024 "veyron.io/veyron/veyron/runtimes/google/rt"
25 vflag "veyron.io/veyron/veyron/security/flag"
26 "veyron.io/veyron/veyron/services/mgmt/node"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070027)
28
Cosmos Nicolaouf889c732014-10-16 20:46:54 -070029var profileOpt = options.Profile{profiles.New()}
30
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -070031const (
32 noWaitersCmd = "noWaiters"
33 forceStopCmd = "forceStop"
34 appCmd = "app"
35)
36
37func init() {
Asim Shankarc920db32014-10-16 19:18:21 -070038 testutil.Init()
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -070039 modules.RegisterChild(noWaitersCmd, "", noWaiters)
40 modules.RegisterChild(forceStopCmd, "", forceStop)
41 modules.RegisterChild(appCmd, "", app)
42}
43
Jiri Simsa5293dcb2014-05-10 09:56:38 -070044// TestBasic verifies that the basic plumbing works: LocalStop calls result in
45// stop messages being sent on the channel passed to WaitForStop.
46func TestBasic(t *testing.T) {
Cosmos Nicolaouf889c732014-10-16 20:46:54 -070047 m, _ := rt.New(profileOpt)
Jiri Simsa5293dcb2014-05-10 09:56:38 -070048 ch := make(chan string, 1)
49 m.WaitForStop(ch)
50 for i := 0; i < 10; i++ {
51 m.Stop()
Cosmos Nicolaoub07fa772014-05-16 08:07:02 -070052 if want, got := veyron2.LocalStop, <-ch; want != got {
Jiri Simsa5293dcb2014-05-10 09:56:38 -070053 t.Errorf("WaitForStop want %q got %q", want, got)
54 }
55 select {
56 case s := <-ch:
57 t.Errorf("channel expected to be empty, got %q instead", s)
58 default:
59 }
60 }
61}
62
63// TestMultipleWaiters verifies that the plumbing works with more than one
64// registered wait channel.
65func TestMultipleWaiters(t *testing.T) {
Cosmos Nicolaouf889c732014-10-16 20:46:54 -070066 m, _ := rt.New(profileOpt)
Jiri Simsa5293dcb2014-05-10 09:56:38 -070067 ch1 := make(chan string, 1)
68 m.WaitForStop(ch1)
69 ch2 := make(chan string, 1)
70 m.WaitForStop(ch2)
71 for i := 0; i < 10; i++ {
72 m.Stop()
Cosmos Nicolaoub07fa772014-05-16 08:07:02 -070073 if want, got := veyron2.LocalStop, <-ch1; want != got {
Jiri Simsa5293dcb2014-05-10 09:56:38 -070074 t.Errorf("WaitForStop want %q got %q", want, got)
75 }
Cosmos Nicolaoub07fa772014-05-16 08:07:02 -070076 if want, got := veyron2.LocalStop, <-ch2; want != got {
Jiri Simsa5293dcb2014-05-10 09:56:38 -070077 t.Errorf("WaitForStop want %q got %q", want, got)
78 }
79 }
80}
81
82// TestMultipleStops verifies that LocalStop does not block even if the wait
83// channel is not being drained: once the channel's buffer fills up, future
84// Stops become no-ops.
85func TestMultipleStops(t *testing.T) {
Cosmos Nicolaouf889c732014-10-16 20:46:54 -070086 m, _ := rt.New(profileOpt)
Jiri Simsa5293dcb2014-05-10 09:56:38 -070087 ch := make(chan string, 1)
88 m.WaitForStop(ch)
89 for i := 0; i < 10; i++ {
90 m.Stop()
91 }
Cosmos Nicolaoub07fa772014-05-16 08:07:02 -070092 if want, got := veyron2.LocalStop, <-ch; want != got {
Jiri Simsa5293dcb2014-05-10 09:56:38 -070093 t.Errorf("WaitForStop want %q got %q", want, got)
94 }
95 select {
96 case s := <-ch:
97 t.Errorf("channel expected to be empty, got %q instead", s)
98 default:
99 }
100}
101
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700102func noWaiters(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
Cosmos Nicolaouf889c732014-10-16 20:46:54 -0700103 m, _ := rt.New(profileOpt)
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700104 fmt.Fprintf(stdout, "ready\n")
105 modules.WaitForEOF(stdin)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700106 m.Stop()
107 os.Exit(42) // This should not be reached.
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700108 return nil
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700109}
110
Bogdan Caprita1002ba42014-06-06 19:24:40 -0700111// TestNoWaiters verifies that the child process exits in the absence of any
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700112// wait channel being registered with its runtime.
113func TestNoWaiters(t *testing.T) {
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700114 sh := modules.NewShell(noWaitersCmd)
115 defer sh.Cleanup(os.Stderr, os.Stderr)
116 h, err := sh.Start(noWaitersCmd)
117 if err != nil {
118 t.Fatalf("unexpected error: %s", err)
119 }
120 expect.NewSession(t, h.Stdout(), time.Minute).Expect("ready")
121 want := fmt.Sprintf("exit status %d", veyron2.UnhandledStopExitCode)
122 if err = h.Shutdown(os.Stderr, os.Stderr); err == nil || err.Error() != want {
123 t.Errorf("got %v, want %s", err, want)
124 }
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700125}
126
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700127func forceStop(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
Cosmos Nicolaouf889c732014-10-16 20:46:54 -0700128 m, _ := rt.New(profileOpt)
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700129 fmt.Fprintf(stdout, "ready\n")
130 modules.WaitForEOF(stdin)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700131 m.WaitForStop(make(chan string, 1))
132 m.ForceStop()
133 os.Exit(42) // This should not be reached.
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700134 return nil
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700135}
136
137// TestForceStop verifies that ForceStop causes the child process to exit
138// immediately.
139func TestForceStop(t *testing.T) {
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700140 sh := modules.NewShell(forceStopCmd)
141 defer sh.Cleanup(os.Stderr, os.Stderr)
142 h, err := sh.Start(forceStopCmd)
143 if err != nil {
144 t.Fatalf("unexpected error: %s", err)
145 }
146 s := expect.NewSession(t, h.Stdout(), time.Minute)
147 s.Expect("ready")
148 err = h.Shutdown(os.Stderr, os.Stderr)
149 want := fmt.Sprintf("exit status %d", veyron2.UnhandledStopExitCode)
150 if err == nil || err.Error() != want {
151 t.Errorf("got %v, want %s", err, want)
152 }
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700153}
Bogdan Caprita0a9f9982014-06-11 15:50:08 -0700154
155func checkProgress(t *testing.T, ch <-chan veyron2.Task, progress, goal int) {
156 if want, got := (veyron2.Task{progress, goal}), <-ch; !reflect.DeepEqual(want, got) {
157 t.Errorf("Unexpected progress: want %+v, got %+v", want, got)
158 }
159}
160
161func checkNoProgress(t *testing.T, ch <-chan veyron2.Task) {
162 select {
163 case p := <-ch:
164 t.Errorf("channel expected to be empty, got %+v instead", p)
165 default:
166 }
167}
168
169// TestProgress verifies that the ticker update/track logic works for a single
170// tracker.
171func TestProgress(t *testing.T) {
Cosmos Nicolaouf889c732014-10-16 20:46:54 -0700172 m, _ := rt.New(profileOpt)
Bogdan Caprita0a9f9982014-06-11 15:50:08 -0700173 m.AdvanceGoal(50)
174 ch := make(chan veyron2.Task, 1)
175 m.TrackTask(ch)
176 checkNoProgress(t, ch)
177 m.AdvanceProgress(10)
178 checkProgress(t, ch, 10, 50)
179 checkNoProgress(t, ch)
180 m.AdvanceProgress(5)
181 checkProgress(t, ch, 15, 50)
182 m.AdvanceGoal(50)
183 checkProgress(t, ch, 15, 100)
184 m.AdvanceProgress(1)
185 checkProgress(t, ch, 16, 100)
186 m.AdvanceGoal(10)
187 checkProgress(t, ch, 16, 110)
188 m.AdvanceProgress(-13)
189 checkNoProgress(t, ch)
190 m.AdvanceGoal(0)
191 checkNoProgress(t, ch)
Bogdan Caprita4258d882014-07-02 09:15:22 -0700192 m.Cleanup()
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700193 if _, ok := <-ch; ok {
194 t.Errorf("Expected channel to be closed")
195 }
Bogdan Caprita0a9f9982014-06-11 15:50:08 -0700196}
197
198// TestProgressMultipleTrackers verifies that the ticker update/track logic
199// works for more than one tracker. It also ensures that the runtime doesn't
200// block when the tracker channels are full.
201func TestProgressMultipleTrackers(t *testing.T) {
Cosmos Nicolaouf889c732014-10-16 20:46:54 -0700202 m, _ := rt.New(profileOpt)
Bogdan Caprita0a9f9982014-06-11 15:50:08 -0700203 // ch1 is 1-buffered, ch2 is 2-buffered.
204 ch1, ch2 := make(chan veyron2.Task, 1), make(chan veyron2.Task, 2)
205 m.TrackTask(ch1)
206 m.TrackTask(ch2)
207 checkNoProgress(t, ch1)
208 checkNoProgress(t, ch2)
209 m.AdvanceProgress(1)
210 checkProgress(t, ch1, 1, 0)
211 checkNoProgress(t, ch1)
212 checkProgress(t, ch2, 1, 0)
213 checkNoProgress(t, ch2)
214 for i := 0; i < 10; i++ {
215 m.AdvanceProgress(1)
216 }
217 checkProgress(t, ch1, 2, 0)
218 checkNoProgress(t, ch1)
219 checkProgress(t, ch2, 2, 0)
220 checkProgress(t, ch2, 3, 0)
221 checkNoProgress(t, ch2)
222 m.AdvanceGoal(4)
223 checkProgress(t, ch1, 11, 4)
224 checkProgress(t, ch2, 11, 4)
Bogdan Caprita4258d882014-07-02 09:15:22 -0700225 m.Cleanup()
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700226 if _, ok := <-ch1; ok {
227 t.Errorf("Expected channel to be closed")
228 }
229 if _, ok := <-ch2; ok {
230 t.Errorf("Expected channel to be closed")
231 }
232}
233
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700234func app(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
Cosmos Nicolaouf889c732014-10-16 20:46:54 -0700235 r, err := rt.New(profileOpt)
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700236 if err != nil {
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700237 return err
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700238 }
Bogdan Caprita4258d882014-07-02 09:15:22 -0700239 defer r.Cleanup()
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700240 ch := make(chan string, 1)
241 r.WaitForStop(ch)
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700242 fmt.Fprintf(stdout, "Got %s\n", <-ch)
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700243 r.AdvanceGoal(10)
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700244 fmt.Fprintf(stdout, "Doing some work\n")
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700245 r.AdvanceProgress(2)
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700246 fmt.Fprintf(stdout, "Doing some more work\n")
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700247 r.AdvanceProgress(5)
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700248 return nil
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700249}
250
251type configServer struct {
252 ch chan<- string
253}
254
255func (c *configServer) Set(_ ipc.ServerContext, key, value string) error {
256 if key != mgmt.AppCycleManagerConfigKey {
257 return fmt.Errorf("Unexpected key: %v", key)
258 }
259 c.ch <- value
260 return nil
261
262}
263
Cosmos Nicolaou39a00e02014-08-14 11:04:14 -0700264func createConfigServer(t *testing.T, r veyron2.Runtime) (ipc.Server, string, <-chan string) {
265 server, err := r.NewServer()
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700266 if err != nil {
267 t.Fatalf("Got error: %v", err)
268 }
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700269 ch := make(chan string)
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -0700270
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700271 var ep naming.Endpoint
Cosmos Nicolaoud6c3c9c2014-09-30 15:42:53 -0700272 if ep, err = server.ListenX(profiles.LocalListenSpec); err != nil {
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700273 t.Fatalf("Got error: %v", err)
274 }
Cosmos Nicolaou8bfacf22014-08-19 11:19:36 -0700275 if err := server.Serve("", ipc.LeafDispatcher(node.NewServerConfig(&configServer{ch}), vflag.NewAuthorizerOrDie())); err != nil {
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -0700276 t.Fatalf("Got error: %v", err)
277 }
278 return server, naming.JoinAddressName(ep.String(), ""), ch
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700279
280}
281
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700282func setupRemoteAppCycleMgr(t *testing.T) (veyron2.Runtime, modules.Handle, appcycle.AppCycle, func()) {
Cosmos Nicolaou39a00e02014-08-14 11:04:14 -0700283 // We need to use the public API since stubs are used below (and they
284 // refer to the global rt.R() function), but we take care to make sure
285 // that the "google" runtime we are trying to test in this package is
286 // the one being used.
Cosmos Nicolaouf889c732014-10-16 20:46:54 -0700287 r, _ := rt.New(profileOpt, options.GoogleRuntime, options.ForceNewSecurityModel{})
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700288
Cosmos Nicolaoubc22fa12014-10-14 20:51:55 -0700289 childcreds := security.NewVeyronCredentials(r.Principal(), appCmd)
Cosmos Nicolaou39a00e02014-08-14 11:04:14 -0700290 configServer, configServiceName, ch := createConfigServer(t, r)
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700291 sh := modules.NewShell(appCmd)
Cosmos Nicolaoubc22fa12014-10-14 20:51:55 -0700292 sh.SetVar("VEYRON_CREDENTIALS", childcreds)
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700293 sh.SetVar(mgmt.ParentNodeManagerConfigKey, configServiceName)
294 h, err := sh.Start("app")
295 if err != nil {
296 t.Fatalf("unexpected error: %s", err)
297 }
298
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700299 appCycleName := <-ch
300 appCycle, err := appcycle.BindAppCycle(appCycleName)
301 if err != nil {
302 t.Fatalf("Got error: %v", err)
303 }
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700304 return r, h, appCycle, func() {
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700305 configServer.Stop()
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700306 sh.Cleanup(os.Stderr, os.Stderr)
Cosmos Nicolaoubc22fa12014-10-14 20:51:55 -0700307 os.RemoveAll(childcreds)
Bogdan Caprita4258d882014-07-02 09:15:22 -0700308 // Don't do r.Cleanup() since the runtime needs to be used by
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700309 // more than one test case.
310 }
311}
312
313// TestRemoteForceStop verifies that the child process exits when sending it
314// a remote ForceStop rpc.
315func TestRemoteForceStop(t *testing.T) {
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700316 r, h, appCycle, cleanup := setupRemoteAppCycleMgr(t)
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700317 defer cleanup()
Cosmos Nicolaou39a00e02014-08-14 11:04:14 -0700318 if err := appCycle.ForceStop(r.NewContext()); err == nil || !strings.Contains(err.Error(), "EOF") {
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700319 t.Fatalf("Expected EOF error, got %v instead", err)
320 }
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700321 s := expect.NewSession(t, h.Stdout(), time.Minute)
322 s.ExpectEOF()
323 err := h.Shutdown(os.Stderr, os.Stderr)
324 want := fmt.Sprintf("exit status %d", veyron2.ForceStopExitCode)
325 if err == nil || err.Error() != want {
326 t.Errorf("got %v, want %s", err, want)
327 }
328
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700329}
330
331// TestRemoteStop verifies that the child shuts down cleanly when sending it
332// a remote Stop rpc.
333func TestRemoteStop(t *testing.T) {
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700334 r, h, appCycle, cleanup := setupRemoteAppCycleMgr(t)
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700335 defer cleanup()
Cosmos Nicolaou39a00e02014-08-14 11:04:14 -0700336 stream, err := appCycle.Stop(r.NewContext())
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700337 if err != nil {
338 t.Fatalf("Got error: %v", err)
339 }
Shyam Jayaraman97b9dca2014-07-31 13:30:46 -0700340 rStream := stream.RecvStream()
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700341 expectTask := func(progress, goal int32) {
Shyam Jayaraman97b9dca2014-07-31 13:30:46 -0700342 if !rStream.Advance() {
343 t.Fatalf("unexpected streaming error: %q", rStream.Err())
Shyam Jayaramanc4aed6e2014-07-22 14:25:06 -0700344 }
Shyam Jayaraman97b9dca2014-07-31 13:30:46 -0700345 task := rStream.Value()
Shyam Jayaramanc4aed6e2014-07-22 14:25:06 -0700346 if task.Progress != progress || task.Goal != goal {
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700347 t.Errorf("Got (%d, %d), want (%d, %d)", task.Progress, task.Goal, progress, goal)
348 }
349 }
350 expectTask(0, 10)
351 expectTask(2, 10)
352 expectTask(7, 10)
Shyam Jayaraman97b9dca2014-07-31 13:30:46 -0700353 if rStream.Advance() || rStream.Err() != nil {
354 t.Errorf("Expected EOF, got (%v, %v) instead", rStream.Value(), rStream.Err())
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700355 }
356 if err := stream.Finish(); err != nil {
357 t.Errorf("Got error %v", err)
358 }
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700359 s := expect.NewSession(t, h.Stdout(), time.Minute)
360 s.Expect(fmt.Sprintf("Got %s", veyron2.RemoteStop))
361 s.Expect("Doing some work")
362 s.Expect("Doing some more work")
363 s.ExpectEOF()
364 if err := h.Shutdown(os.Stderr, os.Stderr); err != nil {
365 t.Fatalf("unexpected error: %s", err)
366 }
Bogdan Caprita0a9f9982014-06-11 15:50:08 -0700367}