blob: 3bf195e31a600f766f47c30c8c5ca019a1fca5b7 [file] [log] [blame]
Cosmos Nicolaoub07fa772014-05-16 08:07:02 -07001package rt_test
Jiri Simsa5293dcb2014-05-10 09:56:38 -07002
3import (
4 "fmt"
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -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"
10
Cosmos Nicolaoub07fa772014-05-16 08:07:02 -070011 "veyron2"
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -070012 "veyron2/ipc"
13 "veyron2/mgmt"
14 "veyron2/naming"
15 "veyron2/services/mgmt/appcycle"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070016
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -070017 "veyron/lib/testutil"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070018 "veyron/lib/testutil/blackbox"
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 Capritaa4d9ee42014-06-20 16:42:53 -0700166 m.Shutdown()
167 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 Capritaa4d9ee42014-06-20 16:42:53 -0700199 m.Shutdown()
200 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 }
218 defer r.Shutdown()
219 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 }
247 const suffix = ""
248 ch := make(chan string)
249 if err := server.Register(suffix, ipc.SoloDispatcher(node.NewServerConfig(&configServer{ch}), vflag.NewAuthorizerOrDie())); err != nil {
250 t.Fatalf("Got error: %v", err)
251 }
252 var ep naming.Endpoint
253 if ep, err = server.Listen("tcp", "127.0.0.1:0"); err != nil {
254 t.Fatalf("Got error: %v", err)
255 }
256 return server, naming.JoinAddressName(ep.String(), suffix), ch
257
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()
267 idFile := testutil.SaveIdentityToFile(testutil.NewBlessedIdentity(id, "test"))
268 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)
281 // Don't do r.Shutdown() since the runtime needs to be used by
282 // 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 }
306 expectTask := func(progress, goal int32) {
307 if task, err := stream.Recv(); err != nil {
308 t.Fatalf("unexpected streaming error: %q", err)
309 } else if task.Progress != progress || task.Goal != goal {
310 t.Errorf("Got (%d, %d), want (%d, %d)", task.Progress, task.Goal, progress, goal)
311 }
312 }
313 expectTask(0, 10)
314 expectTask(2, 10)
315 expectTask(7, 10)
316 if task, err := stream.Recv(); err != io.EOF {
317 t.Errorf("Expected (nil, EOF), got (%v, %v) instead", task, err)
318 }
319 if err := stream.Finish(); err != nil {
320 t.Errorf("Got error %v", err)
321 }
322 c.Expect(fmt.Sprintf("Got %s", veyron2.RemoteStop))
323 c.Expect("Doing some work")
324 c.Expect("Doing some more work")
325 c.ExpectEOFAndWait()
Bogdan Caprita0a9f9982014-06-11 15:50:08 -0700326}