blob: d11e082a2f2920a7e852447b650548229cbc89cf [file] [log] [blame]
Cosmos Nicolaoub07fa772014-05-16 08:07:02 -07001package rt_test
Jiri Simsa5293dcb2014-05-10 09:56:38 -07002
3import (
4 "fmt"
5 "os"
Bogdan Caprita0a9f9982014-06-11 15:50:08 -07006 "reflect"
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -07007 "strings"
Jiri Simsa5293dcb2014-05-10 09:56:38 -07008 "testing"
9
Jiri Simsa519c5072014-09-17 21:37:57 -070010 "veyron.io/veyron/veyron2"
11 "veyron.io/veyron/veyron2/ipc"
12 "veyron.io/veyron/veyron2/mgmt"
13 "veyron.io/veyron/veyron2/naming"
14 "veyron.io/veyron/veyron2/services/mgmt/appcycle"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070015
Jiri Simsa519c5072014-09-17 21:37:57 -070016 _ "veyron.io/veyron/veyron/lib/testutil"
17 "veyron.io/veyron/veyron/lib/testutil/blackbox"
18 "veyron.io/veyron/veyron/lib/testutil/security"
19 "veyron.io/veyron/veyron/runtimes/google/rt"
20 vflag "veyron.io/veyron/veyron/security/flag"
21 "veyron.io/veyron/veyron/services/mgmt/node"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070022)
23
Jiri Simsa5293dcb2014-05-10 09:56:38 -070024// TestBasic verifies that the basic plumbing works: LocalStop calls result in
25// stop messages being sent on the channel passed to WaitForStop.
26func TestBasic(t *testing.T) {
Cosmos Nicolaoub07fa772014-05-16 08:07:02 -070027 m, _ := rt.New()
Jiri Simsa5293dcb2014-05-10 09:56:38 -070028 ch := make(chan string, 1)
29 m.WaitForStop(ch)
30 for i := 0; i < 10; i++ {
31 m.Stop()
Cosmos Nicolaoub07fa772014-05-16 08:07:02 -070032 if want, got := veyron2.LocalStop, <-ch; want != got {
Jiri Simsa5293dcb2014-05-10 09:56:38 -070033 t.Errorf("WaitForStop want %q got %q", want, got)
34 }
35 select {
36 case s := <-ch:
37 t.Errorf("channel expected to be empty, got %q instead", s)
38 default:
39 }
40 }
41}
42
43// TestMultipleWaiters verifies that the plumbing works with more than one
44// registered wait channel.
45func TestMultipleWaiters(t *testing.T) {
Cosmos Nicolaoub07fa772014-05-16 08:07:02 -070046 m, _ := rt.New()
Jiri Simsa5293dcb2014-05-10 09:56:38 -070047 ch1 := make(chan string, 1)
48 m.WaitForStop(ch1)
49 ch2 := make(chan string, 1)
50 m.WaitForStop(ch2)
51 for i := 0; i < 10; i++ {
52 m.Stop()
Cosmos Nicolaoub07fa772014-05-16 08:07:02 -070053 if want, got := veyron2.LocalStop, <-ch1; want != got {
Jiri Simsa5293dcb2014-05-10 09:56:38 -070054 t.Errorf("WaitForStop want %q got %q", want, got)
55 }
Cosmos Nicolaoub07fa772014-05-16 08:07:02 -070056 if want, got := veyron2.LocalStop, <-ch2; want != got {
Jiri Simsa5293dcb2014-05-10 09:56:38 -070057 t.Errorf("WaitForStop want %q got %q", want, got)
58 }
59 }
60}
61
62// TestMultipleStops verifies that LocalStop does not block even if the wait
63// channel is not being drained: once the channel's buffer fills up, future
64// Stops become no-ops.
65func TestMultipleStops(t *testing.T) {
Cosmos Nicolaoub07fa772014-05-16 08:07:02 -070066 m, _ := rt.New()
Jiri Simsa5293dcb2014-05-10 09:56:38 -070067 ch := make(chan string, 1)
68 m.WaitForStop(ch)
69 for i := 0; i < 10; i++ {
70 m.Stop()
71 }
Cosmos Nicolaoub07fa772014-05-16 08:07:02 -070072 if want, got := veyron2.LocalStop, <-ch; want != got {
Jiri Simsa5293dcb2014-05-10 09:56:38 -070073 t.Errorf("WaitForStop want %q got %q", want, got)
74 }
75 select {
76 case s := <-ch:
77 t.Errorf("channel expected to be empty, got %q instead", s)
78 default:
79 }
80}
81
82func init() {
83 blackbox.CommandTable["noWaiters"] = noWaiters
84}
85
86func noWaiters([]string) {
Cosmos Nicolaoub07fa772014-05-16 08:07:02 -070087 m, _ := rt.New()
Jiri Simsa5293dcb2014-05-10 09:56:38 -070088 fmt.Println("ready")
89 blackbox.WaitForEOFOnStdin()
90 m.Stop()
91 os.Exit(42) // This should not be reached.
92}
93
Bogdan Caprita1002ba42014-06-06 19:24:40 -070094// TestNoWaiters verifies that the child process exits in the absence of any
Jiri Simsa5293dcb2014-05-10 09:56:38 -070095// wait channel being registered with its runtime.
96func TestNoWaiters(t *testing.T) {
97 c := blackbox.HelperCommand(t, "noWaiters")
98 defer c.Cleanup()
99 c.Cmd.Start()
100 c.Expect("ready")
101 c.CloseStdin()
Cosmos Nicolaoub07fa772014-05-16 08:07:02 -0700102 c.ExpectEOFAndWaitForExitCode(fmt.Errorf("exit status %d", veyron2.UnhandledStopExitCode))
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700103}
104
105func init() {
106 blackbox.CommandTable["forceStop"] = forceStop
107}
108
109func forceStop([]string) {
Cosmos Nicolaoub07fa772014-05-16 08:07:02 -0700110 m, _ := rt.New()
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700111 fmt.Println("ready")
112 blackbox.WaitForEOFOnStdin()
113 m.WaitForStop(make(chan string, 1))
114 m.ForceStop()
115 os.Exit(42) // This should not be reached.
116}
117
118// TestForceStop verifies that ForceStop causes the child process to exit
119// immediately.
120func TestForceStop(t *testing.T) {
121 c := blackbox.HelperCommand(t, "forceStop")
122 defer c.Cleanup()
123 c.Cmd.Start()
124 c.Expect("ready")
125 c.CloseStdin()
Cosmos Nicolaoub07fa772014-05-16 08:07:02 -0700126 c.ExpectEOFAndWaitForExitCode(fmt.Errorf("exit status %d", veyron2.ForceStopExitCode))
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700127}
Bogdan Caprita0a9f9982014-06-11 15:50:08 -0700128
129func checkProgress(t *testing.T, ch <-chan veyron2.Task, progress, goal int) {
130 if want, got := (veyron2.Task{progress, goal}), <-ch; !reflect.DeepEqual(want, got) {
131 t.Errorf("Unexpected progress: want %+v, got %+v", want, got)
132 }
133}
134
135func checkNoProgress(t *testing.T, ch <-chan veyron2.Task) {
136 select {
137 case p := <-ch:
138 t.Errorf("channel expected to be empty, got %+v instead", p)
139 default:
140 }
141}
142
143// TestProgress verifies that the ticker update/track logic works for a single
144// tracker.
145func TestProgress(t *testing.T) {
146 m, _ := rt.New()
147 m.AdvanceGoal(50)
148 ch := make(chan veyron2.Task, 1)
149 m.TrackTask(ch)
150 checkNoProgress(t, ch)
151 m.AdvanceProgress(10)
152 checkProgress(t, ch, 10, 50)
153 checkNoProgress(t, ch)
154 m.AdvanceProgress(5)
155 checkProgress(t, ch, 15, 50)
156 m.AdvanceGoal(50)
157 checkProgress(t, ch, 15, 100)
158 m.AdvanceProgress(1)
159 checkProgress(t, ch, 16, 100)
160 m.AdvanceGoal(10)
161 checkProgress(t, ch, 16, 110)
162 m.AdvanceProgress(-13)
163 checkNoProgress(t, ch)
164 m.AdvanceGoal(0)
165 checkNoProgress(t, ch)
Bogdan Caprita4258d882014-07-02 09:15:22 -0700166 m.Cleanup()
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700167 if _, ok := <-ch; ok {
168 t.Errorf("Expected channel to be closed")
169 }
Bogdan Caprita0a9f9982014-06-11 15:50:08 -0700170}
171
172// TestProgressMultipleTrackers verifies that the ticker update/track logic
173// works for more than one tracker. It also ensures that the runtime doesn't
174// block when the tracker channels are full.
175func TestProgressMultipleTrackers(t *testing.T) {
176 m, _ := rt.New()
177 // ch1 is 1-buffered, ch2 is 2-buffered.
178 ch1, ch2 := make(chan veyron2.Task, 1), make(chan veyron2.Task, 2)
179 m.TrackTask(ch1)
180 m.TrackTask(ch2)
181 checkNoProgress(t, ch1)
182 checkNoProgress(t, ch2)
183 m.AdvanceProgress(1)
184 checkProgress(t, ch1, 1, 0)
185 checkNoProgress(t, ch1)
186 checkProgress(t, ch2, 1, 0)
187 checkNoProgress(t, ch2)
188 for i := 0; i < 10; i++ {
189 m.AdvanceProgress(1)
190 }
191 checkProgress(t, ch1, 2, 0)
192 checkNoProgress(t, ch1)
193 checkProgress(t, ch2, 2, 0)
194 checkProgress(t, ch2, 3, 0)
195 checkNoProgress(t, ch2)
196 m.AdvanceGoal(4)
197 checkProgress(t, ch1, 11, 4)
198 checkProgress(t, ch2, 11, 4)
Bogdan Caprita4258d882014-07-02 09:15:22 -0700199 m.Cleanup()
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700200 if _, ok := <-ch1; ok {
201 t.Errorf("Expected channel to be closed")
202 }
203 if _, ok := <-ch2; ok {
204 t.Errorf("Expected channel to be closed")
205 }
206}
207
208func init() {
209 blackbox.CommandTable["app"] = app
210}
211
212func app([]string) {
213 r, err := rt.New()
214 if err != nil {
215 fmt.Printf("Error creating runtime: %v\n", err)
216 return
217 }
Bogdan Caprita4258d882014-07-02 09:15:22 -0700218 defer r.Cleanup()
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700219 ch := make(chan string, 1)
220 r.WaitForStop(ch)
221 fmt.Printf("Got %s\n", <-ch)
222 r.AdvanceGoal(10)
223 fmt.Println("Doing some work")
224 r.AdvanceProgress(2)
225 fmt.Println("Doing some more work")
226 r.AdvanceProgress(5)
227}
228
229type configServer struct {
230 ch chan<- string
231}
232
233func (c *configServer) Set(_ ipc.ServerContext, key, value string) error {
234 if key != mgmt.AppCycleManagerConfigKey {
235 return fmt.Errorf("Unexpected key: %v", key)
236 }
237 c.ch <- value
238 return nil
239
240}
241
Cosmos Nicolaou39a00e02014-08-14 11:04:14 -0700242func createConfigServer(t *testing.T, r veyron2.Runtime) (ipc.Server, string, <-chan string) {
243 server, err := r.NewServer()
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700244 if err != nil {
245 t.Fatalf("Got error: %v", err)
246 }
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700247 ch := make(chan string)
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -0700248
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700249 var ep naming.Endpoint
250 if ep, err = server.Listen("tcp", "127.0.0.1:0"); err != nil {
251 t.Fatalf("Got error: %v", err)
252 }
Cosmos Nicolaou8bfacf22014-08-19 11:19:36 -0700253 if err := server.Serve("", ipc.LeafDispatcher(node.NewServerConfig(&configServer{ch}), vflag.NewAuthorizerOrDie())); err != nil {
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -0700254 t.Fatalf("Got error: %v", err)
255 }
256 return server, naming.JoinAddressName(ep.String(), ""), ch
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700257
258}
259
Matt Rosencrantzbf85d542014-08-22 13:31:14 -0700260func setupRemoteAppCycleMgr(t *testing.T) (veyron2.Runtime, *blackbox.Child, appcycle.AppCycle, func()) {
Cosmos Nicolaou39a00e02014-08-14 11:04:14 -0700261 // We need to use the public API since stubs are used below (and they
262 // refer to the global rt.R() function), but we take care to make sure
263 // that the "google" runtime we are trying to test in this package is
264 // the one being used.
Matt Rosencrantzbf85d542014-08-22 13:31:14 -0700265 r, _ := rt.New(veyron2.RuntimeOpt{veyron2.GoogleRuntimeName})
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700266 c := blackbox.HelperCommand(t, "app")
267 id := r.Identity()
Jiri Simsa73e9cac2014-06-30 09:55:10 -0700268 idFile := security.SaveIdentityToFile(security.NewBlessedIdentity(id, "test"))
Cosmos Nicolaou39a00e02014-08-14 11:04:14 -0700269 configServer, configServiceName, ch := createConfigServer(t, r)
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700270 c.Cmd.Env = append(c.Cmd.Env, fmt.Sprintf("VEYRON_IDENTITY=%v", idFile),
271 fmt.Sprintf("%v=%v", mgmt.ParentNodeManagerConfigKey, configServiceName))
272 c.Cmd.Start()
273 appCycleName := <-ch
274 appCycle, err := appcycle.BindAppCycle(appCycleName)
275 if err != nil {
276 t.Fatalf("Got error: %v", err)
277 }
Matt Rosencrantzbf85d542014-08-22 13:31:14 -0700278 return r, c, appCycle, func() {
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700279 configServer.Stop()
280 c.Cleanup()
281 os.Remove(idFile)
Bogdan Caprita4258d882014-07-02 09:15:22 -0700282 // Don't do r.Cleanup() since the runtime needs to be used by
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700283 // more than one test case.
284 }
285}
286
287// TestRemoteForceStop verifies that the child process exits when sending it
288// a remote ForceStop rpc.
289func TestRemoteForceStop(t *testing.T) {
Matt Rosencrantzbf85d542014-08-22 13:31:14 -0700290 r, c, appCycle, cleanup := setupRemoteAppCycleMgr(t)
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700291 defer cleanup()
Cosmos Nicolaou39a00e02014-08-14 11:04:14 -0700292 if err := appCycle.ForceStop(r.NewContext()); err == nil || !strings.Contains(err.Error(), "EOF") {
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700293 t.Fatalf("Expected EOF error, got %v instead", err)
294 }
295 c.ExpectEOFAndWaitForExitCode(fmt.Errorf("exit status %d", veyron2.ForceStopExitCode))
296}
297
298// TestRemoteStop verifies that the child shuts down cleanly when sending it
299// a remote Stop rpc.
300func TestRemoteStop(t *testing.T) {
Matt Rosencrantzbf85d542014-08-22 13:31:14 -0700301 r, c, appCycle, cleanup := setupRemoteAppCycleMgr(t)
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700302 defer cleanup()
Cosmos Nicolaou39a00e02014-08-14 11:04:14 -0700303 stream, err := appCycle.Stop(r.NewContext())
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700304 if err != nil {
305 t.Fatalf("Got error: %v", err)
306 }
Shyam Jayaraman97b9dca2014-07-31 13:30:46 -0700307 rStream := stream.RecvStream()
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700308 expectTask := func(progress, goal int32) {
Shyam Jayaraman97b9dca2014-07-31 13:30:46 -0700309 if !rStream.Advance() {
310 t.Fatalf("unexpected streaming error: %q", rStream.Err())
Shyam Jayaramanc4aed6e2014-07-22 14:25:06 -0700311 }
Shyam Jayaraman97b9dca2014-07-31 13:30:46 -0700312 task := rStream.Value()
Shyam Jayaramanc4aed6e2014-07-22 14:25:06 -0700313 if task.Progress != progress || task.Goal != goal {
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700314 t.Errorf("Got (%d, %d), want (%d, %d)", task.Progress, task.Goal, progress, goal)
315 }
316 }
317 expectTask(0, 10)
318 expectTask(2, 10)
319 expectTask(7, 10)
Shyam Jayaraman97b9dca2014-07-31 13:30:46 -0700320 if rStream.Advance() || rStream.Err() != nil {
321 t.Errorf("Expected EOF, got (%v, %v) instead", rStream.Value(), rStream.Err())
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700322 }
323 if err := stream.Finish(); err != nil {
324 t.Errorf("Got error %v", err)
325 }
326 c.Expect(fmt.Sprintf("Got %s", veyron2.RemoteStop))
327 c.Expect("Doing some work")
328 c.Expect("Doing some more work")
329 c.ExpectEOFAndWait()
Bogdan Caprita0a9f9982014-06-11 15:50:08 -0700330}