/*
- * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
+ * Copyright (c) 2016, 2017 Cisco Systems, Inc. and others. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
*/
package org.opendaylight.mdsal.dom.broker;
-import static org.junit.Assert.assertFalse;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
-
-import java.lang.reflect.Field;
-import java.util.Collection;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.opendaylight.mdsal.dom.broker.TestUtils.getTestRpcImplementation;
+
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.RejectedExecutionException;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.mdsal.dom.api.DOMActionAvailabilityExtension;
+import org.opendaylight.mdsal.dom.api.DOMActionAvailabilityExtension.AvailabilityListener;
+import org.opendaylight.mdsal.dom.api.DOMActionImplementation;
+import org.opendaylight.mdsal.dom.api.DOMActionInstance;
+import org.opendaylight.mdsal.dom.api.DOMActionNotAvailableException;
+import org.opendaylight.mdsal.dom.api.DOMActionProviderService;
+import org.opendaylight.mdsal.dom.api.DOMActionResult;
+import org.opendaylight.mdsal.dom.api.DOMActionService;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
import org.opendaylight.mdsal.dom.api.DOMRpcAvailabilityListener;
import org.opendaylight.mdsal.dom.api.DOMRpcIdentifier;
+import org.opendaylight.mdsal.dom.api.DOMRpcImplementationNotAvailableException;
+import org.opendaylight.mdsal.dom.api.DOMRpcProviderService;
+import org.opendaylight.mdsal.dom.api.DOMSchemaService;
import org.opendaylight.mdsal.dom.broker.util.TestModel;
+import org.opendaylight.mdsal.dom.spi.SimpleDOMActionResult;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
-import org.opendaylight.yangtools.yang.model.api.SchemaPath;
-
-public class DOMRpcRouterTest extends TestUtils {
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContextListener;
+
+@RunWith(MockitoJUnitRunner.StrictStubs.class)
+public class DOMRpcRouterTest {
+ private static final YangInstanceIdentifier BAZ_PATH_BAD = YangInstanceIdentifier.create(
+ new NodeIdentifier(Actions.FOO), NodeIdentifierWithPredicates.of(Actions.FOO, Actions.BAR, "bad"));
+ private static final YangInstanceIdentifier BAZ_PATH_GOOD = YangInstanceIdentifier.create(
+ new NodeIdentifier(Actions.FOO), NodeIdentifierWithPredicates.of(Actions.FOO, Actions.BAR, "good"));
+
+ private static final DOMActionImplementation IMPL =
+ (type, path, input) -> Futures.immediateFuture(new SimpleDOMActionResult(
+ Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(Actions.OUTPUT)).build()));
@Test
- public void registerRpcImplementation() throws Exception {
- final DOMRpcRouter rpcRouter = new DOMRpcRouter();
- final Field routingTableField = DOMRpcRouter.class.getDeclaredField("routingTable");
- routingTableField.setAccessible(true);
- DOMRpcRoutingTable routingTable = (DOMRpcRoutingTable) routingTableField.get(rpcRouter);
- assertFalse(routingTable.getRpcs().containsKey(SchemaPath.ROOT));
-
- rpcRouter.registerRpcImplementation(getTestRpcImplementation(), DOMRpcIdentifier.create(SchemaPath.ROOT, null));
- routingTable = (DOMRpcRoutingTable) routingTableField.get(rpcRouter);
- assertTrue(routingTable.getRpcs().containsKey(SchemaPath.ROOT));
-
- rpcRouter.registerRpcImplementation(getTestRpcImplementation(), DOMRpcIdentifier.create(SchemaPath.SAME, null));
- routingTable = (DOMRpcRoutingTable) routingTableField.get(rpcRouter);
- assertTrue(routingTable.getRpcs().containsKey(SchemaPath.SAME));
+ public void registerRpcImplementation() {
+ try (DOMRpcRouter rpcRouter = rpcsRouter()) {
+ assertOperationKeys(rpcRouter);
+
+ final Registration fooReg = rpcRouter.getRpcProviderService().registerRpcImplementation(
+ getTestRpcImplementation(), DOMRpcIdentifier.create(Rpcs.FOO, null));
+ assertOperationKeys(rpcRouter, Rpcs.FOO);
+
+ final Registration barReg = rpcRouter.getRpcProviderService().registerRpcImplementation(
+ getTestRpcImplementation(), DOMRpcIdentifier.create(Rpcs.BAR, null));
+ assertOperationKeys(rpcRouter, Rpcs.FOO, Rpcs.BAR);
+
+ fooReg.close();
+ assertOperationKeys(rpcRouter, Rpcs.BAR);
+ barReg.close();
+ assertOperationKeys(rpcRouter);
+ }
+ }
+
+ private static void assertOperationKeys(final DOMRpcRouter router, final QName... keys) {
+ assertEquals(Set.of(keys), router.routingTable().getOperations().keySet());
}
@Test
- public void invokeRpc() throws Exception {
- final DOMRpcRouter rpcRouter = new DOMRpcRouter();
- assertNotNull(rpcRouter.invokeRpc(SchemaPath.create(false, TestModel.TEST_QNAME), null));
+ public void testFailedInvokeRpc() {
+ try (DOMRpcRouter rpcRouter = rpcsRouter()) {
+ final ListenableFuture<?> future = rpcRouter.getRpcService().invokeRpc(Rpcs.FOO, null);
+ final Throwable cause = assertThrows(ExecutionException.class, () -> Futures.getDone(future)).getCause();
+ assertThat(cause, instanceOf(DOMRpcImplementationNotAvailableException.class));
+ assertEquals("No implementation of RPC (rpcs)foo available", cause.getMessage());
+ }
}
@Test
- public void registerRpcListener() throws Exception {
- final DOMRpcRouter rpcRouter = new DOMRpcRouter();
- final DOMRpcAvailabilityListener listener = mock(DOMRpcAvailabilityListener.class);
-
- final Field listenersField = DOMRpcRouter.class.getDeclaredField("listeners");
- listenersField.setAccessible(true);
- final Collection<ListenerRegistration<? extends DOMRpcAvailabilityListener>> listenersOriginal =
- (Collection<ListenerRegistration<? extends DOMRpcAvailabilityListener>>) listenersField.get(rpcRouter);
-
- assertNotNull(rpcRouter.registerRpcListener(listener));
-
- final Collection<ListenerRegistration<? extends DOMRpcAvailabilityListener>> listenersChanged =
- (Collection<ListenerRegistration<? extends DOMRpcAvailabilityListener>>) listenersField.get(rpcRouter);
- assertNotEquals(listenersOriginal, listenersChanged);
- assertTrue(listenersOriginal.isEmpty());
- assertFalse(listenersChanged.isEmpty());
+ public void testRpcListener() {
+ try (DOMRpcRouter rpcRouter = new DOMRpcRouter()) {
+ assertEquals(List.of(), rpcRouter.listeners());
+
+ final DOMRpcAvailabilityListener listener = mock(DOMRpcAvailabilityListener.class);
+ doCallRealMethod().when(listener).acceptsImplementation(any());
+ doNothing().when(listener).onRpcAvailable(any());
+ doNothing().when(listener).onRpcUnavailable(any());
+
+ final Registration reg = rpcRouter.getRpcService().registerRpcListener(listener);
+ assertNotNull(reg);
+ assertEquals(List.of(reg), rpcRouter.listeners());
+
+ final Registration implReg = rpcRouter.getRpcProviderService().registerRpcImplementation(
+ getTestRpcImplementation(), DOMRpcIdentifier.create(Rpcs.FOO, null));
+ verify(listener, timeout(1000)).onRpcAvailable(any());
+
+ implReg.close();
+ verify(listener, timeout(1000)).onRpcUnavailable(any());
+
+ reg.close();
+ assertEquals(List.of(), rpcRouter.listeners());
+ }
}
@Test
- public void onGlobalContextUpdated() throws Exception {
- final DOMRpcRouter rpcRouter = new DOMRpcRouter();
- final Field routingTableField = DOMRpcRouter.class.getDeclaredField("routingTable");
- routingTableField.setAccessible(true);
+ public void testActionListener() {
+ try (DOMRpcRouter rpcRouter = new DOMRpcRouter()) {
+ assertEquals(List.of(), rpcRouter.actionListeners());
+
+ final AvailabilityListener listener = mock(AvailabilityListener.class);
+ final Registration reg = rpcRouter.getActionService().getExtensions()
+ .getInstance(DOMActionAvailabilityExtension.class).registerAvailabilityListener(listener);
+ assertNotNull(reg);
+ assertEquals(List.of(reg), rpcRouter.actionListeners());
- final DOMRpcRoutingTable routingTableOriginal = (DOMRpcRoutingTable) routingTableField.get(rpcRouter);
+ // FIXME: register implementation and verify notification
- rpcRouter.onGlobalContextUpdated(TestModel.createTestContext());
+ reg.close();
+ assertEquals(List.of(), rpcRouter.actionListeners());
+ }
+ }
- final DOMRpcRoutingTable routingTableChanged = (DOMRpcRoutingTable) routingTableField.get(rpcRouter);
- assertNotEquals(routingTableOriginal, routingTableChanged);
+ @Test
+ public void onGlobalContextUpdated() {
+ try (DOMRpcRouter rpcRouter = new DOMRpcRouter()) {
+ final DOMRpcRoutingTable routingTableOriginal = rpcRouter.routingTable();
+ rpcRouter.onModelContextUpdated(TestModel.createTestContext());
+ assertNotEquals(routingTableOriginal, rpcRouter.routingTable());
+ }
}
- @Test(expected = RejectedExecutionException.class)
- public void close() throws Exception {
- final DOMRpcRouter rpcRouter = new DOMRpcRouter();
+ @Test
+ public void testClose() {
+ final ListenerRegistration<EffectiveModelContextListener> reg = mock(ListenerRegistration.class);
+ doNothing().when(reg).close();
+ final DOMSchemaService schema = mock(DOMSchemaService.class);
+ doReturn(reg).when(schema).registerSchemaContextListener(any());
+
+ final DOMRpcRouter rpcRouter = new DOMRpcRouter(schema);
rpcRouter.close();
- rpcRouter.registerRpcImplementation(getTestRpcImplementation(), DOMRpcIdentifier.create(SchemaPath.ROOT, null));
+
+ final DOMRpcProviderService svc = rpcRouter.getRpcProviderService();
+ assertThrows(RejectedExecutionException.class, () -> svc.registerRpcImplementation(getTestRpcImplementation(),
+ DOMRpcIdentifier.create(Rpcs.FOO, null)));
+ }
+
+ @Test
+ public void testActionInstanceRouting() throws ExecutionException {
+ try (DOMRpcRouter rpcRouter = actionsRouter()) {
+ final DOMActionProviderService actionProvider = rpcRouter.getActionProviderService();
+ assertNotNull(actionProvider);
+ final DOMActionService actionConsumer = rpcRouter.getActionService();
+ assertNotNull(actionConsumer);
+
+ try (ObjectRegistration<?> reg = actionProvider.registerActionImplementation(IMPL,
+ DOMActionInstance.of(Actions.BAZ_TYPE, LogicalDatastoreType.OPERATIONAL, BAZ_PATH_GOOD))) {
+
+ assertAvailable(actionConsumer, BAZ_PATH_GOOD);
+ assertUnavailable(actionConsumer, BAZ_PATH_BAD);
+ }
+
+ assertUnavailable(actionConsumer, BAZ_PATH_BAD);
+ assertUnavailable(actionConsumer, BAZ_PATH_GOOD);
+ }
+ }
+
+ @Test
+ public void testActionDatastoreRouting() throws ExecutionException {
+ try (DOMRpcRouter rpcRouter = actionsRouter()) {
+ final DOMActionProviderService actionProvider = rpcRouter.getActionProviderService();
+ assertNotNull(actionProvider);
+ final DOMActionService actionConsumer = rpcRouter.getActionService();
+ assertNotNull(actionConsumer);
+
+ try (ObjectRegistration<?> reg = actionProvider.registerActionImplementation(IMPL,
+ DOMActionInstance.of(Actions.BAZ_TYPE, LogicalDatastoreType.OPERATIONAL,
+ YangInstanceIdentifier.empty()))) {
+
+ assertAvailable(actionConsumer, BAZ_PATH_GOOD);
+ assertAvailable(actionConsumer, BAZ_PATH_BAD);
+ }
+
+ assertUnavailable(actionConsumer, BAZ_PATH_BAD);
+ assertUnavailable(actionConsumer, BAZ_PATH_GOOD);
+ }
+ }
+
+ private static DOMRpcRouter actionsRouter() {
+ final DOMRpcRouter router = new DOMRpcRouter();
+ router.onModelContextUpdated(Actions.CONTEXT);
+ return router;
+ }
+
+ private static DOMRpcRouter rpcsRouter() {
+ final DOMRpcRouter router = new DOMRpcRouter();
+ router.onModelContextUpdated(Rpcs.CONTEXT);
+ return router;
+ }
+
+ private static void assertAvailable(final DOMActionService actionService, final YangInstanceIdentifier path) {
+ final DOMActionResult result;
+ try {
+ result = Futures.getDone(invokeBaz(actionService, path));
+ } catch (ExecutionException e) {
+ throw new AssertionError("Unexpected invocation failure", e);
+ }
+ assertEquals(List.of(), result.getErrors());
+ }
+
+ private static void assertUnavailable(final DOMActionService actionService, final YangInstanceIdentifier path) {
+ final ListenableFuture<? extends DOMActionResult> future = invokeBaz(actionService, path);
+ final ExecutionException ex = assertThrows(ExecutionException.class, () -> Futures.getDone(future));
+ assertThat(ex.getCause(), instanceOf(DOMActionNotAvailableException.class));
+ }
+
+ private static ListenableFuture<? extends DOMActionResult> invokeBaz(final DOMActionService actionService,
+ final YangInstanceIdentifier path) {
+ return actionService.invokeAction(Actions.BAZ_TYPE,
+ new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, path),
+ Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(Actions.INPUT)).build());
}
-}
\ No newline at end of file
+}