veyron/runtimes/google/ipc: new Serve method to replace Register+Publish.

Change-Id: Icc69645e753a3e84c5d87a3e9d2d4abd36275fff
diff --git a/runtimes/google/ipc/benchmarks/server.go b/runtimes/google/ipc/benchmarks/server.go
index d585d72..bd5eb0c 100644
--- a/runtimes/google/ipc/benchmarks/server.go
+++ b/runtimes/google/ipc/benchmarks/server.go
@@ -42,13 +42,13 @@
 	if err != nil {
 		vlog.Fatalf("NewServer failed: %v", err)
 	}
-	if err := server.Register("", ipc.SoloDispatcher(NewServerBenchmark(&impl{}), sflag.NewAuthorizerOrDie())); err != nil {
-		vlog.Fatalf("Register failed: %v", err)
-	}
 	ep, err := server.Listen(protocol, address)
 	if err != nil {
 		vlog.Fatalf("Listen failed: %v", err)
 	}
+	if err := server.Serve("", ipc.SoloDispatcher(NewServerBenchmark(&impl{}), sflag.NewAuthorizerOrDie())); err != nil {
+		vlog.Fatalf("Serve failed: %v", err)
+	}
 	return naming.JoinAddressName(ep.String(), ""), func() {
 		if err := server.Stop(); err != nil {
 			vlog.Fatalf("Stop() failed: %v", err)
diff --git a/runtimes/google/ipc/flow_test.go b/runtimes/google/ipc/flow_test.go
index e856eb3..7009258 100644
--- a/runtimes/google/ipc/flow_test.go
+++ b/runtimes/google/ipc/flow_test.go
@@ -105,22 +105,16 @@
 		err    error
 	}
 	tests := []testcase{
-		{"closure", "A", nil, nil, nil},
-		{"closure/foo", "B", nil, nil, errors.New("foo")},
-
-		{"echo", "A", v{""}, v{`method:"A",suffix:"",arg:""`}, nil},
-		{"echo", "B", v{"foo"}, v{`method:"B",suffix:"",arg:"foo"`}, nil},
-		{"echo/abc", "C", v{""}, v{`method:"C",suffix:"abc",arg:""`}, nil},
-		{"echo/abc", "D", v{"foo"}, v{`method:"D",suffix:"abc",arg:"foo"`}, nil},
+		{"echo", "A", v{""}, v{`method:"A",suffix:"echo",arg:""`}, nil},
+		{"echo", "B", v{"foo"}, v{`method:"B",suffix:"echo",arg:"foo"`}, nil},
+		{"echo/abc", "C", v{""}, v{`method:"C",suffix:"echo/abc",arg:""`}, nil},
+		{"echo/abc", "D", v{"foo"}, v{`method:"D",suffix:"echo/abc",arg:"foo"`}, nil},
 	}
 	name := func(t testcase) string {
 		return fmt.Sprintf("%s.%s%v", t.suffix, t.method, t.args)
 	}
 
-	disptrie := newDisptrie()
-	disptrie.Register("echo", testDisp{newEchoInvoker})
-	disptrie.Register("closure", testDisp{newClosureInvoker})
-	ipcServer := &server{disptrie: disptrie}
+	ipcServer := &server{disp: testDisp{newEchoInvoker}}
 	for _, test := range tests {
 		clientFlow, serverFlow := newTestFlows()
 		client := newFlowClient(clientFlow)
diff --git a/runtimes/google/ipc/full_test.go b/runtimes/google/ipc/full_test.go
index 3c68e79..86b5a18 100644
--- a/runtimes/google/ipc/full_test.go
+++ b/runtimes/google/ipc/full_test.go
@@ -202,31 +202,33 @@
 	return nil, nil
 }
 
-func (ns *namespace) SetRoots([]string) error {
+func (ns *namespace) SetRoots(...string) error {
 	panic("SetRoots not implemented")
 	return nil
 }
 
-func startServer(t *testing.T, serverID security.PrivateID, sm stream.Manager, ns naming.Namespace, ts interface{}) ipc.Server {
+func (ns *namespace) Roots() []string {
+	panic("Roots not implemented")
+	return nil
+}
+
+func startServer(t *testing.T, serverID security.PrivateID, sm stream.Manager, ns naming.Namespace, ts interface{}) (naming.Endpoint, ipc.Server) {
 	vlog.VI(1).Info("InternalNewServer")
 	server, err := InternalNewServer(InternalNewContext(), sm, ns, listenerID(serverID))
 	if err != nil {
 		t.Errorf("InternalNewServer failed: %v", err)
 	}
-	vlog.VI(1).Info("server.Register")
-	disp := testServerDisp{ts}
-	if err := server.Register("server", disp); err != nil {
-		t.Errorf("server.Register failed: %v", err)
-	}
 	vlog.VI(1).Info("server.Listen")
-	if _, err := server.Listen("tcp", "localhost:0"); err != nil {
+	ep, err := server.Listen("tcp", "localhost:0")
+	if err != nil {
 		t.Errorf("server.Listen failed: %v", err)
 	}
-	vlog.VI(1).Info("server.Publish")
-	if err := server.Publish("mountpoint"); err != nil {
+	vlog.VI(1).Info("server.Serve")
+	disp := testServerDisp{ts}
+	if err := server.Serve("mountpoint/server", disp); err != nil {
 		t.Errorf("server.Publish failed: %v", err)
 	}
-	return server
+	return ep, server
 }
 
 func verifyMount(t *testing.T, ns naming.Namespace, name string) {
@@ -243,28 +245,35 @@
 
 func stopServer(t *testing.T, server ipc.Server, ns naming.Namespace) {
 	vlog.VI(1).Info("server.Stop")
-	verifyMount(t, ns, "mountpoint/server")
+	n1 := "mountpoint/server"
+	n2 := "should_appear_in_mt/server"
+	verifyMount(t, ns, n1)
 
-	// Check that we can still publish.
-	server.Publish("should_appear_in_mt")
-	verifyMount(t, ns, "should_appear_in_mt/server")
+	// publish a second name
+	if err := server.Serve(n2, nil); err != nil {
+		t.Errorf("server.Serve failed: %v", err)
+	}
+	verifyMount(t, ns, n2)
 
 	if err := server.Stop(); err != nil {
 		t.Errorf("server.Stop failed: %v", err)
 	}
-	// Check that we can no longer publish after Stop.
-	server.Publish("should_not_appear_in_mt")
-	verifyMountMissing(t, ns, "should_not_appear_in_mt/server")
 
-	verifyMountMissing(t, ns, "mountpoint/server")
-	verifyMountMissing(t, ns, "should_appear_in_mt/server")
-	verifyMountMissing(t, ns, "should_not_appear_in_mt/server")
+	verifyMountMissing(t, ns, n1)
+	verifyMountMissing(t, ns, n2)
+
+	// Check that we can no longer serve after Stop.
+	err := server.Serve("name doesn't matter", nil)
+	if err == nil || err.Error() != "ipc: server is stopped" {
+		t.Errorf("either no error, or a wrong error was returned: %v", err)
+	}
 	vlog.VI(1).Info("server.Stop DONE")
 }
 
 type bundle struct {
 	client ipc.Client
 	server ipc.Server
+	ep     naming.Endpoint
 	ns     naming.Namespace
 	sm     stream.Manager
 }
@@ -277,7 +286,7 @@
 func createBundle(t *testing.T, clientID, serverID security.PrivateID, ts interface{}) (b bundle) {
 	b.sm = imanager.InternalNew(naming.FixedRoutingID(0x555555555))
 	b.ns = newNamespace()
-	b.server = startServer(t, serverID, b.sm, b.ns, ts)
+	b.ep, b.server = startServer(t, serverID, b.sm, b.ns, ts)
 	var err error
 	b.client, err = InternalNewClient(b.sm, b.ns, veyron2.LocalID(clientID))
 	if err != nil {
@@ -313,6 +322,53 @@
 	return err == nil || strings.Index(err.Error(), pattern) >= 0
 }
 
+func TestMultipleCallsToServe(t *testing.T) {
+	sm := imanager.InternalNew(naming.FixedRoutingID(0x555555555))
+	ns := newNamespace()
+	server, err := InternalNewServer(InternalNewContext(), sm, ns, listenerID(serverID))
+	if err != nil {
+		t.Errorf("InternalNewServer failed: %v", err)
+	}
+	_, err = server.Listen("tcp", "localhost:0")
+	if err != nil {
+		t.Errorf("server.Listen failed: %v", err)
+	}
+
+	disp := &testServerDisp{&testServer{}}
+	if err := server.Serve("mountpoint/server", disp); err != nil {
+		t.Errorf("server.Publish failed: %v", err)
+	}
+
+	n1 := "mountpoint/server"
+	n2 := "should_appear_in_mt/server"
+	n3 := "should_appear_in_mt/server"
+	n4 := "should_not_appear_in_mt/server"
+
+	verifyMount(t, ns, n1)
+
+	if err := server.Serve(n2, disp); err != nil {
+		t.Errorf("server.Serve failed: %v", err)
+	}
+	if err := server.Serve(n3, nil); err != nil {
+		t.Errorf("server.Serve failed: %v", err)
+	}
+	verifyMount(t, ns, n2)
+	verifyMount(t, ns, n3)
+
+	if err := server.Serve(n4, &testServerDisp{&testServer{}}); err == nil {
+		t.Errorf("server.Serve should have failed")
+	}
+	verifyMountMissing(t, ns, n4)
+
+	if err := server.Stop(); err != nil {
+		t.Errorf("server.Stop failed: %v", err)
+	}
+
+	verifyMountMissing(t, ns, n1)
+	verifyMountMissing(t, ns, n2)
+	verifyMountMissing(t, ns, n3)
+}
+
 func TestStartCall(t *testing.T) {
 	authorizeErr := "not authorized because"
 	nameErr := "does not match the provided pattern"
@@ -355,7 +411,7 @@
 	ns := newNamespace()
 	for _, test := range tests {
 		name := fmt.Sprintf("(clientID:%q serverID:%q)", test.clientID, test.serverID)
-		server := startServer(t, test.serverID, mgr, ns, &testServer{})
+		_, server := startServer(t, test.serverID, mgr, ns, &testServer{})
 		client, err := InternalNewClient(mgr, ns, veyron2.LocalID(test.clientID))
 		if err != nil {
 			t.Errorf("%s: Client creation failed: %v", name, err)
@@ -732,15 +788,16 @@
 	publisher.AddServer("/@2@tcp@localhost:10000@@1000000@2000000@@")
 	publisher.AddServer("/@2@tcp@localhost:10001@@2000000@3000000@@")
 
-	_, err := b.client.StartCall(&fakeContext{}, "incompatible/server/suffix", "Echo", []interface{}{"foo"})
+	_, err := b.client.StartCall(&fakeContext{}, "incompatible/suffix", "Echo", []interface{}{"foo"})
 	if !strings.Contains(err.Error(), version.NoCompatibleVersionErr.Error()) {
 		t.Errorf("Expected error %v, found: %v", version.NoCompatibleVersionErr, err)
 	}
 
 	// Now add a server with a compatible endpoint and try again.
-	b.server.Publish("incompatible")
+	publisher.AddServer("/" + b.ep.String())
+	publisher.AddName("incompatible")
 
-	call, err := b.client.StartCall(&fakeContext{}, "incompatible/server/suffix", "Echo", []interface{}{"foo"})
+	call, err := b.client.StartCall(&fakeContext{}, "incompatible/suffix", "Echo", []interface{}{"foo"})
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -785,7 +842,7 @@
 			server.Stop()
 			continue
 		}
-		if err := server.Publish("mountpoint"); err != nil {
+		if err := server.Serve("mountpoint", &testServerDisp{}); err != nil {
 			t.Errorf("server.Publish failed: %v", err)
 			server.Stop()
 			continue
@@ -835,7 +892,7 @@
 	if err != nil {
 		t.Fatalf("inaming.NewEndpoint(%q): %v", addr, err)
 	}
-	serverName := naming.JoinAddressName(ep.String(), "server/suffix")
+	serverName := naming.JoinAddressName(ep.String(), "suffix")
 	makeCall := func() (string, error) {
 		call, err := b.client.StartCall(&fakeContext{}, serverName, "Echo", []interface{}{"bratman"})
 		if err != nil {
@@ -913,9 +970,6 @@
 		t.Fatal(err)
 	}
 	defer server.Stop()
-	if err := server.Register("server", testServerDisp{&testServer{}}); err != nil {
-		t.Fatal(err)
-	}
 
 	name := "mountpoint/server/suffix"
 	makeCall := func() (string, error) {
@@ -937,7 +991,7 @@
 	if _, err := server.Listen(inaming.Network, "proxy"); err != nil {
 		t.Fatal(err)
 	}
-	if err := server.Publish("mountpoint"); err != nil {
+	if err := server.Serve("mountpoint/server", testServerDisp{&testServer{}}); err != nil {
 		t.Fatal(err)
 	}
 	verifyMount(t, ns, name)
@@ -998,7 +1052,7 @@
 		vlog.Fatalf("InternalNewServer failed: %v", err)
 	}
 	disp := testServerDisp{new(testServer)}
-	if err := server.Register("server", disp); err != nil {
+	if err := server.Serve("server", disp); err != nil {
 		vlog.Fatalf("server.Register failed: %v", err)
 	}
 	ep, err := server.Listen("tcp", argv[0])
diff --git a/runtimes/google/ipc/server.go b/runtimes/google/ipc/server.go
index fb4406d..f162fdf 100644
--- a/runtimes/google/ipc/server.go
+++ b/runtimes/google/ipc/server.go
@@ -36,10 +36,10 @@
 	sync.Mutex
 	ctx              context.T                // context used by the server to make internal RPCs.
 	streamMgr        stream.Manager           // stream manager to listen for new flows.
-	disptrie         *disptrie                // dispatch trie for method dispatching.
 	publisher        publisher.Publisher      // publisher to publish mounttable mounts.
 	listenerOpts     []stream.ListenerOpt     // listener opts passed to Listen.
 	listeners        map[stream.Listener]bool // listeners created by Listen.
+	disp             ipc.Dispatcher           // dispatcher to serve RPCs
 	active           sync.WaitGroup           // active goroutines we've spawned.
 	stopped          bool                     // whether the server has been stopped.
 	stoppedChan      chan struct{}            // closed when the server has been stopped.
@@ -53,7 +53,6 @@
 	s := &server{
 		ctx:         ctx,
 		streamMgr:   streamMgr,
-		disptrie:    newDisptrie(),
 		publisher:   publisher.New(ctx, ns, publishPeriod),
 		listeners:   make(map[stream.Listener]bool),
 		stoppedChan: make(chan struct{}),
@@ -73,15 +72,6 @@
 	return s, nil
 }
 
-func (s *server) Register(prefix string, disp ipc.Dispatcher) error {
-	s.Lock()
-	defer s.Unlock()
-	if s.stopped {
-		return errServerStopped
-	}
-	return s.disptrie.Register(prefix, disp)
-}
-
 func (s *server) Published() ([]string, error) {
 	s.Lock()
 	defer s.Unlock()
@@ -246,8 +236,21 @@
 	}
 }
 
-func (s *server) Publish(name string) error {
-	s.publisher.AddName(name)
+func (s *server) Serve(name string, disp ipc.Dispatcher) error {
+	s.Lock()
+	defer s.Unlock()
+	if s.stopped {
+		return errServerStopped
+	}
+	if s.disp != nil && disp != nil && s.disp != disp {
+		return fmt.Errorf("attempt to change dispatcher")
+	}
+	if disp != nil {
+		s.disp = disp
+	}
+	if len(name) > 0 {
+		s.publisher.AddName(name)
+	}
 	return nil
 }
 
@@ -297,39 +300,37 @@
 
 	// Wait for the publisher and active listener + flows to finish.
 	s.active.Wait()
-
-	// Once all outstanding requests are done, we can clean up the rest of
-	// the state.
-	s.disptrie.Stop()
-
+	s.Lock()
+	s.disp = nil
+	s.Unlock()
 	return firstErr
 }
 
 // flowServer implements the RPC server-side protocol for a single RPC, over a
 // flow that's already connected to the client.
 type flowServer struct {
-	disptrie *disptrie    // dispatch trie
-	server   ipc.Server   // ipc.Server that this flow server belongs to
-	dec      *vom.Decoder // to decode requests and args from the client
-	enc      *vom.Encoder // to encode responses and results to the client
-	flow     stream.Flow  // underlying flow
+	disp   ipc.Dispatcher
+	server ipc.Server   // ipc.Server that this flow server belongs to
+	dec    *vom.Decoder // to decode requests and args from the client
+	enc    *vom.Encoder // to encode responses and results to the client
+	flow   stream.Flow  // underlying flow
 	// Fields filled in during the server invocation.
 
 	// authorizedRemoteID is the PublicID obtained after authorizing the remoteID
 	// of the underlying flow for the current request context.
-	authorizedRemoteID   security.PublicID
-	blessing             security.PublicID
-	method, name, suffix string
-	label                security.Label
-	discharges           security.CaveatDischargeMap
-	deadline             time.Time
-	endStreamArgs        bool // are the stream args at EOF?
+	authorizedRemoteID security.PublicID
+	blessing           security.PublicID
+	method, suffix     string
+	label              security.Label
+	discharges         security.CaveatDischargeMap
+	deadline           time.Time
+	endStreamArgs      bool // are the stream args at EOF?
 }
 
 func newFlowServer(flow stream.Flow, server *server) *flowServer {
 	return &flowServer{
-		server:   server,
-		disptrie: server.disptrie,
+		server: server,
+		disp:   server.disp,
 		// TODO(toddw): Support different codecs
 		dec:  vom.NewDecoder(flow),
 		enc:  vom.NewEncoder(flow),
@@ -449,10 +450,10 @@
 		// should servers be able to assume that a blessing is something that does not
 		// have the authorizations that the server's own identity has?
 	}
+
 	// Lookup the invoker.
-	invoker, auth, name, suffix, verr := fs.lookup(req.Suffix)
-	fs.name = name
-	fs.suffix = suffix
+	invoker, auth, suffix, verr := fs.lookup(req.Suffix)
+	fs.suffix = suffix // with leading /'s stripped
 	if verr != nil {
 		return nil, verr
 	}
@@ -479,7 +480,6 @@
 				LocalID:    fs.flow.LocalID(),
 				RemoteID:   fs.flow.RemoteID(),
 				Method:     fs.method,
-				Name:       fs.name,
 				Suffix:     fs.suffix,
 				Discharges: fs.discharges,
 				Label:      fs.label})); err != nil {
@@ -499,20 +499,18 @@
 // name.  The name is stripped of any leading slashes, and the invoker is looked
 // up in the server's dispatcher.  The (stripped) name and dispatch suffix are
 // also returned.
-func (fs *flowServer) lookup(name string) (ipc.Invoker, security.Authorizer, string, string, verror.E) {
+func (fs *flowServer) lookup(name string) (ipc.Invoker, security.Authorizer, string, verror.E) {
 	name = strings.TrimLeft(name, "/")
-	disps, suffix := fs.disptrie.Lookup(name)
-	for _, disp := range disps {
-		invoker, auth, err := disp.Lookup(suffix)
+	if fs.disp != nil {
+		invoker, auth, err := fs.disp.Lookup(name)
 		switch {
 		case err != nil:
-			return nil, nil, "", "", verror.Convert(err)
+			return nil, nil, "", verror.Convert(err)
 		case invoker != nil:
-			return invoker, auth, name, suffix, nil
+			return invoker, auth, name, nil
 		}
-		// The dispatcher doesn't handle this suffix, try the next one.
 	}
-	return nil, nil, "", "", verror.NotFoundf(fmt.Sprintf("ipc: dispatcher not found for %q", name))
+	return nil, nil, "", verror.NotFoundf(fmt.Sprintf("ipc: dispatcher not found for %q", name))
 }
 
 func (fs *flowServer) authorize(auth security.Authorizer) error {
@@ -561,18 +559,23 @@
 
 // Implementations of ipc.ServerContext methods.
 
-func (fs *flowServer) Server() ipc.Server                            { return fs.server }
-func (fs *flowServer) Method() string                                { return fs.method }
-func (fs *flowServer) Name() string                                  { return fs.name }
-func (fs *flowServer) Suffix() string                                { return fs.suffix }
-func (fs *flowServer) Label() security.Label                         { return fs.label }
 func (fs *flowServer) CaveatDischarges() security.CaveatDischargeMap { return fs.discharges }
-func (fs *flowServer) LocalID() security.PublicID                    { return fs.flow.LocalID() }
-func (fs *flowServer) RemoteID() security.PublicID                   { return fs.authorizedRemoteID }
-func (fs *flowServer) Deadline() time.Time                           { return fs.deadline }
-func (fs *flowServer) Blessing() security.PublicID                   { return fs.blessing }
-func (fs *flowServer) LocalEndpoint() naming.Endpoint                { return fs.flow.LocalEndpoint() }
-func (fs *flowServer) RemoteEndpoint() naming.Endpoint               { return fs.flow.RemoteEndpoint() }
+
+func (fs *flowServer) Server() ipc.Server { return fs.server }
+func (fs *flowServer) Method() string     { return fs.method }
+
+// TODO(cnicolaou): remove Name from ipc.ServerContext and all of
+// its implementations
+func (fs *flowServer) Name() string          { return fs.suffix }
+func (fs *flowServer) Suffix() string        { return fs.suffix }
+func (fs *flowServer) Label() security.Label { return fs.label }
+
+func (fs *flowServer) LocalID() security.PublicID      { return fs.flow.LocalID() }
+func (fs *flowServer) RemoteID() security.PublicID     { return fs.authorizedRemoteID }
+func (fs *flowServer) Deadline() time.Time             { return fs.deadline }
+func (fs *flowServer) Blessing() security.PublicID     { return fs.blessing }
+func (fs *flowServer) LocalEndpoint() naming.Endpoint  { return fs.flow.LocalEndpoint() }
+func (fs *flowServer) RemoteEndpoint() naming.Endpoint { return fs.flow.RemoteEndpoint() }
 
 func (fs *flowServer) IsClosed() bool {
 	return fs.flow.IsClosed()
diff --git a/runtimes/google/naming/namespace/all_test.go b/runtimes/google/naming/namespace/all_test.go
index a628069..4db264c 100644
--- a/runtimes/google/naming/namespace/all_test.go
+++ b/runtimes/google/naming/namespace/all_test.go
@@ -1,7 +1,10 @@
 package namespace_test
 
 import (
+	"reflect"
+	"runtime"
 	"runtime/debug"
+	"sort"
 	"testing"
 	"time"
 
@@ -22,9 +25,19 @@
 	t.Fatal(string(debug.Stack()))
 }
 
+func compare(t *testing.T, caller, name string, got, want []string) {
+	if len(got) != len(want) {
+		boom(t, "%s: %q returned wrong # servers: got %v, want %v, servers: got %v, want %v", caller, name, len(got), len(want), got, want)
+	}
+	sort.Strings(got)
+	sort.Strings(want)
+	if !reflect.DeepEqual(got, want) {
+		boom(t, "%s: %q: got %v, want %v", caller, name, got, want)
+	}
+}
+
 func doGlob(t *testing.T, ctx context.T, ns naming.Namespace, pattern string) []string {
 	var replies []string
-
 	rc, err := ns.Glob(ctx, pattern)
 	if err != nil {
 		boom(t, "Glob(%s): %s", pattern, err)
@@ -35,27 +48,6 @@
 	return replies
 }
 
-func checkMatch(t *testing.T, pattern string, expected []string, got []string) {
-L:
-	for _, e := range expected {
-		for _, g := range got {
-			if g == e {
-				continue L
-			}
-		}
-		boom(t, "Glob %s expected %v got %v", pattern, expected, got)
-	}
-L2:
-	for _, g := range got {
-		for _, e := range expected {
-			if g == e {
-				continue L2
-			}
-		}
-		boom(t, "Glob %s expected %v got %v", pattern, expected, got)
-	}
-}
-
 type testServer struct{}
 
 func (*testServer) KnockKnock(call ipc.ServerCall) string {
@@ -78,8 +70,423 @@
 	}
 }
 
+func testResolveToMountTable(t *testing.T, r veyron2.Runtime, ns naming.Namespace, name string, want ...string) {
+	servers, err := ns.ResolveToMountTable(r.NewContext(), name)
+	if err != nil {
+		boom(t, "Failed to ResolveToMountTable %q: %s", name, err)
+	}
+	compare(t, "ResolveToMoutTable", name, servers, want)
+}
+
+func testResolve(t *testing.T, r veyron2.Runtime, ns naming.Namespace, name string, want ...string) {
+	servers, err := ns.Resolve(r.NewContext(), name)
+	if err != nil {
+		boom(t, "Failed to Resolve %q: %s", name, err)
+	}
+	compare(t, "Resolve", name, servers, want)
+}
+
+func testUnresolve(t *testing.T, r veyron2.Runtime, ns naming.Namespace, name string, want ...string) {
+	servers, err := ns.Unresolve(r.NewContext(), name)
+	if err != nil {
+		boom(t, "Failed to Resolve %q: %s", name, err)
+	}
+	compare(t, "Unresolve", name, servers, want)
+}
+
+type serverEntry struct {
+	mountPoint string
+	server     ipc.Server
+	endpoint   naming.Endpoint
+	name       string
+}
+
+func runServer(t *testing.T, sr veyron2.Runtime, disp ipc.Dispatcher, mountPoint string) *serverEntry {
+	return run(t, sr, disp, mountPoint, false)
+}
+
+func runMT(t *testing.T, sr veyron2.Runtime, mountPoint string) *serverEntry {
+	mt, err := service.NewMountTable("")
+	if err != nil {
+		boom(t, "NewMountTable returned error: %v", err)
+	}
+	return run(t, sr, mt, mountPoint, true)
+}
+
+func run(t *testing.T, sr veyron2.Runtime, disp ipc.Dispatcher, mountPoint string, mt bool) *serverEntry {
+	s, err := sr.NewServer(veyron2.ServesMountTableOpt(mt))
+	if err != nil {
+		boom(t, "r.NewServer: %s", err)
+	}
+	// Add a mount table server.
+	// Start serving on a loopback address.
+	ep, err := s.Listen("tcp", "127.0.0.1:0")
+	if err != nil {
+		boom(t, "Failed to Listen: %s", err)
+	}
+	if err := s.Serve(mountPoint, disp); err != nil {
+		boom(t, "Failed to serve mount table at %s: %s", mountPoint, err)
+	}
+	name := naming.JoinAddressName(ep.String(), "")
+	if !mt {
+		name = name + "//"
+	}
+	return &serverEntry{mountPoint: mountPoint, server: s, endpoint: ep, name: name}
+}
+
+const (
+	mt1MP = "mt1"
+	mt2MP = "mt2"
+	mt3MP = "mt3"
+	mt4MP = "mt4"
+	mt5MP = "mt5"
+	j1MP  = "joke1"
+	j2MP  = "joke2"
+	j3MP  = "joke3"
+
+	ttl = 100 * time.Second
+)
+
+// runMountTables creates a root mountable with some mount tables mounted
+// in it: mt{1,2,3,4,5}
+func runMountTables(t *testing.T, r veyron2.Runtime) (*serverEntry, map[string]*serverEntry) {
+	root := runMT(t, r, "")
+	r.Namespace().SetRoots(root.name)
+	t.Logf("mountTable %q -> %s", root.mountPoint, root.endpoint)
+
+	mps := make(map[string]*serverEntry)
+	for _, mp := range []string{mt1MP, mt2MP, mt3MP, mt4MP, mt5MP} {
+		m := runMT(t, r, mp)
+		t.Logf("mountTable %q -> %s", mp, m.endpoint)
+		mps[mp] = m
+	}
+	return root, mps
+}
+
+// createNamespace creates a hiearachy of mounttables and servers
+// as follows:
+// /mt1, /mt2, /mt3, /mt4, /mt5, /joke1, /joke2, /joke3.
+// That is, mt1 is a mount table mounted in the root mount table,
+// joke1 is a server mounted in the root mount table.
+func createNamespace(t *testing.T, r veyron2.Runtime) (*serverEntry, map[string]*serverEntry, map[string]*serverEntry, func()) {
+	root, mts := runMountTables(t, r)
+	jokes := make(map[string]*serverEntry)
+	// Let's run some non-mount table services.
+	for _, j := range []string{j1MP, j2MP, j3MP} {
+		disp := ipc.SoloDispatcher(&testServer{}, nil)
+		jokes[j] = runServer(t, r, disp, j)
+	}
+	return root, mts, jokes, func() {
+		for _, s := range jokes {
+			s.server.Stop()
+		}
+		for _, s := range mts {
+			s.server.Stop()
+		}
+		root.server.Stop()
+	}
+}
+
+// runNestedMountTables creates some nested mount tables in the hierarchy
+// created by createNamespace as follows:
+// /mt4/foo, /mt4/foo/bar and /mt4/baz where foo, bar and baz are mount tables.
+func runNestedMountTables(t *testing.T, r veyron2.Runtime, mts map[string]*serverEntry) {
+	ns, ctx := r.Namespace(), r.NewContext()
+	// Set up some nested mounts and verify resolution.
+	for _, m := range []string{"mt4/foo", "mt4/foo/bar"} {
+		mts[m] = runMT(t, r, m)
+	}
+
+	// Use a global name for a mount, rather than a relative one.
+	// We directly mount baz into the mt4/foo mount table.
+	globalMP := naming.JoinAddressName(mts["mt4/foo"].name, "baz")
+	mts["baz"] = runMT(t, r, "baz")
+	if err := ns.Mount(ctx, globalMP, mts["baz"].name, ttl); err != nil {
+		boom(t, "Failed to Mount %s: %s", globalMP, err)
+	}
+}
+
+// TestNamespaceCommon tests common use of the Namespace library
+// against a root mount table and some mount tables mounted on it.
+func TestNamespaceCommon(t *testing.T) {
+	// We need the default runtime for the server-side mounttable code
+	// which references rt.R() to create new endpoints
+	rt.Init()
+	r, _ := rt.New() // We use a different runtime for the client side.
+	root, mts, jokes, stopper := createNamespace(t, r)
+	defer stopper()
+	ns := r.Namespace()
+
+	// All of the initial mounts are served by the root mounttable
+	// and hence ResolveToMountTable should return the root mountable
+	// as the address portion of the terminal name for those mounttables.
+	testResolveToMountTable(t, r, ns, "", root.name)
+	for _, m := range []string{mt2MP, mt3MP, mt5MP} {
+		rootMT := naming.MakeTerminal(naming.Join(root.name, m))
+		// All of these mount tables are hosted by the root mount table
+		testResolveToMountTable(t, r, ns, m, rootMT)
+		testResolveToMountTable(t, r, ns, "//"+m, rootMT)
+
+		// The server registered for each mount point is a mount table
+		testResolve(t, r, ns, m, mts[m].name)
+
+		// ResolveToMountTable will walk through to the sub MountTables
+		mtbar := naming.Join(m, "bar")
+		subMT := naming.MakeTerminal(naming.Join(mts[m].name, "bar"))
+		testResolveToMountTable(t, r, ns, mtbar, subMT)
+
+		// ResolveToMountTable will not walk through if the name is terminal
+		testResolveToMountTable(t, r, ns, "//"+mtbar, naming.Join(rootMT, "bar"))
+	}
+
+	for _, j := range []string{j1MP, j2MP, j3MP} {
+		testResolve(t, r, ns, j, jokes[j].name)
+	}
+}
+
+// TestNamespaceDetails tests more detailed use of the Namespace library,
+// including the intricacies of // meaning and placement.
+func TestNamespaceDetails(t *testing.T) {
+	sr := rt.Init()
+	r, _ := rt.New() // We use a different runtime for the client side.
+	root, mts, _, stopper := createNamespace(t, sr)
+	defer stopper()
+
+	ns := r.Namespace()
+	ns.SetRoots(root.name)
+
+	// Mount using a relative name starting with //.
+	// This means don't walk out of the namespace's root mount table
+	// even if there is already something mounted at mt2. Thus, the example
+	// below will fail.
+	mt3Server := mts[mt3MP].name
+	mt2a := "//mt2/a"
+	if err := ns.Mount(r.NewContext(), mt2a, mt3Server, ttl); err != naming.ErrNoSuchName {
+		boom(t, "Successfully mounted %s - expected an err %v, not %v", mt2a, naming.ErrNoSuchName, err)
+	}
+
+	// Mount using the relative name not starting with //.
+	// This means walk through mt2 if it already exists and mount within
+	// the lower level mount table, if the name doesn't exist we'll create
+	// a new name for it.
+	mt2a = "mt2/a"
+	if err := ns.Mount(r.NewContext(), mt2a, mt3Server, ttl); err != nil {
+		boom(t, "Failed to Mount %s: %s", mt2a, err)
+	}
+
+	mt2mt := naming.MakeTerminal(naming.Join(mts[mt2MP].name, "a"))
+	// The mt2/a is served by the mt2 mount table
+	testResolveToMountTable(t, r, ns, mt2a, mt2mt)
+	// The server for mt2a is mt3server from the second mount above.
+	testResolve(t, r, ns, mt2a, mt3Server)
+
+	// Using a terminal or non-terminal name makes no difference if the
+	// mount is directed to the root name server (since that's the root
+	// for the namespace for this process) and the name exists within
+	// that mount table. In both cases, the server will be added to the
+	// set of mount table servers for that name.
+	for _, mp := range []struct{ name, server string }{
+		{"mt2", mts[mt4MP].name},
+		{"//mt2", mts[mt5MP].name},
+	} {
+		if err := ns.Mount(r.NewContext(), mp.name, mp.server, ttl); err != nil {
+			boom(t, "Failed to Mount %s: %s", mp.name, err)
+		}
+	}
+
+	// We now have 3 mount tables prepared to serve mt2/a
+	testResolveToMountTable(t, r, ns, "mt2/a",
+		mts[mt2MP].name+"//a",
+		mts[mt4MP].name+"//a",
+		mts[mt5MP].name+"//a")
+	testResolve(t, r, ns, "mt2", mts[mt2MP].name, mts[mt4MP].name, mts[mt5MP].name)
+}
+
+// TestNestedMounts tests some more deeply nested mounts
+func TestNestedMounts(t *testing.T) {
+	sr := rt.Init()
+	r, _ := rt.New() // We use a different runtime for the client side.
+	root, mts, _, stopper := createNamespace(t, sr)
+	runNestedMountTables(t, sr, mts)
+	defer stopper()
+
+	ns := r.Namespace()
+	ns.SetRoots(root.name)
+
+	// Set up some nested mounts and verify resolution.
+	for _, m := range []string{"mt4/foo", "mt4/foo/bar"} {
+		testResolve(t, r, ns, m, mts[m].name)
+	}
+
+	testResolveToMountTable(t, r, ns, "mt4/foo",
+		mts[mt4MP].name+"//foo")
+	testResolveToMountTable(t, r, ns, "mt4/foo/bar",
+		mts["mt4/foo"].name+"//bar")
+	testResolveToMountTable(t, r, ns, "mt4/foo/baz", mts["mt4/foo"].name+"//baz")
+}
+
+// TestServers tests invoking RPCs on simple servers
+func TestServers(t *testing.T) {
+	sr := rt.Init()
+	r, _ := rt.New() // We use a different runtime for the client side.
+	root, mts, jokes, stopper := createNamespace(t, sr)
+	defer stopper()
+	ns := r.Namespace()
+	ns.SetRoots(root.name)
+
+	// Let's run some non-mount table servic
+	for _, j := range []string{j1MP, j2MP, j3MP} {
+		testResolve(t, r, ns, j, jokes[j].name)
+		knockKnock(t, r, j)
+		globalName := naming.JoinAddressName(mts["mt4"].name, j)
+		disp := ipc.SoloDispatcher(&testServer{}, nil)
+		gj := "g_" + j
+		jokes[gj] = runServer(t, r, disp, globalName)
+		testResolve(t, r, ns, "mt4/"+j, jokes[gj].name)
+		knockKnock(t, r, "mt4/"+j)
+		testResolveToMountTable(t, r, ns, "mt4/"+j, naming.MakeTerminal(globalName))
+		testResolveToMountTable(t, r, ns, "mt4/"+j+"/garbage", naming.MakeTerminal(globalName+"/garbage"))
+	}
+}
+
+// TestGlob tests some glob patterns.
+func TestGlob(t *testing.T) {
+	sr := rt.Init()
+	r, _ := rt.New() // We use a different runtime for the client side.
+	root, mts, _, stopper := createNamespace(t, sr)
+	runNestedMountTables(t, sr, mts)
+	defer stopper()
+	ns := r.Namespace()
+	ns.SetRoots(root.name)
+
+	tln := []string{"baz", "mt1", "mt2", "mt3", "mt4", "mt5", "joke1", "joke2", "joke3"}
+	barbaz := []string{"mt4/foo/bar", "mt4/foo/baz"}
+	foo := append([]string{"mt4/foo"}, barbaz...)
+	// Try various globs.
+	globTests := []struct {
+		pattern  string
+		expected []string
+	}{
+		{"*", tln},
+		// TODO(cnicolaou): the glob that doesn't match a name should fail.
+		//
+		{"x", []string{"x"}},
+		{"m*", []string{"mt1", "mt2", "mt3", "mt4", "mt5"}},
+		{"mt[2,3]", []string{"mt2", "mt3"}},
+		{"*z", []string{"baz"}},
+		{"...", append(append(tln, foo...), "")},
+		{"*/...", append(tln, foo...)},
+		{"*/foo/*", barbaz},
+		{"*/*/*z", []string{"mt4/foo/baz"}},
+		{"*/f??/*z", []string{"mt4/foo/baz"}},
+	}
+	for _, test := range globTests {
+		out := doGlob(t, r, ns, test.pattern)
+		compare(t, "Glob", test.pattern, test.expected, out)
+	}
+}
+
+func TestCycles(t *testing.T) {
+	t.Skip() // Remove when the bug is fixed.
+
+	sr := rt.Init()
+	r, _ := rt.New() // We use a different runtime for the client side.
+	defer r.Shutdown()
+
+	root, _, _, stopper := createNamespace(t, sr)
+	defer stopper()
+	ns := r.Namespace()
+	ns.SetRoots(root.name)
+
+	c1 := runMT(t, r, "c1")
+	c2 := runMT(t, r, "c2")
+	c3 := runMT(t, r, "c3")
+	defer c1.server.Stop()
+	defer c2.server.Stop()
+	defer c3.server.Stop()
+
+	m := "c1/c2"
+	if err := ns.Mount(r.NewContext(), m, c1.name, ttl); err != nil {
+		boom(t, "Failed to Mount %s: %s", "c1/c2", err)
+	}
+
+	m = "c1/c2/c3"
+	if err := ns.Mount(r.NewContext(), m, c3.name, ttl); err != nil {
+		boom(t, "Failed to Mount %s: %s", m, err)
+	}
+
+	m = "c1/c3/c4"
+	if err := ns.Mount(r.NewContext(), m, c1.name, ttl); err != nil {
+		boom(t, "Failed to Mount %s: %s", m, err)
+	}
+
+	testResolve(t, r, ns, "c1", c1.name)
+	testResolve(t, r, ns, "c1/c2", c1.name)
+	testResolve(t, r, ns, "c1/c3", c3.name)
+	testResolve(t, r, ns, "c1/c3/c4", c1.name)
+	testResolve(t, r, ns, "c1/c3/c4/c3/c4", c1.name)
+	cycle := "c3/c4"
+	for i := 0; i < 40; i++ {
+		cycle += "/c3/c4"
+	}
+	if _, err := ns.Resolve(r, "c1/"+cycle); err.Error() != "Resolution depth exceeded" {
+		boom(t, "Failed to detect cycle")
+	}
+
+	// Remove the timeout when the bug is fixed, right now, this just
+	// finishes the test immediately. Add a comparison for the expected
+	// output from glob also.
+	ch := make(chan struct{})
+	go func() {
+		doGlob(t, r, ns, "c1/...")
+		close(ch)
+	}()
+	select {
+	case <-ch:
+	case <-time.After(time.Millisecond * 100):
+		t.Errorf("glob timedout")
+	}
+}
+
+func TestUnresolve(t *testing.T) {
+	// TODO(cnicolaou): move unresolve tests into this test, right now,
+	// that's annoying because the stub compiler has some blocking bugs and the
+	// Unresolve functionality is partially implemented in the stubs.
+	t.Skip()
+	sr := rt.Init()
+	r, _ := rt.New() // We use a different runtime for the client side.
+	defer r.Shutdown()
+	root, mts, jokes, stopper := createNamespace(t, sr)
+	runNestedMountTables(t, sr, mts)
+	defer stopper()
+	ns := r.Namespace()
+	ns.SetRoots(root.name)
+
+	vlog.Infof("Glob: %v", doGlob(t, r, ns, "*"))
+	testResolve(t, r, ns, "joke1", jokes["joke1"].name)
+	testUnresolve(t, r, ns, "joke1", "")
+}
+
+// TestGoroutineLeaks tests for leaking goroutines - we have many:-(
+func TestGoroutineLeaks(t *testing.T) {
+	t.Skip()
+	sr := rt.Init()
+	r, _ := rt.New() // We use a different runtime for the client side.
+	defer r.Shutdown()
+	_, _, _, stopper := createNamespace(t, sr)
+	defer func() {
+		vlog.Infof("%d goroutines:", runtime.NumGoroutine())
+	}()
+	defer stopper()
+	defer func() {
+		vlog.Infof("%d goroutines:", runtime.NumGoroutine())
+	}()
+	//panic("this will show up lots of goroutine+channel leaks!!!!")
+}
+
 func TestBadRoots(t *testing.T) {
 	r, _ := rt.New()
+	defer r.Shutdown()
 	if _, err := namespace.New(r); err != nil {
 		t.Errorf("namespace.New should not have failed with no roots")
 	}
@@ -87,264 +494,3 @@
 		t.Errorf("namespace.New should have failed with an unrooted name")
 	}
 }
-
-const (
-	mt1Prefix = "mt1"
-	mt2Prefix = "mt2"
-	mt3Prefix = "mt3"
-	mt4Prefix = "mt4"
-	mt5Prefix = "mt5"
-)
-
-func testResolveToMountTable(t *testing.T, ctx context.T, mt naming.Namespace, name, want string) {
-	servers, err := mt.ResolveToMountTable(ctx, name)
-	if err != nil {
-		boom(t, "Failed to ResolveToMountTable %q: %s", name, err)
-	}
-	if len(servers) != 1 || servers[0] != want {
-		boom(t, "ResolveToMountTable %q returned wrong servers: got %v, want %v", name, servers, want)
-	}
-}
-
-func testResolve(t *testing.T, ctx context.T, mt naming.Namespace, name, want string) {
-	servers, err := mt.Resolve(ctx, name)
-	if err != nil {
-		boom(t, "Failed to Resolve %q: %s", name, err)
-	}
-	if len(servers) != 1 || servers[0] != want {
-		boom(t, "Resolve %q returned wrong servers: got %v, want %v", name, servers, want)
-	}
-}
-
-func newMountTable(t *testing.T) ipc.Dispatcher {
-	mt, err := service.NewMountTable("")
-	if err != nil {
-		boom(t, "NewMountTable returned error: %v", err)
-	}
-	return mt
-}
-
-func runServer(t *testing.T) (ipc.Server, naming.Endpoint) {
-	// We are also running a server on this runtime using stubs so we must
-	// use rt.Init(). If the server were in a separate address as per usual,
-	// this wouldn't be needed and we could use rt.New.
-	sr := rt.Init()
-	vlog.Infof("TestNamespace")
-	server, err := sr.NewServer()
-	if err != nil {
-		boom(t, "r.NewServer: %s", err)
-	}
-
-	// Add some mount table servers.
-	if err := server.Register(mt1Prefix, newMountTable(t)); err != nil {
-		boom(t, "Failed to register mount table: %s", err)
-	}
-
-	if err := server.Register(mt2Prefix, newMountTable(t)); err != nil {
-		boom(t, "Failed to register mount table: %s", err)
-	}
-
-	if err := server.Register(mt3Prefix, newMountTable(t)); err != nil {
-		boom(t, "Failed to register mount table: %s", err)
-	}
-
-	if err := server.Register(mt4Prefix, newMountTable(t)); err != nil {
-		boom(t, "Failed to register mount table: %s", err)
-	}
-
-	if err := server.Register(mt5Prefix, newMountTable(t)); err != nil {
-		boom(t, "Failed to register mount table: %s", err)
-	}
-
-	// Add a few simple services.
-	if err := server.Register("joke1", ipc.SoloDispatcher(new(testServer), nil)); err != nil {
-		boom(t, "Failed to register test service: %s", err)
-	}
-	if err := server.Register("joke2", ipc.SoloDispatcher(new(testServer), nil)); err != nil {
-		boom(t, "Failed to register test service: %s", err)
-	}
-	if err := server.Register("joke3", ipc.SoloDispatcher(new(testServer), nil)); err != nil {
-		boom(t, "Failed to register test service: %s", err)
-	}
-
-	// Start serving on a loopback address.
-	ep, err := server.Listen("tcp", "127.0.0.1:0")
-	if err != nil {
-		boom(t, "Failed to Listen: %s", err)
-	}
-	t.Logf("endpoint %s", ep)
-	return server, ep
-}
-
-func TestNamespace(t *testing.T) {
-	// Run a MountTable server, which is serving MountTables on:
-	// /<estr>/{mt1,mt2,mt3,mt4,mt5}
-	server, ep := runServer(t)
-	defer server.Stop()
-
-	estr := ep.String()
-
-	// Run a client, creating a new runtime for it and intializing its
-	// namespace root to point to the server created above on /<ep>/mt1.
-	// This means that any relative names mounted using this local namespace
-	// will appear below mt1.
-	r, err := rt.New(veyron2.NamespaceRoots([]string{naming.JoinAddressName(estr, mt1Prefix)}))
-	if err != nil {
-		boom(t, "Failed to create client runtime: %s", err)
-	}
-	mt := r.Namespace()
-
-	ctx := r.NewContext()
-
-	// Create a DAG of mount table servers using relative addresses.
-	ttl := time.Duration(100) * time.Second
-	// Mount using a relative name starting with //.  This means don't walk out of the
-	// namespace's root mount table even if there is already something mounted at mt2.
-	mt2Name := naming.JoinAddressName(estr, mt2Prefix)
-	if err := mt.Mount(ctx, "//mt2", mt2Name, ttl); err != nil {
-		boom(t, "Failed to Mount //mt2: %s", err)
-	}
-	// Mount using the relative name not starting with //.  This means walk through mt3
-	// if it already exists and mount at its root.  However, since it doesn't exist, this is the
-	// same as if we'd mounted at //mt3.
-	//
-	// NB: if we mount two replica mount table servers at the same place in the namespace,
-	// we MUST use the // form or it will try to mount the second inside the first rather
-	// than at the same place as the first.
-	mt3Name := naming.JoinAddressName(estr, mt3Prefix)
-	if err := mt.Mount(ctx, "mt3", mt3Name, ttl); err != nil {
-		boom(t, "Failed to Mount mt3: %s", err)
-	}
-
-	mt1MT := naming.MakeTerminal(naming.JoinAddressName(estr, mt1Prefix))
-	mt2MT := naming.MakeTerminal(naming.JoinAddressName(estr, naming.Join(mt1Prefix, mt2Prefix)))
-	mt3MT := naming.MakeTerminal(naming.JoinAddressName(estr, naming.Join(mt1Prefix, mt3Prefix)))
-
-	// After the mounts above we have MountTables at /<estr>/mt1{//mt2,//mt3},
-	// with server addresses as per below.
-	testResolveToMountTable(t, ctx, mt, "", mt1MT)
-	testResolveToMountTable(t, ctx, mt, "mt2", mt2MT)
-	testResolveToMountTable(t, ctx, mt, "mt3", mt3MT)
-	testResolveToMountTable(t, ctx, mt, "//mt3", naming.JoinAddressName(estr, "//mt1//mt3"))
-
-	// We can resolve to the MountTables using rooted, terminal names
-	// as follows, both mt1 and mt1/{mt2,mt3} are served by the
-	// top-level MountTable
-	testResolve(t, ctx, mt, naming.JoinAddressName(estr, "//mt1"), mt1MT)
-	testResolve(t, ctx, mt, naming.JoinAddressName(estr, "//mt1/mt2"), mt2MT)
-	testResolve(t, ctx, mt, naming.JoinAddressName(estr, "//mt1/mt3"), mt3MT)
-
-	// returns [mt2, mt3]
-	vlog.Infof("GLOB: %s", doGlob(t, ctx, mt, "*"))
-
-	// Perform two mounts that have to actually walk through other mount tables.
-	if err := mt.Mount(ctx, "mt2/mt4", naming.JoinAddressName(estr, mt4Prefix), ttl); err != nil {
-		boom(t, "Failed to Mount mt2/mt4: %s", err)
-	}
-	if err := mt.Mount(ctx, "mt3/mt4", naming.JoinAddressName(estr, mt4Prefix), ttl); err != nil {
-		boom(t, "Failed to Mount mt3/mt4: %s", err)
-	}
-
-	// After the mounts above we now have /<estr>{/mt1/mt2/mt4,/mt1/mt3/mt4}.
-	testResolveToMountTable(t, ctx, mt, "mt2/mt4", naming.JoinAddressName(estr, "//mt2/mt4"))
-	testResolveToMountTable(t, ctx, mt, "mt3/mt4", naming.JoinAddressName(estr, "//mt3/mt4"))
-
-	testResolve(t, ctx, mt, naming.JoinAddressName(estr, "//mt1/mt2/mt4"), naming.JoinAddressName(estr, "//mt1/mt2/mt4"))
-
-	// Perform a mount that uses a global name as the mount point rather than
-	// one relative to our namespace's root.
-	global := naming.JoinAddressName(estr, "mt3/mt4/mt5")
-	if err := mt.Mount(ctx, global, naming.JoinAddressName(estr, mt5Prefix), ttl); err != nil {
-		boom(t, "Failed to Mount %s: %s", global, err)
-	}
-
-	// This mounts the service OA (ep/joke1) as joke1.
-	if err := mt.Mount(ctx, "joke1", naming.JoinAddressName(estr, "//joke1"), ttl); err != nil {
-		boom(t, "Failed to Mount joke1: %s", err)
-	}
-	// This mounts the raw server endpoint as joke2 -- like Publish would.
-	if err := mt.Mount(ctx, "joke2", naming.JoinAddressName(estr, "")+"//", ttl); err != nil {
-		boom(t, "Failed to Mount joke2: %s", err)
-	}
-	// This mounts the raw server endpoint as joke3 in mt3 -- like Publish would.
-	if err := mt.Mount(ctx, "mt3/joke3", naming.JoinAddressName(estr, "")+"//", ttl); err != nil {
-		boom(t, "Failed to Mount joke3: %s", err)
-	}
-
-	// After the mounts above we have:
-	// /<estr>/mt3/mt4/mt5 - the global mount above
-	// /<estr>/mt1/{joke1,joke2,mt3/joker3}
-
-	// Now try resolving inside the namespace.   This guarantees both that the mounts did
-	// what we expected AND that we can actually resolve the results.
-
-	// Get back an error since this will walk through mt5 to its root.
-	_, err = mt.Resolve(ctx, "mt3/mt4/mt5")
-	if err == nil {
-		boom(t, "Should have failed to mt3/mt4/mt5")
-	}
-
-	// Resolving m3/mt4/mt5 to a MountTable using the local namepsace gives
-	// us /<estr>//mt4/mt5.
-	testResolveToMountTable(t, ctx, mt, "mt3/mt4/mt5", naming.JoinAddressName(estr, "//mt4/mt5"))
-	testResolveToMountTable(t, ctx, mt, "mt3/mt4//mt5", naming.JoinAddressName(estr, "//mt4//mt5"))
-
-	// But looking up mt4/mt5 in the local namespace will give us
-	// /<estr>//mt1/mt4/mt5 since the local namespace has mt1 as its root!
-	testResolveToMountTable(t, ctx, mt, "mt4/mt5", naming.JoinAddressName(estr, "//mt1/mt4/mt5"))
-
-	// Looking mt3//mt4/mt5 will return the MountTable that serves //mt4/mt5.
-	testResolveToMountTable(t, ctx, mt, "mt3//mt4/mt5", naming.JoinAddressName(estr, "//mt3//mt4/mt5"))
-	// And the MountTable that serves //mt4/mt5 is /<epstr>//mt1/mt4/mt5
-	testResolveToMountTable(t, ctx, mt, "//mt4/mt5", naming.JoinAddressName(estr, "//mt1//mt4/mt5"))
-
-	vlog.Infof("\n-------------------------------------------------")
-	jokeTests := []struct {
-		name, resolved, resolvedToMT string
-	}{
-		{"joke1", naming.JoinAddressName(estr, "//joke1"), naming.JoinAddressName(estr, "//mt1/joke1")},
-		{"joke2", naming.JoinAddressName(estr, "") + "//", naming.JoinAddressName(estr, "//mt1/joke2")},
-		{"mt3/joke3", naming.JoinAddressName(estr, "") + "//", naming.JoinAddressName(estr, "//mt3/joke3")},
-	}
-	for _, test := range jokeTests {
-
-		servers, err := mt.Resolve(ctx, test.name)
-		if err != nil {
-			boom(t, "Failed to Resolve %s: %s", test.name, err)
-		}
-		if len(servers) != 1 || servers[0] != test.resolved {
-			boom(t, "Resolve %s returned wrong servers: %v, expected: %s", test.name, servers, test.resolved)
-		}
-
-		servers, err = mt.ResolveToMountTable(ctx, test.name)
-		if err != nil {
-			boom(t, "Failed to ResolveToMountTable %s: %s", test.name, err)
-		}
-		if len(servers) != 1 || servers[0] != test.resolvedToMT {
-			boom(t, "ResolveToMountTable %s returned wrong servers: %v, expected: %s", test.name, servers, test.resolvedToMT)
-		}
-	}
-
-	knockKnock(t, r, "joke1")
-	knockKnock(t, r, "joke2/joke2")
-	knockKnock(t, r, "mt3/joke3/joke3")
-
-	// Try various globs.
-	globTests := []struct {
-		pattern  string
-		expected []string
-	}{
-		{"*", []string{"mt2", "mt3", "joke1", "joke2"}},
-
-		{"*/...", []string{"mt2", "mt3", "mt2/mt4", "mt3/mt4", "mt2/mt4/mt5", "mt3/mt4/mt5", "joke1", "joke2", "mt3/joke3"}},
-		{"*/m?4/*5", []string{"mt2/mt4/mt5", "mt3/mt4/mt5"}},
-		{"*2*/*/*5", []string{"mt2/mt4/mt5"}},
-		{"mt2/*/*5", []string{"mt2/mt4/mt5"}},
-		{"mt2/mt4/*5", []string{"mt2/mt4/mt5"}},
-	}
-	for _, test := range globTests {
-		out := doGlob(t, ctx, mt, test.pattern)
-		checkMatch(t, test.pattern, test.expected, out)
-	}
-
-}
diff --git a/runtimes/google/naming/namespace/mount.go b/runtimes/google/naming/namespace/mount.go
index 721ef85..78eeda3 100644
--- a/runtimes/google/naming/namespace/mount.go
+++ b/runtimes/google/naming/namespace/mount.go
@@ -5,6 +5,7 @@
 
 	"veyron2/context"
 	"veyron2/ipc"
+	"veyron2/vlog"
 )
 
 // mountIntoMountTable mounts a single server into a single mount table.
@@ -52,6 +53,7 @@
 			finalerr = err
 		}
 	}
+	vlog.VI(1).Infof("Mount(%s, %s) -> %v", name, server, finalerr)
 	return finalerr
 }
 
diff --git a/runtimes/google/naming/namespace/namespace.go b/runtimes/google/naming/namespace/namespace.go
index bc64e1f..16b4fdd 100644
--- a/runtimes/google/naming/namespace/namespace.go
+++ b/runtimes/google/naming/namespace/namespace.go
@@ -41,7 +41,7 @@
 }
 
 // SetRoots implements naming.MountTable.SetRoots
-func (ns *namespace) SetRoots(roots []string) error {
+func (ns *namespace) SetRoots(roots ...string) error {
 	if !rooted(roots) {
 		return badRoots(roots)
 	}
@@ -52,6 +52,17 @@
 	return nil
 }
 
+// Roots implements naming.MountTable.Roots
+func (ns *namespace) Roots() []string {
+	ns.RLock()
+	defer ns.RUnlock()
+	roots := make([]string, len(ns.roots))
+	for i, r := range ns.roots {
+		roots[i] = r
+	}
+	return roots
+}
+
 // rootName 'roots' a name: if name is not a rooted name, it prepends the root
 // mounttable's OA.
 func (ns *namespace) rootName(name string) []string {
diff --git a/runtimes/google/naming/namespace/resolve.go b/runtimes/google/naming/namespace/resolve.go
index 92fc6f0..3043a31 100644
--- a/runtimes/google/naming/namespace/resolve.go
+++ b/runtimes/google/naming/namespace/resolve.go
@@ -2,6 +2,7 @@
 
 import (
 	"errors"
+	"runtime"
 
 	"veyron2/context"
 	"veyron2/ipc"
@@ -72,60 +73,77 @@
 	return
 }
 
-// Resolve implements veyron2/naming.MountTable.
+// Resolve implements veyron2/naming.Namespace.
 func (ns *namespace) Resolve(ctx context.T, name string) ([]string, error) {
-	vlog.VI(2).Infof("Resolve %s", name)
 	names := ns.rootName(name)
+	if vlog.V(2) {
+		_, file, line, _ := runtime.Caller(1)
+		vlog.Infof("Resolve(%s) called from %s:%d", name, file, line)
+		vlog.Infof("Resolve(%s) -> rootNames %s", name, names)
+	}
 	if len(names) == 0 {
 		return nil, naming.ErrNoMountTable
 	}
 	// Iterate walking through mount table servers.
 	for remaining := maxDepth; remaining > 0; remaining-- {
-		vlog.VI(2).Infof("Resolve loop %s", names)
+		vlog.VI(2).Infof("Resolve(%s) loop %s", name, names)
 		if terminal(names) {
+			vlog.VI(1).Infof("Resolve(%s) -> %s", name, names)
 			return names, nil
 		}
 		var err error
 		curr := names
 		if names, err = resolveAgainstMountTable(ctx, ns.rt.Client(), names); err != nil {
-
 			// If the name could not be found in the mount table, return an error.
 			if verror.Equal(naming.ErrNoSuchNameRoot, err) {
 				err = naming.ErrNoSuchName
 			}
 			if verror.Equal(naming.ErrNoSuchName, err) {
+				vlog.VI(1).Infof("Resolve(%s) -> (NoSuchName: %v)", name, curr)
 				return nil, err
 			}
 			// Any other failure (server not found, no ResolveStep
 			// method, etc.) are a sign that iterative resolution can
 			// stop.
-			return makeTerminal(curr), nil
+			t := makeTerminal(curr)
+			vlog.VI(1).Infof("Resolve(%s) -> %s", name, t)
+			return t, nil
 		}
 	}
 	return nil, naming.ErrResolutionDepthExceeded
 }
 
-// ResolveToMountTable implements veyron2/naming.MountTable.
+// ResolveToMountTable implements veyron2/naming.Namespace.
 func (ns *namespace) ResolveToMountTable(ctx context.T, name string) ([]string, error) {
 	names := ns.rootName(name)
-	vlog.VI(2).Infof("ResolveToMountTable %s -> rootNames %s", name, names)
+	if vlog.V(2) {
+		_, file, line, _ := runtime.Caller(1)
+		vlog.Infof("ResolveToMountTable(%s) called from %s:%d", name, file, line)
+		vlog.Infof("ResolveToMountTable(%s) -> rootNames %s", name, names)
+	}
 	if len(names) == 0 {
 		return nil, naming.ErrNoMountTable
 	}
 	last := names
 	for remaining := maxDepth; remaining > 0; remaining-- {
-		vlog.VI(2).Infof("ResolveToMountTable loop %s", names)
+		vlog.VI(2).Infof("ResolveToMountTable(%s) loop %s", name, names)
 		var err error
 		curr := names
 		if terminal(curr) {
-			return makeTerminal(last), nil
+			t := makeTerminal(last)
+			vlog.VI(1).Infof("ResolveToMountTable(%s) -> %s", name, t)
+			return t, nil
 		}
 		if names, err = resolveAgainstMountTable(ctx, ns.rt.Client(), names); err != nil {
 			if verror.Equal(naming.ErrNoSuchNameRoot, err) {
-				return makeTerminal(last), nil
+				t := makeTerminal(last)
+				vlog.VI(1).Infof("ResolveToMountTable(%s) -> %s (NoSuchRoot: %v)", name, t, curr)
+				return t, nil
 			}
 			if verror.Equal(naming.ErrNoSuchName, err) {
-				return makeTerminal(curr), nil
+				t := makeTerminal(curr)
+				vlog.VI(1).Infof("ResolveToMountTable(%s) -> %s (NoSuchName: %v)", name, t, curr)
+				return t, nil
 			}
 			// Lots of reasons why another error can happen.  We are trying
 			// to single out "this isn't a mount table".
@@ -133,11 +151,14 @@
 			// that means "we are up but don't implement what you are
 			// asking for".
 			if notAnMT(err) {
-				return makeTerminal(last), nil
+				t := makeTerminal(last)
+				vlog.VI(1).Infof("ResolveToMountTable(%s) -> %s", name, t)
+				return t, nil
 			}
 			// TODO(caprita): If the server is unreachable for
 			// example, we may still want to return its parent
 			// mounttable rather than an error.
+			vlog.VI(1).Infof("ResolveToMountTable(%s) -> %v", name, err)
 			return nil, err
 		}
 
@@ -184,7 +205,7 @@
 // selecting the right branch (or should we return a representative of all
 // branches?).
 
-// Unesolve implements veyron2/naming.MountTable.
+// Unesolve implements veyron2/naming.Namespace.
 func (ns *namespace) Unresolve(ctx context.T, name string) ([]string, error) {
 	vlog.VI(2).Infof("Unresolve %s", name)
 	names, err := ns.Resolve(ctx, name)
diff --git a/runtimes/google/rt/mgmt.go b/runtimes/google/rt/mgmt.go
index ae53bb4..ccfebd7 100644
--- a/runtimes/google/rt/mgmt.go
+++ b/runtimes/google/rt/mgmt.go
@@ -52,16 +52,15 @@
 	if m.server, err = rt.NewServer(); err != nil {
 		return err
 	}
-	const suffix = ""
-	if err := m.server.Register(suffix, ipc.SoloDispatcher(appcycle.NewServerAppCycle(m), vflag.NewAuthorizerOrDie())); err != nil {
-		return err
-	}
 	// TODO(caprita): We should pick the address to listen on from config.
 	var ep naming.Endpoint
 	if ep, err = m.server.Listen("tcp", "127.0.0.1:0"); err != nil {
 		return err
 	}
-	return m.callbackToParent(parentName, naming.JoinAddressName(ep.String(), suffix))
+	if err := m.server.Serve("", ipc.SoloDispatcher(appcycle.NewServerAppCycle(m), vflag.NewAuthorizerOrDie())); err != nil {
+		return err
+	}
+	return m.callbackToParent(parentName, naming.JoinAddressName(ep.String(), ""))
 }
 
 func (m *mgmtImpl) callbackToParent(parentName, myName string) error {
diff --git a/runtimes/google/rt/mgmt_test.go b/runtimes/google/rt/mgmt_test.go
index 0c38c3c..e3dfdf6 100644
--- a/runtimes/google/rt/mgmt_test.go
+++ b/runtimes/google/rt/mgmt_test.go
@@ -245,16 +245,16 @@
 	if err != nil {
 		t.Fatalf("Got error: %v", err)
 	}
-	const suffix = ""
 	ch := make(chan string)
-	if err := server.Register(suffix, ipc.SoloDispatcher(node.NewServerConfig(&configServer{ch}), vflag.NewAuthorizerOrDie())); err != nil {
-		t.Fatalf("Got error: %v", err)
-	}
+
 	var ep naming.Endpoint
 	if ep, err = server.Listen("tcp", "127.0.0.1:0"); err != nil {
 		t.Fatalf("Got error: %v", err)
 	}
-	return server, naming.JoinAddressName(ep.String(), suffix), ch
+	if err := server.Serve("", ipc.SoloDispatcher(node.NewServerConfig(&configServer{ch}), vflag.NewAuthorizerOrDie())); err != nil {
+		t.Fatalf("Got error: %v", err)
+	}
+	return server, naming.JoinAddressName(ep.String(), ""), ch
 
 }
 
diff --git a/runtimes/google/vsync/vsyncd/main.go b/runtimes/google/vsync/vsyncd/main.go
index a2ca5a9..506c49d 100644
--- a/runtimes/google/vsync/vsyncd/main.go
+++ b/runtimes/google/vsync/vsyncd/main.go
@@ -41,9 +41,7 @@
 	// Register the "sync" prefix with the sync dispatcher.
 	syncd := vsync.NewSyncd(*peerEndpoints, *peerDeviceIDs, *devid, *storePath, *vstoreEndpoint, *syncTick)
 	serverSync := vsync.NewServerSync(syncd)
-	if err := s.Register("sync", ipc.SoloDispatcher(serverSync, nil)); err != nil {
-		vlog.Fatalf("syncd:: error registering service: err %v", err)
-	}
+	dispatcher := ipc.SoloDispatcher(serverSync, nil)
 
 	// Create an endpoint and begin listening.
 	if endpoint, err := s.Listen("tcp", *address); err == nil {
@@ -54,7 +52,7 @@
 
 	// Publish the vsync service. This will register it in the mount table and maintain the
 	// registration while the program runs.
-	if err := s.Publish("sync"); err != nil {
+	if err := s.Serve("sync", dispatcher); err != nil {
 		vlog.Fatalf("syncd: error publishing service: err %v", err)
 	}