blob: 4208a00891c48931c4bea1acbf77d091cb9612e2 [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
Cosmos Nicolaoub07fa772014-05-16 08:07:02 -070010 "veyron2"
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -070011 "veyron2/ipc"
12 "veyron2/mgmt"
13 "veyron2/naming"
14 "veyron2/services/mgmt/appcycle"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070015
Jiri Simsa73e9cac2014-06-30 09:55:10 -070016 _ "veyron/lib/testutil"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070017 "veyron/lib/testutil/blackbox"
Jiri Simsa73e9cac2014-06-30 09:55:10 -070018 "veyron/lib/testutil/security"
Cosmos Nicolaoub07fa772014-05-16 08:07:02 -070019 "veyron/runtimes/google/rt"
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -070020 vflag "veyron/security/flag"
21 "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
242func createConfigServer(t *testing.T) (ipc.Server, string, <-chan string) {
243 server, err := rt.R().NewServer()
244 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 Nicolaoufdc838b2014-06-30 21:44:27 -0700253 if err := server.Serve("", ipc.SoloDispatcher(node.NewServerConfig(&configServer{ch}), vflag.NewAuthorizerOrDie())); err != nil {
254 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
260func setupRemoteAppCycleMgr(t *testing.T) (*blackbox.Child, appcycle.AppCycle, func()) {
261 r, err := rt.Init()
262 if err != nil {
263 t.Fatalf("Error creating runtime: %v", err)
264 }
265 c := blackbox.HelperCommand(t, "app")
266 id := r.Identity()
Jiri Simsa73e9cac2014-06-30 09:55:10 -0700267 idFile := security.SaveIdentityToFile(security.NewBlessedIdentity(id, "test"))
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700268 configServer, configServiceName, ch := createConfigServer(t)
269 c.Cmd.Env = append(c.Cmd.Env, fmt.Sprintf("VEYRON_IDENTITY=%v", idFile),
270 fmt.Sprintf("%v=%v", mgmt.ParentNodeManagerConfigKey, configServiceName))
271 c.Cmd.Start()
272 appCycleName := <-ch
273 appCycle, err := appcycle.BindAppCycle(appCycleName)
274 if err != nil {
275 t.Fatalf("Got error: %v", err)
276 }
277 return c, appCycle, func() {
278 configServer.Stop()
279 c.Cleanup()
280 os.Remove(idFile)
Bogdan Caprita4258d882014-07-02 09:15:22 -0700281 // Don't do r.Cleanup() since the runtime needs to be used by
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700282 // more than one test case.
283 }
284}
285
286// TestRemoteForceStop verifies that the child process exits when sending it
287// a remote ForceStop rpc.
288func TestRemoteForceStop(t *testing.T) {
289 c, appCycle, cleanup := setupRemoteAppCycleMgr(t)
290 defer cleanup()
291 if err := appCycle.ForceStop(rt.R().NewContext()); err == nil || !strings.Contains(err.Error(), "EOF") {
292 t.Fatalf("Expected EOF error, got %v instead", err)
293 }
294 c.ExpectEOFAndWaitForExitCode(fmt.Errorf("exit status %d", veyron2.ForceStopExitCode))
295}
296
297// TestRemoteStop verifies that the child shuts down cleanly when sending it
298// a remote Stop rpc.
299func TestRemoteStop(t *testing.T) {
300 c, appCycle, cleanup := setupRemoteAppCycleMgr(t)
301 defer cleanup()
302 stream, err := appCycle.Stop(rt.R().NewContext())
303 if err != nil {
304 t.Fatalf("Got error: %v", err)
305 }
Shyam Jayaraman97b9dca2014-07-31 13:30:46 -0700306 rStream := stream.RecvStream()
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700307 expectTask := func(progress, goal int32) {
Shyam Jayaraman97b9dca2014-07-31 13:30:46 -0700308 if !rStream.Advance() {
309 t.Fatalf("unexpected streaming error: %q", rStream.Err())
Shyam Jayaramanc4aed6e2014-07-22 14:25:06 -0700310 }
Shyam Jayaraman97b9dca2014-07-31 13:30:46 -0700311 task := rStream.Value()
Shyam Jayaramanc4aed6e2014-07-22 14:25:06 -0700312 if task.Progress != progress || task.Goal != goal {
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700313 t.Errorf("Got (%d, %d), want (%d, %d)", task.Progress, task.Goal, progress, goal)
314 }
315 }
316 expectTask(0, 10)
317 expectTask(2, 10)
318 expectTask(7, 10)
Shyam Jayaraman97b9dca2014-07-31 13:30:46 -0700319 if rStream.Advance() || rStream.Err() != nil {
320 t.Errorf("Expected EOF, got (%v, %v) instead", rStream.Value(), rStream.Err())
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700321 }
322 if err := stream.Finish(); err != nil {
323 t.Errorf("Got error %v", err)
324 }
325 c.Expect(fmt.Sprintf("Got %s", veyron2.RemoteStop))
326 c.Expect("Doing some work")
327 c.Expect("Doing some more work")
328 c.ExpectEOFAndWait()
Bogdan Caprita0a9f9982014-06-11 15:50:08 -0700329}