blob: fc399192fbffdcea1354e601945eb2f8ee96be27 [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"
Asim Shankar95910b62014-10-31 22:02:29 -070020 "veyron.io/veyron/veyron/lib/flags/consts"
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -070021 "veyron.io/veyron/veyron/lib/modules"
Asim Shankarc920db32014-10-16 19:18:21 -070022 "veyron.io/veyron/veyron/lib/testutil"
Jiri Simsa519c5072014-09-17 21:37:57 -070023 "veyron.io/veyron/veyron/lib/testutil/security"
Cosmos Nicolaoud6c3c9c2014-09-30 15:42:53 -070024 "veyron.io/veyron/veyron/profiles"
Jiri Simsa519c5072014-09-17 21:37:57 -070025 "veyron.io/veyron/veyron/runtimes/google/rt"
26 vflag "veyron.io/veyron/veyron/security/flag"
27 "veyron.io/veyron/veyron/services/mgmt/node"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070028)
29
Cosmos Nicolaouf889c732014-10-16 20:46:54 -070030var profileOpt = options.Profile{profiles.New()}
31
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -070032const (
33 noWaitersCmd = "noWaiters"
34 forceStopCmd = "forceStop"
35 appCmd = "app"
36)
37
38func init() {
Asim Shankarc920db32014-10-16 19:18:21 -070039 testutil.Init()
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -070040 modules.RegisterChild(noWaitersCmd, "", noWaiters)
41 modules.RegisterChild(forceStopCmd, "", forceStop)
42 modules.RegisterChild(appCmd, "", app)
43}
44
Jiri Simsa5293dcb2014-05-10 09:56:38 -070045// TestBasic verifies that the basic plumbing works: LocalStop calls result in
46// stop messages being sent on the channel passed to WaitForStop.
47func TestBasic(t *testing.T) {
Cosmos Nicolaouf889c732014-10-16 20:46:54 -070048 m, _ := rt.New(profileOpt)
Jiri Simsa5293dcb2014-05-10 09:56:38 -070049 ch := make(chan string, 1)
50 m.WaitForStop(ch)
51 for i := 0; i < 10; i++ {
52 m.Stop()
Cosmos Nicolaoub07fa772014-05-16 08:07:02 -070053 if want, got := veyron2.LocalStop, <-ch; want != got {
Jiri Simsa5293dcb2014-05-10 09:56:38 -070054 t.Errorf("WaitForStop want %q got %q", want, got)
55 }
56 select {
57 case s := <-ch:
58 t.Errorf("channel expected to be empty, got %q instead", s)
59 default:
60 }
61 }
62}
63
64// TestMultipleWaiters verifies that the plumbing works with more than one
65// registered wait channel.
66func TestMultipleWaiters(t *testing.T) {
Cosmos Nicolaouf889c732014-10-16 20:46:54 -070067 m, _ := rt.New(profileOpt)
Jiri Simsa5293dcb2014-05-10 09:56:38 -070068 ch1 := make(chan string, 1)
69 m.WaitForStop(ch1)
70 ch2 := make(chan string, 1)
71 m.WaitForStop(ch2)
72 for i := 0; i < 10; i++ {
73 m.Stop()
Cosmos Nicolaoub07fa772014-05-16 08:07:02 -070074 if want, got := veyron2.LocalStop, <-ch1; want != got {
Jiri Simsa5293dcb2014-05-10 09:56:38 -070075 t.Errorf("WaitForStop want %q got %q", want, got)
76 }
Cosmos Nicolaoub07fa772014-05-16 08:07:02 -070077 if want, got := veyron2.LocalStop, <-ch2; want != got {
Jiri Simsa5293dcb2014-05-10 09:56:38 -070078 t.Errorf("WaitForStop want %q got %q", want, got)
79 }
80 }
81}
82
83// TestMultipleStops verifies that LocalStop does not block even if the wait
84// channel is not being drained: once the channel's buffer fills up, future
85// Stops become no-ops.
86func TestMultipleStops(t *testing.T) {
Cosmos Nicolaouf889c732014-10-16 20:46:54 -070087 m, _ := rt.New(profileOpt)
Jiri Simsa5293dcb2014-05-10 09:56:38 -070088 ch := make(chan string, 1)
89 m.WaitForStop(ch)
90 for i := 0; i < 10; i++ {
91 m.Stop()
92 }
Cosmos Nicolaoub07fa772014-05-16 08:07:02 -070093 if want, got := veyron2.LocalStop, <-ch; want != got {
Jiri Simsa5293dcb2014-05-10 09:56:38 -070094 t.Errorf("WaitForStop want %q got %q", want, got)
95 }
96 select {
97 case s := <-ch:
98 t.Errorf("channel expected to be empty, got %q instead", s)
99 default:
100 }
101}
102
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700103func noWaiters(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
Cosmos Nicolaouf889c732014-10-16 20:46:54 -0700104 m, _ := rt.New(profileOpt)
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700105 fmt.Fprintf(stdout, "ready\n")
106 modules.WaitForEOF(stdin)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700107 m.Stop()
108 os.Exit(42) // This should not be reached.
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700109 return nil
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700110}
111
Bogdan Caprita1002ba42014-06-06 19:24:40 -0700112// TestNoWaiters verifies that the child process exits in the absence of any
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700113// wait channel being registered with its runtime.
114func TestNoWaiters(t *testing.T) {
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700115 sh := modules.NewShell(noWaitersCmd)
116 defer sh.Cleanup(os.Stderr, os.Stderr)
Cosmos Nicolaou612ad382014-10-29 19:41:35 -0700117 h, err := sh.Start(noWaitersCmd, nil)
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700118 if err != nil {
119 t.Fatalf("unexpected error: %s", err)
120 }
121 expect.NewSession(t, h.Stdout(), time.Minute).Expect("ready")
122 want := fmt.Sprintf("exit status %d", veyron2.UnhandledStopExitCode)
123 if err = h.Shutdown(os.Stderr, os.Stderr); err == nil || err.Error() != want {
124 t.Errorf("got %v, want %s", err, want)
125 }
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700126}
127
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700128func forceStop(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
Cosmos Nicolaouf889c732014-10-16 20:46:54 -0700129 m, _ := rt.New(profileOpt)
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700130 fmt.Fprintf(stdout, "ready\n")
131 modules.WaitForEOF(stdin)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700132 m.WaitForStop(make(chan string, 1))
133 m.ForceStop()
134 os.Exit(42) // This should not be reached.
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700135 return nil
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700136}
137
138// TestForceStop verifies that ForceStop causes the child process to exit
139// immediately.
140func TestForceStop(t *testing.T) {
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700141 sh := modules.NewShell(forceStopCmd)
142 defer sh.Cleanup(os.Stderr, os.Stderr)
Cosmos Nicolaou612ad382014-10-29 19:41:35 -0700143 h, err := sh.Start(forceStopCmd, nil)
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700144 if err != nil {
145 t.Fatalf("unexpected error: %s", err)
146 }
147 s := expect.NewSession(t, h.Stdout(), time.Minute)
148 s.Expect("ready")
149 err = h.Shutdown(os.Stderr, os.Stderr)
150 want := fmt.Sprintf("exit status %d", veyron2.UnhandledStopExitCode)
151 if err == nil || err.Error() != want {
152 t.Errorf("got %v, want %s", err, want)
153 }
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700154}
Bogdan Caprita0a9f9982014-06-11 15:50:08 -0700155
156func checkProgress(t *testing.T, ch <-chan veyron2.Task, progress, goal int) {
157 if want, got := (veyron2.Task{progress, goal}), <-ch; !reflect.DeepEqual(want, got) {
158 t.Errorf("Unexpected progress: want %+v, got %+v", want, got)
159 }
160}
161
162func checkNoProgress(t *testing.T, ch <-chan veyron2.Task) {
163 select {
164 case p := <-ch:
165 t.Errorf("channel expected to be empty, got %+v instead", p)
166 default:
167 }
168}
169
170// TestProgress verifies that the ticker update/track logic works for a single
171// tracker.
172func TestProgress(t *testing.T) {
Cosmos Nicolaouf889c732014-10-16 20:46:54 -0700173 m, _ := rt.New(profileOpt)
Bogdan Caprita0a9f9982014-06-11 15:50:08 -0700174 m.AdvanceGoal(50)
175 ch := make(chan veyron2.Task, 1)
176 m.TrackTask(ch)
177 checkNoProgress(t, ch)
178 m.AdvanceProgress(10)
179 checkProgress(t, ch, 10, 50)
180 checkNoProgress(t, ch)
181 m.AdvanceProgress(5)
182 checkProgress(t, ch, 15, 50)
183 m.AdvanceGoal(50)
184 checkProgress(t, ch, 15, 100)
185 m.AdvanceProgress(1)
186 checkProgress(t, ch, 16, 100)
187 m.AdvanceGoal(10)
188 checkProgress(t, ch, 16, 110)
189 m.AdvanceProgress(-13)
190 checkNoProgress(t, ch)
191 m.AdvanceGoal(0)
192 checkNoProgress(t, ch)
Bogdan Caprita4258d882014-07-02 09:15:22 -0700193 m.Cleanup()
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700194 if _, ok := <-ch; ok {
195 t.Errorf("Expected channel to be closed")
196 }
Bogdan Caprita0a9f9982014-06-11 15:50:08 -0700197}
198
199// TestProgressMultipleTrackers verifies that the ticker update/track logic
200// works for more than one tracker. It also ensures that the runtime doesn't
201// block when the tracker channels are full.
202func TestProgressMultipleTrackers(t *testing.T) {
Cosmos Nicolaouf889c732014-10-16 20:46:54 -0700203 m, _ := rt.New(profileOpt)
Bogdan Caprita0a9f9982014-06-11 15:50:08 -0700204 // ch1 is 1-buffered, ch2 is 2-buffered.
205 ch1, ch2 := make(chan veyron2.Task, 1), make(chan veyron2.Task, 2)
206 m.TrackTask(ch1)
207 m.TrackTask(ch2)
208 checkNoProgress(t, ch1)
209 checkNoProgress(t, ch2)
210 m.AdvanceProgress(1)
211 checkProgress(t, ch1, 1, 0)
212 checkNoProgress(t, ch1)
213 checkProgress(t, ch2, 1, 0)
214 checkNoProgress(t, ch2)
215 for i := 0; i < 10; i++ {
216 m.AdvanceProgress(1)
217 }
218 checkProgress(t, ch1, 2, 0)
219 checkNoProgress(t, ch1)
220 checkProgress(t, ch2, 2, 0)
221 checkProgress(t, ch2, 3, 0)
222 checkNoProgress(t, ch2)
223 m.AdvanceGoal(4)
224 checkProgress(t, ch1, 11, 4)
225 checkProgress(t, ch2, 11, 4)
Bogdan Caprita4258d882014-07-02 09:15:22 -0700226 m.Cleanup()
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700227 if _, ok := <-ch1; ok {
228 t.Errorf("Expected channel to be closed")
229 }
230 if _, ok := <-ch2; ok {
231 t.Errorf("Expected channel to be closed")
232 }
233}
234
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700235func app(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
Cosmos Nicolaouf889c732014-10-16 20:46:54 -0700236 r, err := rt.New(profileOpt)
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700237 if err != nil {
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700238 return err
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700239 }
Bogdan Caprita4258d882014-07-02 09:15:22 -0700240 defer r.Cleanup()
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700241 ch := make(chan string, 1)
242 r.WaitForStop(ch)
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700243 fmt.Fprintf(stdout, "Got %s\n", <-ch)
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700244 r.AdvanceGoal(10)
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700245 fmt.Fprintf(stdout, "Doing some work\n")
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700246 r.AdvanceProgress(2)
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700247 fmt.Fprintf(stdout, "Doing some more work\n")
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700248 r.AdvanceProgress(5)
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700249 return nil
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700250}
251
252type configServer struct {
253 ch chan<- string
254}
255
256func (c *configServer) Set(_ ipc.ServerContext, key, value string) error {
257 if key != mgmt.AppCycleManagerConfigKey {
258 return fmt.Errorf("Unexpected key: %v", key)
259 }
260 c.ch <- value
261 return nil
262
263}
264
Cosmos Nicolaou39a00e02014-08-14 11:04:14 -0700265func createConfigServer(t *testing.T, r veyron2.Runtime) (ipc.Server, string, <-chan string) {
266 server, err := r.NewServer()
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700267 if err != nil {
268 t.Fatalf("Got error: %v", err)
269 }
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700270 ch := make(chan string)
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -0700271
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700272 var ep naming.Endpoint
Cosmos Nicolaouf8d4c2b2014-10-23 22:36:38 -0700273 if ep, err = server.Listen(profiles.LocalListenSpec); err != nil {
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700274 t.Fatalf("Got error: %v", err)
275 }
Todd Wang702385a2014-11-07 01:54:08 -0800276 if err := server.Serve("", node.ConfigServer(&configServer{ch}), vflag.NewAuthorizerOrDie()); err != nil {
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -0700277 t.Fatalf("Got error: %v", err)
278 }
279 return server, naming.JoinAddressName(ep.String(), ""), ch
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700280
281}
282
Todd Wang702385a2014-11-07 01:54:08 -0800283func setupRemoteAppCycleMgr(t *testing.T) (veyron2.Runtime, modules.Handle, appcycle.AppCycleClientMethods, func()) {
Cosmos Nicolaou39a00e02014-08-14 11:04:14 -0700284 // We need to use the public API since stubs are used below (and they
285 // refer to the global rt.R() function), but we take care to make sure
286 // that the "google" runtime we are trying to test in this package is
287 // the one being used.
Cosmos Nicolaouaadfd4d2014-10-31 18:51:25 -0700288 r, _ := rt.New(profileOpt)
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700289
Cosmos Nicolaoubc22fa12014-10-14 20:51:55 -0700290 childcreds := security.NewVeyronCredentials(r.Principal(), appCmd)
Cosmos Nicolaou39a00e02014-08-14 11:04:14 -0700291 configServer, configServiceName, ch := createConfigServer(t, r)
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700292 sh := modules.NewShell(appCmd)
Asim Shankar95910b62014-10-31 22:02:29 -0700293 sh.SetVar(consts.VeyronCredentials, childcreds)
Jiri Simsa37893392014-11-07 10:55:45 -0800294 sh.SetConfigKey(mgmt.ParentNameConfigKey, configServiceName)
295 sh.SetConfigKey(mgmt.ProtocolConfigKey, "tcp")
296 sh.SetConfigKey(mgmt.AddressConfigKey, "127.0.0.1:0")
Cosmos Nicolaou612ad382014-10-29 19:41:35 -0700297 h, err := sh.Start("app", nil)
Cosmos Nicolaou59496fe2014-10-14 11:21:05 -0700298 if err != nil {
299 t.Fatalf("unexpected error: %s", err)
300 }
301
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700302 appCycleName := <-ch
Todd Wang702385a2014-11-07 01:54:08 -0800303 appCycle := appcycle.AppCycleClient(appCycleName)
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}