blob: df084f2ca0df69644cb79c136c6d641015601246 [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"
Cosmos Nicolaoud6c3c9c2014-09-30 15:42:53 -070019 "veyron.io/veyron/veyron/profiles"
Jiri Simsa519c5072014-09-17 21:37:57 -070020 "veyron.io/veyron/veyron/runtimes/google/rt"
21 vflag "veyron.io/veyron/veyron/security/flag"
22 "veyron.io/veyron/veyron/services/mgmt/node"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070023)
24
Jiri Simsa5293dcb2014-05-10 09:56:38 -070025// TestBasic verifies that the basic plumbing works: LocalStop calls result in
26// stop messages being sent on the channel passed to WaitForStop.
27func TestBasic(t *testing.T) {
Cosmos Nicolaoub07fa772014-05-16 08:07:02 -070028 m, _ := rt.New()
Jiri Simsa5293dcb2014-05-10 09:56:38 -070029 ch := make(chan string, 1)
30 m.WaitForStop(ch)
31 for i := 0; i < 10; i++ {
32 m.Stop()
Cosmos Nicolaoub07fa772014-05-16 08:07:02 -070033 if want, got := veyron2.LocalStop, <-ch; want != got {
Jiri Simsa5293dcb2014-05-10 09:56:38 -070034 t.Errorf("WaitForStop want %q got %q", want, got)
35 }
36 select {
37 case s := <-ch:
38 t.Errorf("channel expected to be empty, got %q instead", s)
39 default:
40 }
41 }
42}
43
44// TestMultipleWaiters verifies that the plumbing works with more than one
45// registered wait channel.
46func TestMultipleWaiters(t *testing.T) {
Cosmos Nicolaoub07fa772014-05-16 08:07:02 -070047 m, _ := rt.New()
Jiri Simsa5293dcb2014-05-10 09:56:38 -070048 ch1 := make(chan string, 1)
49 m.WaitForStop(ch1)
50 ch2 := make(chan string, 1)
51 m.WaitForStop(ch2)
52 for i := 0; i < 10; i++ {
53 m.Stop()
Cosmos Nicolaoub07fa772014-05-16 08:07:02 -070054 if want, got := veyron2.LocalStop, <-ch1; want != got {
Jiri Simsa5293dcb2014-05-10 09:56:38 -070055 t.Errorf("WaitForStop want %q got %q", want, got)
56 }
Cosmos Nicolaoub07fa772014-05-16 08:07:02 -070057 if want, got := veyron2.LocalStop, <-ch2; want != got {
Jiri Simsa5293dcb2014-05-10 09:56:38 -070058 t.Errorf("WaitForStop want %q got %q", want, got)
59 }
60 }
61}
62
63// TestMultipleStops verifies that LocalStop does not block even if the wait
64// channel is not being drained: once the channel's buffer fills up, future
65// Stops become no-ops.
66func TestMultipleStops(t *testing.T) {
Cosmos Nicolaoub07fa772014-05-16 08:07:02 -070067 m, _ := rt.New()
Jiri Simsa5293dcb2014-05-10 09:56:38 -070068 ch := make(chan string, 1)
69 m.WaitForStop(ch)
70 for i := 0; i < 10; i++ {
71 m.Stop()
72 }
Cosmos Nicolaoub07fa772014-05-16 08:07:02 -070073 if want, got := veyron2.LocalStop, <-ch; want != got {
Jiri Simsa5293dcb2014-05-10 09:56:38 -070074 t.Errorf("WaitForStop want %q got %q", want, got)
75 }
76 select {
77 case s := <-ch:
78 t.Errorf("channel expected to be empty, got %q instead", s)
79 default:
80 }
81}
82
83func init() {
84 blackbox.CommandTable["noWaiters"] = noWaiters
85}
86
87func noWaiters([]string) {
Cosmos Nicolaoub07fa772014-05-16 08:07:02 -070088 m, _ := rt.New()
Jiri Simsa5293dcb2014-05-10 09:56:38 -070089 fmt.Println("ready")
90 blackbox.WaitForEOFOnStdin()
91 m.Stop()
92 os.Exit(42) // This should not be reached.
93}
94
Bogdan Caprita1002ba42014-06-06 19:24:40 -070095// TestNoWaiters verifies that the child process exits in the absence of any
Jiri Simsa5293dcb2014-05-10 09:56:38 -070096// wait channel being registered with its runtime.
97func TestNoWaiters(t *testing.T) {
98 c := blackbox.HelperCommand(t, "noWaiters")
99 defer c.Cleanup()
100 c.Cmd.Start()
101 c.Expect("ready")
102 c.CloseStdin()
Cosmos Nicolaoub07fa772014-05-16 08:07:02 -0700103 c.ExpectEOFAndWaitForExitCode(fmt.Errorf("exit status %d", veyron2.UnhandledStopExitCode))
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700104}
105
106func init() {
107 blackbox.CommandTable["forceStop"] = forceStop
108}
109
110func forceStop([]string) {
Cosmos Nicolaoub07fa772014-05-16 08:07:02 -0700111 m, _ := rt.New()
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700112 fmt.Println("ready")
113 blackbox.WaitForEOFOnStdin()
114 m.WaitForStop(make(chan string, 1))
115 m.ForceStop()
116 os.Exit(42) // This should not be reached.
117}
118
119// TestForceStop verifies that ForceStop causes the child process to exit
120// immediately.
121func TestForceStop(t *testing.T) {
122 c := blackbox.HelperCommand(t, "forceStop")
123 defer c.Cleanup()
124 c.Cmd.Start()
125 c.Expect("ready")
126 c.CloseStdin()
Cosmos Nicolaoub07fa772014-05-16 08:07:02 -0700127 c.ExpectEOFAndWaitForExitCode(fmt.Errorf("exit status %d", veyron2.ForceStopExitCode))
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700128}
Bogdan Caprita0a9f9982014-06-11 15:50:08 -0700129
130func checkProgress(t *testing.T, ch <-chan veyron2.Task, progress, goal int) {
131 if want, got := (veyron2.Task{progress, goal}), <-ch; !reflect.DeepEqual(want, got) {
132 t.Errorf("Unexpected progress: want %+v, got %+v", want, got)
133 }
134}
135
136func checkNoProgress(t *testing.T, ch <-chan veyron2.Task) {
137 select {
138 case p := <-ch:
139 t.Errorf("channel expected to be empty, got %+v instead", p)
140 default:
141 }
142}
143
144// TestProgress verifies that the ticker update/track logic works for a single
145// tracker.
146func TestProgress(t *testing.T) {
147 m, _ := rt.New()
148 m.AdvanceGoal(50)
149 ch := make(chan veyron2.Task, 1)
150 m.TrackTask(ch)
151 checkNoProgress(t, ch)
152 m.AdvanceProgress(10)
153 checkProgress(t, ch, 10, 50)
154 checkNoProgress(t, ch)
155 m.AdvanceProgress(5)
156 checkProgress(t, ch, 15, 50)
157 m.AdvanceGoal(50)
158 checkProgress(t, ch, 15, 100)
159 m.AdvanceProgress(1)
160 checkProgress(t, ch, 16, 100)
161 m.AdvanceGoal(10)
162 checkProgress(t, ch, 16, 110)
163 m.AdvanceProgress(-13)
164 checkNoProgress(t, ch)
165 m.AdvanceGoal(0)
166 checkNoProgress(t, ch)
Bogdan Caprita4258d882014-07-02 09:15:22 -0700167 m.Cleanup()
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700168 if _, ok := <-ch; ok {
169 t.Errorf("Expected channel to be closed")
170 }
Bogdan Caprita0a9f9982014-06-11 15:50:08 -0700171}
172
173// TestProgressMultipleTrackers verifies that the ticker update/track logic
174// works for more than one tracker. It also ensures that the runtime doesn't
175// block when the tracker channels are full.
176func TestProgressMultipleTrackers(t *testing.T) {
177 m, _ := rt.New()
178 // ch1 is 1-buffered, ch2 is 2-buffered.
179 ch1, ch2 := make(chan veyron2.Task, 1), make(chan veyron2.Task, 2)
180 m.TrackTask(ch1)
181 m.TrackTask(ch2)
182 checkNoProgress(t, ch1)
183 checkNoProgress(t, ch2)
184 m.AdvanceProgress(1)
185 checkProgress(t, ch1, 1, 0)
186 checkNoProgress(t, ch1)
187 checkProgress(t, ch2, 1, 0)
188 checkNoProgress(t, ch2)
189 for i := 0; i < 10; i++ {
190 m.AdvanceProgress(1)
191 }
192 checkProgress(t, ch1, 2, 0)
193 checkNoProgress(t, ch1)
194 checkProgress(t, ch2, 2, 0)
195 checkProgress(t, ch2, 3, 0)
196 checkNoProgress(t, ch2)
197 m.AdvanceGoal(4)
198 checkProgress(t, ch1, 11, 4)
199 checkProgress(t, ch2, 11, 4)
Bogdan Caprita4258d882014-07-02 09:15:22 -0700200 m.Cleanup()
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700201 if _, ok := <-ch1; ok {
202 t.Errorf("Expected channel to be closed")
203 }
204 if _, ok := <-ch2; ok {
205 t.Errorf("Expected channel to be closed")
206 }
207}
208
209func init() {
210 blackbox.CommandTable["app"] = app
211}
212
213func app([]string) {
214 r, err := rt.New()
215 if err != nil {
216 fmt.Printf("Error creating runtime: %v\n", err)
217 return
218 }
Bogdan Caprita4258d882014-07-02 09:15:22 -0700219 defer r.Cleanup()
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700220 ch := make(chan string, 1)
221 r.WaitForStop(ch)
222 fmt.Printf("Got %s\n", <-ch)
223 r.AdvanceGoal(10)
224 fmt.Println("Doing some work")
225 r.AdvanceProgress(2)
226 fmt.Println("Doing some more work")
227 r.AdvanceProgress(5)
228}
229
230type configServer struct {
231 ch chan<- string
232}
233
234func (c *configServer) Set(_ ipc.ServerContext, key, value string) error {
235 if key != mgmt.AppCycleManagerConfigKey {
236 return fmt.Errorf("Unexpected key: %v", key)
237 }
238 c.ch <- value
239 return nil
240
241}
242
Cosmos Nicolaou39a00e02014-08-14 11:04:14 -0700243func createConfigServer(t *testing.T, r veyron2.Runtime) (ipc.Server, string, <-chan string) {
244 server, err := r.NewServer()
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700245 if err != nil {
246 t.Fatalf("Got error: %v", err)
247 }
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700248 ch := make(chan string)
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -0700249
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700250 var ep naming.Endpoint
Cosmos Nicolaoud6c3c9c2014-09-30 15:42:53 -0700251 if ep, err = server.ListenX(profiles.LocalListenSpec); err != nil {
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700252 t.Fatalf("Got error: %v", err)
253 }
Cosmos Nicolaou8bfacf22014-08-19 11:19:36 -0700254 if err := server.Serve("", ipc.LeafDispatcher(node.NewServerConfig(&configServer{ch}), vflag.NewAuthorizerOrDie())); err != nil {
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -0700255 t.Fatalf("Got error: %v", err)
256 }
257 return server, naming.JoinAddressName(ep.String(), ""), ch
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700258
259}
260
Matt Rosencrantzbf85d542014-08-22 13:31:14 -0700261func setupRemoteAppCycleMgr(t *testing.T) (veyron2.Runtime, *blackbox.Child, appcycle.AppCycle, func()) {
Cosmos Nicolaou39a00e02014-08-14 11:04:14 -0700262 // We need to use the public API since stubs are used below (and they
263 // refer to the global rt.R() function), but we take care to make sure
264 // that the "google" runtime we are trying to test in this package is
265 // the one being used.
Matt Rosencrantzbf85d542014-08-22 13:31:14 -0700266 r, _ := rt.New(veyron2.RuntimeOpt{veyron2.GoogleRuntimeName})
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700267 c := blackbox.HelperCommand(t, "app")
268 id := r.Identity()
Jiri Simsa73e9cac2014-06-30 09:55:10 -0700269 idFile := security.SaveIdentityToFile(security.NewBlessedIdentity(id, "test"))
Cosmos Nicolaou39a00e02014-08-14 11:04:14 -0700270 configServer, configServiceName, ch := createConfigServer(t, r)
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700271 c.Cmd.Env = append(c.Cmd.Env, fmt.Sprintf("VEYRON_IDENTITY=%v", idFile),
272 fmt.Sprintf("%v=%v", mgmt.ParentNodeManagerConfigKey, configServiceName))
273 c.Cmd.Start()
274 appCycleName := <-ch
275 appCycle, err := appcycle.BindAppCycle(appCycleName)
276 if err != nil {
277 t.Fatalf("Got error: %v", err)
278 }
Matt Rosencrantzbf85d542014-08-22 13:31:14 -0700279 return r, c, appCycle, func() {
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700280 configServer.Stop()
281 c.Cleanup()
282 os.Remove(idFile)
Bogdan Caprita4258d882014-07-02 09:15:22 -0700283 // Don't do r.Cleanup() since the runtime needs to be used by
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700284 // more than one test case.
285 }
286}
287
288// TestRemoteForceStop verifies that the child process exits when sending it
289// a remote ForceStop rpc.
290func TestRemoteForceStop(t *testing.T) {
Matt Rosencrantzbf85d542014-08-22 13:31:14 -0700291 r, c, appCycle, cleanup := setupRemoteAppCycleMgr(t)
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700292 defer cleanup()
Cosmos Nicolaou39a00e02014-08-14 11:04:14 -0700293 if err := appCycle.ForceStop(r.NewContext()); err == nil || !strings.Contains(err.Error(), "EOF") {
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700294 t.Fatalf("Expected EOF error, got %v instead", err)
295 }
296 c.ExpectEOFAndWaitForExitCode(fmt.Errorf("exit status %d", veyron2.ForceStopExitCode))
297}
298
299// TestRemoteStop verifies that the child shuts down cleanly when sending it
300// a remote Stop rpc.
301func TestRemoteStop(t *testing.T) {
Matt Rosencrantzbf85d542014-08-22 13:31:14 -0700302 r, c, appCycle, cleanup := setupRemoteAppCycleMgr(t)
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700303 defer cleanup()
Cosmos Nicolaou39a00e02014-08-14 11:04:14 -0700304 stream, err := appCycle.Stop(r.NewContext())
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700305 if err != nil {
306 t.Fatalf("Got error: %v", err)
307 }
Shyam Jayaraman97b9dca2014-07-31 13:30:46 -0700308 rStream := stream.RecvStream()
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700309 expectTask := func(progress, goal int32) {
Shyam Jayaraman97b9dca2014-07-31 13:30:46 -0700310 if !rStream.Advance() {
311 t.Fatalf("unexpected streaming error: %q", rStream.Err())
Shyam Jayaramanc4aed6e2014-07-22 14:25:06 -0700312 }
Shyam Jayaraman97b9dca2014-07-31 13:30:46 -0700313 task := rStream.Value()
Shyam Jayaramanc4aed6e2014-07-22 14:25:06 -0700314 if task.Progress != progress || task.Goal != goal {
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700315 t.Errorf("Got (%d, %d), want (%d, %d)", task.Progress, task.Goal, progress, goal)
316 }
317 }
318 expectTask(0, 10)
319 expectTask(2, 10)
320 expectTask(7, 10)
Shyam Jayaraman97b9dca2014-07-31 13:30:46 -0700321 if rStream.Advance() || rStream.Err() != nil {
322 t.Errorf("Expected EOF, got (%v, %v) instead", rStream.Value(), rStream.Err())
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700323 }
324 if err := stream.Finish(); err != nil {
325 t.Errorf("Got error %v", err)
326 }
327 c.Expect(fmt.Sprintf("Got %s", veyron2.RemoteStop))
328 c.Expect("Doing some work")
329 c.Expect("Doing some more work")
330 c.ExpectEOFAndWait()
Bogdan Caprita0a9f9982014-06-11 15:50:08 -0700331}