blob: b80357721d141a4c4883d8ca16dcfb63025a73e1 [file] [log] [blame]
Cosmos Nicolaou4e029972014-06-13 14:53:08 -07001package namespace_test
Jiri Simsa5293dcb2014-05-10 09:56:38 -07002
3import (
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -07004 "runtime"
Jiri Simsa5293dcb2014-05-10 09:56:38 -07005 "runtime/debug"
Matt Rosencrantz50c2bb82014-07-24 09:10:33 -07006 "sync"
Jiri Simsa5293dcb2014-05-10 09:56:38 -07007 "testing"
8 "time"
9
Jiri Simsa519c5072014-09-17 21:37:57 -070010 "veyron.io/veyron/veyron2"
11 "veyron.io/veyron/veyron2/ipc"
12 "veyron.io/veyron/veyron2/naming"
Asim Shankarcc044212014-10-15 23:25:26 -070013 "veyron.io/veyron/veyron2/options"
Jiri Simsa519c5072014-09-17 21:37:57 -070014 "veyron.io/veyron/veyron2/rt"
15 "veyron.io/veyron/veyron2/security"
16 "veyron.io/veyron/veyron2/services/mounttable"
17 "veyron.io/veyron/veyron2/services/mounttable/types"
Benjamin Prosnitzb8178d32014-11-04 10:24:12 -080018 verror "veyron.io/veyron/veyron2/verror2"
Jiri Simsa519c5072014-09-17 21:37:57 -070019 "veyron.io/veyron/veyron2/vlog"
Cosmos Nicolaouf889c732014-10-16 20:46:54 -070020
21 "veyron.io/veyron/veyron/lib/glob"
22 "veyron.io/veyron/veyron/lib/testutil"
23 _ "veyron.io/veyron/veyron/profiles"
24 "veyron.io/veyron/veyron/runtimes/google/naming/namespace"
25 service "veyron.io/veyron/veyron/services/mounttable/lib"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070026)
27
Asim Shankarc920db32014-10-16 19:18:21 -070028func init() { testutil.Init() }
29
Jiri Simsa5293dcb2014-05-10 09:56:38 -070030func boom(t *testing.T, f string, v ...interface{}) {
31 t.Logf(f, v...)
32 t.Fatal(string(debug.Stack()))
33}
34
David Why Use Two When One Will Do Presottocce8f4f2014-09-30 14:50:44 -070035// N squared but who cares, this is a little test.
36// Ignores dups.
37func contains(container, contained []string) bool {
38L:
39 for _, d := range contained {
40 for _, r := range container {
41 if r == d {
42 continue L
43 }
44 }
45 return false
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -070046 }
David Why Use Two When One Will Do Presottocce8f4f2014-09-30 14:50:44 -070047 return true
48}
49
50func compare(t *testing.T, caller, name string, got, want []string) {
51 // Compare ignoring dups.
52 if !contains(got, want) || !contains(want, got) {
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -070053 boom(t, "%s: %q: got %v, want %v", caller, name, got, want)
54 }
55}
56
Matt Rosencrantz137b8d22014-08-18 09:56:15 -070057func doGlob(t *testing.T, r veyron2.Runtime, ns naming.Namespace, pattern string, limit int) []string {
Jiri Simsa5293dcb2014-05-10 09:56:38 -070058 var replies []string
Matt Rosencrantz137b8d22014-08-18 09:56:15 -070059 rc, err := ns.Glob(r.NewContext(), pattern)
Jiri Simsa5293dcb2014-05-10 09:56:38 -070060 if err != nil {
61 boom(t, "Glob(%s): %s", pattern, err)
62 }
63 for s := range rc {
64 replies = append(replies, s.Name)
David Why Use Two When One Will Do Presotto2db32f92014-07-18 12:20:35 -070065 if limit > 0 && len(replies) > limit {
66 boom(t, "Glob returns too many results, perhaps not limiting recursion")
67 }
Jiri Simsa5293dcb2014-05-10 09:56:38 -070068 }
69 return replies
70}
71
Robin Thellend434e39f2014-08-27 14:46:27 -070072type testServer struct {
73 suffix string
74}
Jiri Simsa5293dcb2014-05-10 09:56:38 -070075
Robin Thellend434e39f2014-08-27 14:46:27 -070076func (testServer) KnockKnock(call ipc.ServerCall) string {
Jiri Simsa5293dcb2014-05-10 09:56:38 -070077 return "Who's there?"
78}
79
Robin Thellend434e39f2014-08-27 14:46:27 -070080// Glob applies pattern to the following tree:
81// "" -> {level1} -> {level2}
82// "".Glob("*") returns "level1"
83// "".Glob("...") returns "level1" and "level1/level2"
84// "level1".Glob("*") returns "level2"
85func (t *testServer) Glob(call ipc.ServerCall, pattern string) error {
86 g, err := glob.Parse(pattern)
87 if err != nil {
88 return err
89 }
90 tree := []string{"", "level1", "level2"}
91 for i, leaf := range tree {
92 if leaf == t.suffix {
93 return t.globLoop(call, "", g, tree[i+1:])
94 }
95 }
96 return nil
97}
98
99func (t *testServer) globLoop(call ipc.ServerCall, prefix string, g *glob.Glob, tree []string) error {
100 if g.Len() == 0 {
101 if err := call.Send(types.MountEntry{Name: prefix}); err != nil {
102 return err
103 }
104 }
105 if g.Finished() || len(tree) == 0 {
106 return nil
107 }
Tilak Sharma577ce8d2014-09-22 10:25:00 -0700108 if ok, _, left := g.MatchInitialSegment(tree[0]); ok {
Robin Thellend434e39f2014-08-27 14:46:27 -0700109 if err := t.globLoop(call, naming.Join(prefix, tree[0]), left, tree[1:]); err != nil {
110 return err
111 }
112 }
113 return nil
114}
115
Asim Shankar98d65962014-10-24 16:55:30 -0700116type allowEveryoneAuthorizer struct{}
117
118func (allowEveryoneAuthorizer) Authorize(security.Context) error { return nil }
119
Robin Thellend434e39f2014-08-27 14:46:27 -0700120type dispatcher struct{}
121
Cosmos Nicolaou1ee5e1a2014-11-02 10:20:30 -0800122func (d *dispatcher) Lookup(suffix, method string) (interface{}, security.Authorizer, error) {
Asim Shankar98d65962014-10-24 16:55:30 -0700123 return ipc.ReflectInvoker(&testServer{suffix}), allowEveryoneAuthorizer{}, nil
Robin Thellend434e39f2014-08-27 14:46:27 -0700124}
125
Matt Rosencrantzf5afcaf2014-06-02 11:31:22 -0700126func knockKnock(t *testing.T, runtime veyron2.Runtime, name string) {
127 client := runtime.Client()
128 ctx := runtime.NewContext()
129 call, err := client.StartCall(ctx, name, "KnockKnock", nil)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700130 if err != nil {
131 boom(t, "StartCall failed: %s", err)
132 }
133 var result string
134 if err := call.Finish(&result); err != nil {
135 boom(t, "Finish returned an error: %s", err)
136 }
137 if result != "Who's there?" {
138 boom(t, "Wrong result: %v", result)
139 }
140}
141
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -0700142func testResolveToMountTable(t *testing.T, r veyron2.Runtime, ns naming.Namespace, name string, want ...string) {
143 servers, err := ns.ResolveToMountTable(r.NewContext(), name)
144 if err != nil {
145 boom(t, "Failed to ResolveToMountTable %q: %s", name, err)
146 }
David Why Use Two When One Will Do Presotto59a254c2014-10-30 13:09:29 -0700147 compare(t, "ResolveToMountTable", name, servers, want)
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -0700148}
149
150func testResolve(t *testing.T, r veyron2.Runtime, ns naming.Namespace, name string, want ...string) {
151 servers, err := ns.Resolve(r.NewContext(), name)
152 if err != nil {
153 boom(t, "Failed to Resolve %q: %s", name, err)
154 }
155 compare(t, "Resolve", name, servers, want)
156}
157
158func testUnresolve(t *testing.T, r veyron2.Runtime, ns naming.Namespace, name string, want ...string) {
159 servers, err := ns.Unresolve(r.NewContext(), name)
160 if err != nil {
161 boom(t, "Failed to Resolve %q: %s", name, err)
162 }
163 compare(t, "Unresolve", name, servers, want)
164}
165
166type serverEntry struct {
167 mountPoint string
168 server ipc.Server
169 endpoint naming.Endpoint
170 name string
171}
172
173func runServer(t *testing.T, sr veyron2.Runtime, disp ipc.Dispatcher, mountPoint string) *serverEntry {
174 return run(t, sr, disp, mountPoint, false)
175}
176
177func runMT(t *testing.T, sr veyron2.Runtime, mountPoint string) *serverEntry {
178 mt, err := service.NewMountTable("")
179 if err != nil {
180 boom(t, "NewMountTable returned error: %v", err)
181 }
182 return run(t, sr, mt, mountPoint, true)
183}
184
185func run(t *testing.T, sr veyron2.Runtime, disp ipc.Dispatcher, mountPoint string, mt bool) *serverEntry {
Asim Shankarcc044212014-10-15 23:25:26 -0700186 s, err := sr.NewServer(options.ServesMountTable(mt))
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -0700187 if err != nil {
188 boom(t, "r.NewServer: %s", err)
189 }
190 // Add a mount table server.
191 // Start serving on a loopback address.
Cosmos Nicolaouf8d4c2b2014-10-23 22:36:38 -0700192 ep, err := s.Listen(ipc.ListenSpec{Protocol: "tcp", Address: "127.0.0.1:0"})
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -0700193 if err != nil {
194 boom(t, "Failed to Listen: %s", err)
195 }
196 if err := s.Serve(mountPoint, disp); err != nil {
197 boom(t, "Failed to serve mount table at %s: %s", mountPoint, err)
198 }
199 name := naming.JoinAddressName(ep.String(), "")
200 if !mt {
201 name = name + "//"
202 }
203 return &serverEntry{mountPoint: mountPoint, server: s, endpoint: ep, name: name}
204}
205
206const (
207 mt1MP = "mt1"
208 mt2MP = "mt2"
209 mt3MP = "mt3"
210 mt4MP = "mt4"
211 mt5MP = "mt5"
212 j1MP = "joke1"
213 j2MP = "joke2"
214 j3MP = "joke3"
215
216 ttl = 100 * time.Second
217)
218
219// runMountTables creates a root mountable with some mount tables mounted
220// in it: mt{1,2,3,4,5}
221func runMountTables(t *testing.T, r veyron2.Runtime) (*serverEntry, map[string]*serverEntry) {
222 root := runMT(t, r, "")
223 r.Namespace().SetRoots(root.name)
224 t.Logf("mountTable %q -> %s", root.mountPoint, root.endpoint)
225
226 mps := make(map[string]*serverEntry)
227 for _, mp := range []string{mt1MP, mt2MP, mt3MP, mt4MP, mt5MP} {
228 m := runMT(t, r, mp)
229 t.Logf("mountTable %q -> %s", mp, m.endpoint)
230 mps[mp] = m
231 }
232 return root, mps
233}
234
Robin Thellend434e39f2014-08-27 14:46:27 -0700235// createNamespace creates a hierarchy of mounttables and servers
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -0700236// as follows:
237// /mt1, /mt2, /mt3, /mt4, /mt5, /joke1, /joke2, /joke3.
238// That is, mt1 is a mount table mounted in the root mount table,
239// joke1 is a server mounted in the root mount table.
240func createNamespace(t *testing.T, r veyron2.Runtime) (*serverEntry, map[string]*serverEntry, map[string]*serverEntry, func()) {
241 root, mts := runMountTables(t, r)
242 jokes := make(map[string]*serverEntry)
243 // Let's run some non-mount table services.
244 for _, j := range []string{j1MP, j2MP, j3MP} {
Robin Thellend434e39f2014-08-27 14:46:27 -0700245 disp := &dispatcher{}
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -0700246 jokes[j] = runServer(t, r, disp, j)
247 }
248 return root, mts, jokes, func() {
249 for _, s := range jokes {
250 s.server.Stop()
251 }
252 for _, s := range mts {
253 s.server.Stop()
254 }
255 root.server.Stop()
256 }
257}
258
259// runNestedMountTables creates some nested mount tables in the hierarchy
260// created by createNamespace as follows:
261// /mt4/foo, /mt4/foo/bar and /mt4/baz where foo, bar and baz are mount tables.
262func runNestedMountTables(t *testing.T, r veyron2.Runtime, mts map[string]*serverEntry) {
263 ns, ctx := r.Namespace(), r.NewContext()
264 // Set up some nested mounts and verify resolution.
265 for _, m := range []string{"mt4/foo", "mt4/foo/bar"} {
266 mts[m] = runMT(t, r, m)
267 }
268
269 // Use a global name for a mount, rather than a relative one.
270 // We directly mount baz into the mt4/foo mount table.
271 globalMP := naming.JoinAddressName(mts["mt4/foo"].name, "baz")
272 mts["baz"] = runMT(t, r, "baz")
273 if err := ns.Mount(ctx, globalMP, mts["baz"].name, ttl); err != nil {
274 boom(t, "Failed to Mount %s: %s", globalMP, err)
275 }
276}
277
278// TestNamespaceCommon tests common use of the Namespace library
279// against a root mount table and some mount tables mounted on it.
280func TestNamespaceCommon(t *testing.T) {
281 // We need the default runtime for the server-side mounttable code
282 // which references rt.R() to create new endpoints
283 rt.Init()
284 r, _ := rt.New() // We use a different runtime for the client side.
285 root, mts, jokes, stopper := createNamespace(t, r)
286 defer stopper()
287 ns := r.Namespace()
288
289 // All of the initial mounts are served by the root mounttable
290 // and hence ResolveToMountTable should return the root mountable
291 // as the address portion of the terminal name for those mounttables.
292 testResolveToMountTable(t, r, ns, "", root.name)
293 for _, m := range []string{mt2MP, mt3MP, mt5MP} {
294 rootMT := naming.MakeTerminal(naming.Join(root.name, m))
295 // All of these mount tables are hosted by the root mount table
296 testResolveToMountTable(t, r, ns, m, rootMT)
297 testResolveToMountTable(t, r, ns, "//"+m, rootMT)
298
299 // The server registered for each mount point is a mount table
300 testResolve(t, r, ns, m, mts[m].name)
301
302 // ResolveToMountTable will walk through to the sub MountTables
303 mtbar := naming.Join(m, "bar")
304 subMT := naming.MakeTerminal(naming.Join(mts[m].name, "bar"))
305 testResolveToMountTable(t, r, ns, mtbar, subMT)
306
307 // ResolveToMountTable will not walk through if the name is terminal
308 testResolveToMountTable(t, r, ns, "//"+mtbar, naming.Join(rootMT, "bar"))
309 }
310
311 for _, j := range []string{j1MP, j2MP, j3MP} {
312 testResolve(t, r, ns, j, jokes[j].name)
313 }
314}
315
316// TestNamespaceDetails tests more detailed use of the Namespace library,
317// including the intricacies of // meaning and placement.
318func TestNamespaceDetails(t *testing.T) {
319 sr := rt.Init()
320 r, _ := rt.New() // We use a different runtime for the client side.
321 root, mts, _, stopper := createNamespace(t, sr)
322 defer stopper()
323
324 ns := r.Namespace()
325 ns.SetRoots(root.name)
326
327 // Mount using a relative name starting with //.
328 // This means don't walk out of the namespace's root mount table
329 // even if there is already something mounted at mt2. Thus, the example
330 // below will fail.
331 mt3Server := mts[mt3MP].name
332 mt2a := "//mt2/a"
Benjamin Prosnitzb8178d32014-11-04 10:24:12 -0800333 if err := ns.Mount(r.NewContext(), mt2a, mt3Server, ttl); verror.Is(err, naming.ErrNoSuchName.ID) {
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -0700334 boom(t, "Successfully mounted %s - expected an err %v, not %v", mt2a, naming.ErrNoSuchName, err)
335 }
336
337 // Mount using the relative name not starting with //.
338 // This means walk through mt2 if it already exists and mount within
339 // the lower level mount table, if the name doesn't exist we'll create
340 // a new name for it.
341 mt2a = "mt2/a"
342 if err := ns.Mount(r.NewContext(), mt2a, mt3Server, ttl); err != nil {
343 boom(t, "Failed to Mount %s: %s", mt2a, err)
344 }
345
346 mt2mt := naming.MakeTerminal(naming.Join(mts[mt2MP].name, "a"))
347 // The mt2/a is served by the mt2 mount table
348 testResolveToMountTable(t, r, ns, mt2a, mt2mt)
349 // The server for mt2a is mt3server from the second mount above.
350 testResolve(t, r, ns, mt2a, mt3Server)
351
352 // Using a terminal or non-terminal name makes no difference if the
353 // mount is directed to the root name server (since that's the root
354 // for the namespace for this process) and the name exists within
355 // that mount table. In both cases, the server will be added to the
356 // set of mount table servers for that name.
357 for _, mp := range []struct{ name, server string }{
358 {"mt2", mts[mt4MP].name},
359 {"//mt2", mts[mt5MP].name},
360 } {
David Why Use Two When One Will Do Presottoc28686e2014-11-05 11:19:29 -0800361 if err := ns.Mount(r.NewContext(), mp.name, mp.server, ttl, naming.ServesMountTableOpt(true)); err != nil {
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -0700362 boom(t, "Failed to Mount %s: %s", mp.name, err)
363 }
364 }
365
366 // We now have 3 mount tables prepared to serve mt2/a
367 testResolveToMountTable(t, r, ns, "mt2/a",
368 mts[mt2MP].name+"//a",
369 mts[mt4MP].name+"//a",
370 mts[mt5MP].name+"//a")
371 testResolve(t, r, ns, "mt2", mts[mt2MP].name, mts[mt4MP].name, mts[mt5MP].name)
372}
373
374// TestNestedMounts tests some more deeply nested mounts
375func TestNestedMounts(t *testing.T) {
376 sr := rt.Init()
377 r, _ := rt.New() // We use a different runtime for the client side.
378 root, mts, _, stopper := createNamespace(t, sr)
379 runNestedMountTables(t, sr, mts)
380 defer stopper()
381
382 ns := r.Namespace()
383 ns.SetRoots(root.name)
384
385 // Set up some nested mounts and verify resolution.
386 for _, m := range []string{"mt4/foo", "mt4/foo/bar"} {
387 testResolve(t, r, ns, m, mts[m].name)
388 }
389
390 testResolveToMountTable(t, r, ns, "mt4/foo",
391 mts[mt4MP].name+"//foo")
392 testResolveToMountTable(t, r, ns, "mt4/foo/bar",
393 mts["mt4/foo"].name+"//bar")
394 testResolveToMountTable(t, r, ns, "mt4/foo/baz", mts["mt4/foo"].name+"//baz")
395}
396
397// TestServers tests invoking RPCs on simple servers
398func TestServers(t *testing.T) {
399 sr := rt.Init()
400 r, _ := rt.New() // We use a different runtime for the client side.
401 root, mts, jokes, stopper := createNamespace(t, sr)
402 defer stopper()
403 ns := r.Namespace()
404 ns.SetRoots(root.name)
405
Robin Thellend434e39f2014-08-27 14:46:27 -0700406 // Let's run some non-mount table services
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -0700407 for _, j := range []string{j1MP, j2MP, j3MP} {
408 testResolve(t, r, ns, j, jokes[j].name)
409 knockKnock(t, r, j)
410 globalName := naming.JoinAddressName(mts["mt4"].name, j)
Robin Thellend434e39f2014-08-27 14:46:27 -0700411 disp := &dispatcher{}
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -0700412 gj := "g_" + j
413 jokes[gj] = runServer(t, r, disp, globalName)
414 testResolve(t, r, ns, "mt4/"+j, jokes[gj].name)
415 knockKnock(t, r, "mt4/"+j)
416 testResolveToMountTable(t, r, ns, "mt4/"+j, naming.MakeTerminal(globalName))
417 testResolveToMountTable(t, r, ns, "mt4/"+j+"/garbage", naming.MakeTerminal(globalName+"/garbage"))
418 }
419}
420
421// TestGlob tests some glob patterns.
422func TestGlob(t *testing.T) {
423 sr := rt.Init()
424 r, _ := rt.New() // We use a different runtime for the client side.
425 root, mts, _, stopper := createNamespace(t, sr)
426 runNestedMountTables(t, sr, mts)
427 defer stopper()
428 ns := r.Namespace()
429 ns.SetRoots(root.name)
430
431 tln := []string{"baz", "mt1", "mt2", "mt3", "mt4", "mt5", "joke1", "joke2", "joke3"}
432 barbaz := []string{"mt4/foo/bar", "mt4/foo/baz"}
Robin Thellend434e39f2014-08-27 14:46:27 -0700433 level12 := []string{"joke1/level1", "joke1/level1/level2", "joke2/level1", "joke2/level1/level2", "joke3/level1", "joke3/level1/level2"}
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -0700434 foo := append([]string{"mt4/foo"}, barbaz...)
Robin Thellend434e39f2014-08-27 14:46:27 -0700435 foo = append(foo, level12...)
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -0700436 // Try various globs.
437 globTests := []struct {
438 pattern string
439 expected []string
440 }{
441 {"*", tln},
David Why Use Two When One Will Do Presottocce8f4f2014-09-30 14:50:44 -0700442 {"x", []string{}},
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -0700443 {"m*", []string{"mt1", "mt2", "mt3", "mt4", "mt5"}},
444 {"mt[2,3]", []string{"mt2", "mt3"}},
445 {"*z", []string{"baz"}},
Robin Thellend434e39f2014-08-27 14:46:27 -0700446 {"joke1/*", []string{"joke1/level1"}},
447 {"j?ke1/level1/*", []string{"joke1/level1/level2"}},
448 {"joke1/level1/*", []string{"joke1/level1/level2"}},
449 {"joke1/level1/level2/...", []string{"joke1/level1/level2"}},
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -0700450 {"...", append(append(tln, foo...), "")},
451 {"*/...", append(tln, foo...)},
452 {"*/foo/*", barbaz},
453 {"*/*/*z", []string{"mt4/foo/baz"}},
454 {"*/f??/*z", []string{"mt4/foo/baz"}},
Robin Thellend434e39f2014-08-27 14:46:27 -0700455 {"mt4/foo/baz", []string{"mt4/foo/baz"}},
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -0700456 }
457 for _, test := range globTests {
David Why Use Two When One Will Do Presotto2db32f92014-07-18 12:20:35 -0700458 out := doGlob(t, r, ns, test.pattern, 0)
Robin Thellend434e39f2014-08-27 14:46:27 -0700459 compare(t, "Glob", test.pattern, out, test.expected)
Robin Thellend5f838572014-07-10 13:28:56 -0700460 // Do the same with a full rooted name.
David Why Use Two When One Will Do Presotto2db32f92014-07-18 12:20:35 -0700461 out = doGlob(t, r, ns, naming.JoinAddressName(root.name, test.pattern), 0)
Robin Thellend5f838572014-07-10 13:28:56 -0700462 var expectedWithRoot []string
463 for _, s := range test.expected {
464 expectedWithRoot = append(expectedWithRoot, naming.JoinAddressName(root.name, s))
465 }
Robin Thellend434e39f2014-08-27 14:46:27 -0700466 compare(t, "Glob", test.pattern, out, expectedWithRoot)
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -0700467 }
468}
469
Matt Rosencrantz50c2bb82014-07-24 09:10:33 -0700470type GlobbableServer struct {
471 callCount int
472 mu sync.Mutex
473}
474
Adam Sadovsky41cb9142014-07-31 10:42:54 -0700475func (g *GlobbableServer) Glob(ipc.ServerContext, string, mounttable.GlobbableServiceGlobStream) error {
Matt Rosencrantz50c2bb82014-07-24 09:10:33 -0700476 g.mu.Lock()
477 defer g.mu.Unlock()
478 g.callCount++
479 return nil
480}
481
482func (g *GlobbableServer) GetAndResetCount() int {
483 g.mu.Lock()
484 defer g.mu.Unlock()
485 cnt := g.callCount
486 g.callCount = 0
487
488 return cnt
489}
490
491// TestGlobEarlyStop tests that Glob doesn't query terminal servers with finished patterns.
492func TestGlobEarlyStop(t *testing.T) {
493 sr := rt.Init()
494 r, _ := rt.New() // We use a different runtime for the client side.
495 root, mts, _, stopper := createNamespace(t, sr)
496 runNestedMountTables(t, sr, mts)
497 defer stopper()
498
499 globServer := &GlobbableServer{}
500 name := naming.JoinAddressName(mts["mt4/foo/bar"].name, "glob")
Cosmos Nicolaou8bfacf22014-08-19 11:19:36 -0700501 runningGlobServer := runServer(t, r, ipc.LeafDispatcher(mounttable.NewServerGlobbable(globServer), nil), name)
Matt Rosencrantz50c2bb82014-07-24 09:10:33 -0700502 defer runningGlobServer.server.Stop()
503
504 ns := r.Namespace()
505 ns.SetRoots(root.name)
506
507 tests := []struct {
508 pattern string
509 expectedCalls int
David Why Use Two When One Will Do Presottoc28686e2014-11-05 11:19:29 -0800510 expected []string
Matt Rosencrantz50c2bb82014-07-24 09:10:33 -0700511 }{
David Why Use Two When One Will Do Presottoc28686e2014-11-05 11:19:29 -0800512 {"mt4/foo/bar/glob", 0, []string{"mt4/foo/bar/glob"}},
513 {"mt4/foo/bar/glob/...", 1, []string{"mt4/foo/bar/glob"}},
514 {"mt4/foo/bar/glob/*", 1, nil},
515 {"mt4/foo/bar/***", 0, []string{"mt4/foo/bar", "mt4/foo/bar/glob"}},
516 {"mt4/foo/bar/...", 1, []string{"mt4/foo/bar", "mt4/foo/bar/glob"}},
517 {"mt4/foo/bar/*", 0, []string{"mt4/foo/bar/glob"}},
518 {"mt4/***/bar/***", 0, []string{"mt4/foo/bar", "mt4/foo/bar/glob"}},
519 {"mt4/*/bar/***", 0, []string{"mt4/foo/bar", "mt4/foo/bar/glob"}},
Matt Rosencrantz50c2bb82014-07-24 09:10:33 -0700520 }
David Why Use Two When One Will Do Presottoc28686e2014-11-05 11:19:29 -0800521 // Test allowing the tests to descend into leaves.
Matt Rosencrantz50c2bb82014-07-24 09:10:33 -0700522 for _, test := range tests {
523 out := doGlob(t, r, ns, test.pattern, 0)
David Why Use Two When One Will Do Presottoc28686e2014-11-05 11:19:29 -0800524 compare(t, "Glob", test.pattern, out, test.expected)
Matt Rosencrantz50c2bb82014-07-24 09:10:33 -0700525 if calls := globServer.GetAndResetCount(); calls != test.expectedCalls {
526 boom(t, "Wrong number of Glob calls to terminal server got: %d want: %d.", calls, test.expectedCalls)
527 }
528 }
529}
530
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -0700531func TestCycles(t *testing.T) {
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -0700532 sr := rt.Init()
533 r, _ := rt.New() // We use a different runtime for the client side.
Bogdan Caprita4258d882014-07-02 09:15:22 -0700534 defer r.Cleanup()
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -0700535
536 root, _, _, stopper := createNamespace(t, sr)
537 defer stopper()
538 ns := r.Namespace()
539 ns.SetRoots(root.name)
540
541 c1 := runMT(t, r, "c1")
542 c2 := runMT(t, r, "c2")
543 c3 := runMT(t, r, "c3")
544 defer c1.server.Stop()
545 defer c2.server.Stop()
546 defer c3.server.Stop()
547
548 m := "c1/c2"
David Why Use Two When One Will Do Presottoc28686e2014-11-05 11:19:29 -0800549 if err := ns.Mount(r.NewContext(), m, c1.name, ttl, naming.ServesMountTableOpt(true)); err != nil {
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -0700550 boom(t, "Failed to Mount %s: %s", "c1/c2", err)
551 }
552
553 m = "c1/c2/c3"
David Why Use Two When One Will Do Presottoc28686e2014-11-05 11:19:29 -0800554 if err := ns.Mount(r.NewContext(), m, c3.name, ttl, naming.ServesMountTableOpt(true)); err != nil {
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -0700555 boom(t, "Failed to Mount %s: %s", m, err)
556 }
557
558 m = "c1/c3/c4"
David Why Use Two When One Will Do Presottoc28686e2014-11-05 11:19:29 -0800559 if err := ns.Mount(r.NewContext(), m, c1.name, ttl, naming.ServesMountTableOpt(true)); err != nil {
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -0700560 boom(t, "Failed to Mount %s: %s", m, err)
561 }
562
563 testResolve(t, r, ns, "c1", c1.name)
564 testResolve(t, r, ns, "c1/c2", c1.name)
565 testResolve(t, r, ns, "c1/c3", c3.name)
566 testResolve(t, r, ns, "c1/c3/c4", c1.name)
567 testResolve(t, r, ns, "c1/c3/c4/c3/c4", c1.name)
568 cycle := "c3/c4"
569 for i := 0; i < 40; i++ {
570 cycle += "/c3/c4"
571 }
Benjamin Prosnitzb8178d32014-11-04 10:24:12 -0800572 if _, err := ns.Resolve(r.NewContext(), "c1/"+cycle); !verror.Is(err, naming.ErrResolutionDepthExceeded.ID) {
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -0700573 boom(t, "Failed to detect cycle")
574 }
575
David Why Use Two When One Will Do Presotto2db32f92014-07-18 12:20:35 -0700576 // Perform the glob with a response length limit.
577 doGlob(t, r, ns, "c1/...", 1000)
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -0700578}
579
580func TestUnresolve(t *testing.T) {
581 // TODO(cnicolaou): move unresolve tests into this test, right now,
582 // that's annoying because the stub compiler has some blocking bugs and the
583 // Unresolve functionality is partially implemented in the stubs.
584 t.Skip()
585 sr := rt.Init()
586 r, _ := rt.New() // We use a different runtime for the client side.
Bogdan Caprita4258d882014-07-02 09:15:22 -0700587 defer r.Cleanup()
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -0700588 root, mts, jokes, stopper := createNamespace(t, sr)
589 runNestedMountTables(t, sr, mts)
590 defer stopper()
591 ns := r.Namespace()
592 ns.SetRoots(root.name)
593
David Why Use Two When One Will Do Presotto2db32f92014-07-18 12:20:35 -0700594 vlog.Infof("Glob: %v", doGlob(t, r, ns, "*", 0))
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -0700595 testResolve(t, r, ns, "joke1", jokes["joke1"].name)
596 testUnresolve(t, r, ns, "joke1", "")
597}
598
599// TestGoroutineLeaks tests for leaking goroutines - we have many:-(
600func TestGoroutineLeaks(t *testing.T) {
601 t.Skip()
602 sr := rt.Init()
603 r, _ := rt.New() // We use a different runtime for the client side.
Bogdan Caprita4258d882014-07-02 09:15:22 -0700604 defer r.Cleanup()
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -0700605 _, _, _, stopper := createNamespace(t, sr)
606 defer func() {
607 vlog.Infof("%d goroutines:", runtime.NumGoroutine())
608 }()
609 defer stopper()
610 defer func() {
611 vlog.Infof("%d goroutines:", runtime.NumGoroutine())
612 }()
613 //panic("this will show up lots of goroutine+channel leaks!!!!")
614}
615
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700616func TestBadRoots(t *testing.T) {
617 r, _ := rt.New()
Bogdan Caprita4258d882014-07-02 09:15:22 -0700618 defer r.Cleanup()
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700619 if _, err := namespace.New(r); err != nil {
620 t.Errorf("namespace.New should not have failed with no roots")
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700621 }
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700622 if _, err := namespace.New(r, "not a rooted name"); err == nil {
623 t.Errorf("namespace.New should have failed with an unrooted name")
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700624 }
625}