| // Copyright 2015 The Vanadium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| // |
| |
| package io.v.impl.google.namespace; |
| |
| import com.google.common.base.Function; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.Iterables; |
| import com.google.common.util.concurrent.SettableFuture; |
| import com.google.common.util.concurrent.Uninterruptibles; |
| |
| import io.v.v23.VIterable; |
| import junit.framework.TestCase; |
| |
| import org.joda.time.Duration; |
| |
| import java.util.List; |
| import java.util.Map; |
| import java.util.concurrent.ExecutionException; |
| import java.util.concurrent.Future; |
| import java.util.concurrent.TimeUnit; |
| |
| import io.v.impl.google.services.mounttable.MountTableServer; |
| import io.v.v23.V; |
| import io.v.v23.context.CancelableVContext; |
| import io.v.v23.context.VContext; |
| import io.v.v23.namespace.Namespace; |
| import io.v.v23.naming.Endpoint; |
| import io.v.v23.naming.GlobReply; |
| import io.v.v23.naming.MountEntry; |
| import io.v.v23.rpc.Callback; |
| import io.v.v23.rpc.ListenSpec; |
| import io.v.v23.rpc.Server; |
| import io.v.v23.rpc.ServerCall; |
| import io.v.v23.security.BlessingPattern; |
| import io.v.v23.security.VSecurity; |
| import io.v.v23.security.access.AccessList; |
| import io.v.v23.security.access.Constants; |
| import io.v.v23.security.access.Permissions; |
| import io.v.v23.services.permissions.ObjectServer; |
| import io.v.v23.verror.VException; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| |
| /** |
| * Test the {@link NamespaceImpl} implementation. |
| */ |
| public class NamespaceTest extends TestCase { |
| private VContext ctx; |
| private VContext dummyServerCtx; |
| private Endpoint mountTableEndpoint; |
| private Endpoint dummyServerEndpoint; |
| |
| @Override |
| protected void setUp() throws Exception { |
| ctx = V.init(); |
| ctx = V.withListenSpec(ctx, V.getListenSpec(ctx).withAddress( |
| new ListenSpec.Address("tcp", "localhost:0"))); |
| dummyServerCtx = V.withNewServer(ctx, "", new DummyServer(), |
| VSecurity.newAllowEveryoneAuthorizer()); |
| Server dummyServer = V.getServer(dummyServerCtx); |
| assertThat(dummyServer).isNotNull(); |
| assertThat(dummyServer.getStatus()).isNotNull(); |
| assertThat(dummyServer.getStatus().getEndpoints()).isNotEmpty(); |
| dummyServerEndpoint = dummyServer.getStatus().getEndpoints()[0]; |
| AccessList acl = new AccessList( |
| ImmutableList.of(new BlessingPattern("...")), ImmutableList.<String>of()); |
| Permissions allowAll = new Permissions(ImmutableMap.of( |
| Constants.READ.getValue(), acl, |
| Constants.WRITE.getValue(), acl, |
| Constants.ADMIN.getValue(), acl)); |
| ctx = MountTableServer.withNewServer(ctx, new MountTableServer.Params() |
| .withPermissions(ImmutableMap.of("test", allowAll)) |
| .withStatsPrefix("test")); |
| Server mtServer = V.getServer(ctx); |
| assertThat(mtServer).isNotNull(); |
| assertThat(mtServer.getStatus()).isNotNull(); |
| assertThat(mtServer.getStatus().getEndpoints()).isNotEmpty(); |
| mountTableEndpoint = mtServer.getStatus().getEndpoints()[0]; |
| Namespace n = V.getNamespace(ctx); |
| n.setRoots(ImmutableList.of(mountTableEndpoint.name())); |
| } |
| |
| @Override |
| protected void tearDown() throws Exception { |
| Server mtServer = V.getServer(ctx); |
| if (mtServer != null) { |
| mtServer.stop(); |
| } |
| Server dummyServer = V.getServer(dummyServerCtx); |
| if (dummyServer != null) { |
| dummyServer.stop(); |
| } |
| } |
| |
| private Iterable<String> globNames(Iterable<GlobReply> globReplies) { |
| return Iterables.transform(globReplies, new Function<GlobReply, String>() { |
| @Override |
| public String apply(GlobReply reply) { |
| if (reply instanceof GlobReply.Entry) { |
| return ((GlobReply.Entry) reply).getElem().getName(); |
| } else { |
| return ((GlobReply.Error) reply).getElem().getError().getMessage(); |
| } |
| } |
| }); |
| } |
| |
| public void testMountAndUnmount() throws Exception { |
| Namespace n = V.getNamespace(ctx); |
| n.mount(ctx, "test/test", dummyServerEndpoint.name(), Duration.standardDays(1)); |
| assertThat(globNames(n.glob(ctx, "test/*"))).containsExactly("test/test"); |
| n.unmount(ctx, "test/test", ""); |
| assertThat(globNames(n.glob(ctx, "test/*"))).isEmpty(); |
| } |
| |
| public void testMountAndUnmountAsync() throws Exception { |
| Namespace n = V.getNamespace(ctx); |
| { |
| FutureCallback<Void> callback = new FutureCallback<>(); |
| n.mount(ctx, "test/test", dummyServerEndpoint.name(), Duration.standardDays(1), |
| callback); |
| assertThat( |
| Uninterruptibles.getUninterruptibly(callback.getFuture(), 1, TimeUnit.SECONDS)) |
| .isNull(); |
| } |
| |
| assertThat(globNames(n.glob(ctx, "test/*"))).containsExactly("test/test"); |
| { |
| final FutureCallback<Void> callback = new FutureCallback<>(); |
| n.unmount(ctx, "test/test", "", callback); |
| assertThat( |
| Uninterruptibles.getUninterruptibly(callback.getFuture(), 1, TimeUnit.SECONDS)) |
| .isNull(); |
| } |
| |
| assertThat(globNames(n.glob(ctx, "test/*"))).isEmpty(); |
| } |
| |
| public void testDelete() throws Exception { |
| Namespace n = V.getNamespace(ctx); |
| n.mount(ctx, "test/test/test", dummyServerEndpoint.name(), Duration.standardDays(1)); |
| n.mount(ctx, "test/test/test2", dummyServerEndpoint.name(), Duration.standardDays(1)); |
| assertThat(globNames(n.glob(ctx, "test/*/*"))).containsExactly( |
| "test/test/test", "test/test/test2"); |
| // Shouldn't delete anything since the dir contains children. |
| try { |
| n.delete(ctx, "test/test", false); |
| fail("Namespace.delete() should have failed because a directory has children"); |
| } catch (VException e) { |
| // OK |
| } |
| assertThat(globNames(n.glob(ctx, "test/*/*"))).containsExactly( |
| "test/test/test", "test/test/test2"); |
| n.delete(ctx, "test/test", true); |
| assertThat(globNames(n.glob(ctx, "test/*"))).isEmpty(); |
| } |
| |
| public void testDeleteAsync() throws Exception { |
| Namespace n = V.getNamespace(ctx); |
| n.mount(ctx, "test/test/test", dummyServerEndpoint.name(), Duration.standardDays(1)); |
| n.mount(ctx, "test/test/test2", dummyServerEndpoint.name(), Duration.standardDays(1)); |
| assertThat(globNames(n.glob(ctx, "test/*/*"))).containsExactly( |
| "test/test/test", "test/test/test2"); |
| { |
| final FutureCallback<Void> callback = new FutureCallback<>(); |
| n.delete(ctx, "test/test", false, callback); |
| try { |
| Uninterruptibles.getUninterruptibly(callback.getFuture()); |
| fail("expected an ExecutionException whose cause is a VException"); |
| } catch (ExecutionException e) { |
| assertThat(e.getCause()).isInstanceOf(VException.class); |
| } |
| } |
| |
| assertThat(globNames(n.glob(ctx, "test/*/*"))).containsExactly( |
| "test/test/test", "test/test/test2"); |
| { |
| final FutureCallback<Void> callback = new FutureCallback<>(); |
| n.delete(ctx, "test/test", true, callback); |
| assertThat(Uninterruptibles.getUninterruptibly( |
| callback.getFuture(), 1, TimeUnit.SECONDS)).isNull(); |
| } |
| assertThat(globNames(n.glob(ctx, "test/*"))).isEmpty(); |
| } |
| |
| public void testGlobAsync() throws Exception { |
| Namespace n = V.getNamespace(ctx); |
| n.mount(ctx, "test/test", dummyServerEndpoint.name(), Duration.standardDays(1)); |
| { |
| final FutureCallback<VIterable<GlobReply>> callback = new FutureCallback<>(); |
| n.glob(ctx, "test/*", callback); |
| |
| List<GlobReply> reply = ImmutableList.copyOf( |
| Uninterruptibles.getUninterruptibly(callback.getFuture(), 1, TimeUnit.SECONDS)); |
| assertThat(reply).hasSize(1); |
| assertThat(reply.get(0).getElem()).isInstanceOf(MountEntry.class); |
| assertThat(((MountEntry) (reply.get(0).getElem())).getName()).isEqualTo("test/test"); |
| |
| } |
| } |
| |
| public void testGlobWithContextCancel() throws Exception { |
| Namespace n = V.getNamespace(ctx); |
| n.mount(ctx, "test/test", dummyServerEndpoint.name(), Duration.standardDays(1)); |
| |
| CancelableVContext cancelContext = ctx.withCancel(); |
| FutureCallback<VIterable<GlobReply>> callback = new FutureCallback<>(); |
| n.glob(cancelContext, "test/*", callback); |
| cancelContext.cancel(); |
| List<GlobReply> replies = ImmutableList.copyOf(Uninterruptibles.getUninterruptibly(callback.getFuture(), 1, TimeUnit.SECONDS)); |
| assertThat(replies).isEmpty(); |
| } |
| |
| public void testResolve() throws Exception { |
| Namespace n = V.getNamespace(ctx); |
| n.mount(ctx, "test/test", dummyServerEndpoint.name(), Duration.standardDays(1)); |
| MountEntry entry = n.resolve(ctx, "test/test"); |
| assertThat(entry).isNotNull(); |
| assertThat(entry.getServers()).isNotNull(); |
| assertThat(entry.getServers()).hasSize(1); |
| assertThat(entry.getServers().get(0).getServer()).isEqualTo(dummyServerEndpoint.name()); |
| |
| } |
| |
| public void testResolveAsync() throws Exception { |
| Namespace n = V.getNamespace(ctx); |
| n.mount(ctx, "test/test", dummyServerEndpoint.name(), Duration.standardDays(1)); |
| { |
| FutureCallback<MountEntry> callback = new FutureCallback<>(); |
| n.resolve(ctx, "test/test", callback); |
| |
| MountEntry entry = |
| Uninterruptibles.getUninterruptibly(callback.getFuture(), 1, TimeUnit.SECONDS); |
| assertThat(entry).isNotNull(); |
| assertThat(entry.getServers()).isNotNull(); |
| assertThat(entry.getServers()).hasSize(1); |
| assertThat(entry.getServers().get(0).getServer()).isEqualTo(dummyServerEndpoint |
| .name()); |
| } |
| } |
| |
| public void testResolveToMountTable() throws Exception { |
| Namespace n = V.getNamespace(ctx); |
| n.mount(ctx, "test/test", dummyServerEndpoint.name(), Duration.standardDays(1)); |
| MountEntry entry = n.resolveToMountTable(ctx, "test/test"); |
| assertThat(entry).isNotNull(); |
| assertThat(entry.getServers()).isNotNull(); |
| assertThat(entry.getServers()).hasSize(1); |
| assertThat(entry.getServers().get(0).getServer()).isEqualTo(mountTableEndpoint.name()); |
| } |
| |
| public void testResolveToMountTableAsync() throws Exception { |
| Namespace n = V.getNamespace(ctx); |
| n.mount(ctx, "test/test", dummyServerEndpoint.name(), Duration.standardDays(1)); |
| { |
| FutureCallback<MountEntry> callback = new FutureCallback<>(); |
| n.resolveToMountTable(ctx, "test/test", callback); |
| MountEntry entry = |
| Uninterruptibles.getUninterruptibly(callback.getFuture(), 1, TimeUnit.SECONDS); |
| assertThat(entry).isNotNull(); |
| assertThat(entry.getServers()).isNotNull(); |
| assertThat(entry.getServers()).hasSize(1); |
| assertThat(entry.getServers().get(0).getServer()).isEqualTo(mountTableEndpoint |
| .name()); |
| } |
| } |
| |
| public void testPermissionsAsync() throws Exception { |
| AccessList acl = new AccessList(ImmutableList.of(new BlessingPattern("...")), |
| ImmutableList.<String>of()); |
| Namespace n = V.getNamespace(ctx); |
| n.mount(ctx, "test/test", dummyServerEndpoint.name(), Duration.standardDays(1)); |
| { |
| final FutureCallback<Void> callback = new FutureCallback<>(); |
| n.setPermissions(ctx, "test/test", new Permissions(ImmutableMap.of("1", acl)), "1", |
| callback); |
| assertThat(Uninterruptibles.getUninterruptibly(callback.getFuture(), 1, |
| TimeUnit.SECONDS)).isNull(); |
| } |
| |
| { |
| final FutureCallback<Map<String, Permissions>> callback = new FutureCallback<>(); |
| n.getPermissions(ctx, "test/test", callback); |
| Map<String, Permissions> permissions = |
| Uninterruptibles.getUninterruptibly(callback.getFuture(), 1, TimeUnit.SECONDS); |
| assertThat(permissions).isNotNull(); |
| assertThat(permissions).hasSize(1); |
| // TODO(sjr): figure out what is actually in this map |
| assertThat(permissions).containsKey("2"); |
| } |
| |
| } |
| private static class DummyServer implements ObjectServer { |
| @Override |
| public void setPermissions(VContext ctx, ServerCall call, Permissions permissions, |
| String version) throws VException { |
| throw new VException("Unimplemented!"); |
| } |
| @Override |
| public GetPermissionsOut getPermissions(VContext ctx, ServerCall call) throws VException { |
| throw new VException("Unimplemented!"); |
| } |
| } |
| |
| private static class FutureCallback<T> implements Callback<T> { |
| private final SettableFuture<T> future = SettableFuture.create(); |
| |
| public Future<T> getFuture() { |
| return future; |
| } |
| |
| @Override |
| public void onSuccess(T result) { |
| future.set(result); |
| } |
| |
| @Override |
| public void onFailure(VException error) { |
| future.setException(error); |
| } |
| } |
| } |