TestUtils is a utility class
[mdsal.git] / dom / mdsal-dom-broker / src / test / java / org / opendaylight / mdsal / dom / broker / DOMRpcRouterTest.java
1 /*
2  * Copyright (c) 2016, 2017 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.mdsal.dom.broker;
9
10 import static org.hamcrest.CoreMatchers.instanceOf;
11 import static org.hamcrest.MatcherAssert.assertThat;
12 import static org.junit.Assert.assertEquals;
13 import static org.junit.Assert.assertFalse;
14 import static org.junit.Assert.assertNotEquals;
15 import static org.junit.Assert.assertNotNull;
16 import static org.junit.Assert.assertThrows;
17 import static org.junit.Assert.assertTrue;
18 import static org.mockito.ArgumentMatchers.any;
19 import static org.mockito.Mockito.doCallRealMethod;
20 import static org.mockito.Mockito.doNothing;
21 import static org.mockito.Mockito.doReturn;
22 import static org.mockito.Mockito.mock;
23 import static org.mockito.Mockito.timeout;
24 import static org.mockito.Mockito.verify;
25 import static org.opendaylight.mdsal.dom.broker.TestUtils.getTestRpcImplementation;
26
27 import com.google.common.util.concurrent.Futures;
28 import com.google.common.util.concurrent.ListenableFuture;
29 import java.util.List;
30 import java.util.concurrent.ExecutionException;
31 import java.util.concurrent.RejectedExecutionException;
32 import org.junit.BeforeClass;
33 import org.junit.Test;
34 import org.junit.runner.RunWith;
35 import org.mockito.junit.MockitoJUnitRunner;
36 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
37 import org.opendaylight.mdsal.dom.api.DOMActionAvailabilityExtension;
38 import org.opendaylight.mdsal.dom.api.DOMActionAvailabilityExtension.AvailabilityListener;
39 import org.opendaylight.mdsal.dom.api.DOMActionImplementation;
40 import org.opendaylight.mdsal.dom.api.DOMActionInstance;
41 import org.opendaylight.mdsal.dom.api.DOMActionNotAvailableException;
42 import org.opendaylight.mdsal.dom.api.DOMActionProviderService;
43 import org.opendaylight.mdsal.dom.api.DOMActionResult;
44 import org.opendaylight.mdsal.dom.api.DOMActionService;
45 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
46 import org.opendaylight.mdsal.dom.api.DOMRpcAvailabilityListener;
47 import org.opendaylight.mdsal.dom.api.DOMRpcIdentifier;
48 import org.opendaylight.mdsal.dom.api.DOMRpcImplementationNotAvailableException;
49 import org.opendaylight.mdsal.dom.api.DOMRpcProviderService;
50 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
51 import org.opendaylight.mdsal.dom.broker.util.TestModel;
52 import org.opendaylight.mdsal.dom.spi.SimpleDOMActionResult;
53 import org.opendaylight.yangtools.concepts.ListenerRegistration;
54 import org.opendaylight.yangtools.concepts.ObjectRegistration;
55 import org.opendaylight.yangtools.concepts.Registration;
56 import org.opendaylight.yangtools.yang.common.QName;
57 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
58 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
59 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
60 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
61 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
62 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContextListener;
63 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
64 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
65
66 @RunWith(MockitoJUnitRunner.StrictStubs.class)
67 public class DOMRpcRouterTest {
68     private static final QName FOO = QName.create("actions", "foo");
69     private static final QName BAR = QName.create(FOO, "bar");
70     private static final QName BAZ = QName.create(FOO, "baz");
71     private static final QName INPUT = QName.create(FOO, "input");
72     private static final QName OUTPUT = QName.create(FOO, "output");
73
74     private static final Absolute BAZ_TYPE = Absolute.of(FOO, BAZ);
75     private static final YangInstanceIdentifier BAZ_PATH_BAD = YangInstanceIdentifier.create(
76         new NodeIdentifier(FOO), NodeIdentifierWithPredicates.of(FOO, BAR, "bad"));
77     private static final YangInstanceIdentifier BAZ_PATH_GOOD = YangInstanceIdentifier.create(
78         new NodeIdentifier(FOO), NodeIdentifierWithPredicates.of(FOO, BAR, "good"));
79
80     private static final DOMActionImplementation IMPL =
81         (type, path, input) -> Futures.immediateFuture(new SimpleDOMActionResult(
82             Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(OUTPUT)).build()));
83
84     private static EffectiveModelContext ACTIONS_CONTEXT;
85
86     @BeforeClass
87     public static void beforeClass() {
88         ACTIONS_CONTEXT = YangParserTestUtils.parseYangResource("/actions.yang");
89     }
90
91     @Test
92     public void registerRpcImplementation() {
93         try (DOMRpcRouter rpcRouter = new DOMRpcRouter()) {
94             DOMRpcRoutingTable routingTable = rpcRouter.routingTable();
95             assertFalse(routingTable.getOperations().containsKey(TestModel.TEST_QNAME));
96
97             rpcRouter.getRpcProviderService().registerRpcImplementation(getTestRpcImplementation(),
98                 DOMRpcIdentifier.create(TestModel.TEST_QNAME, null));
99             routingTable = rpcRouter.routingTable();
100             assertTrue(routingTable.getOperations().containsKey(TestModel.TEST_QNAME));
101
102             rpcRouter.getRpcProviderService().registerRpcImplementation(getTestRpcImplementation(),
103                 DOMRpcIdentifier.create(TestModel.TEST2_QNAME, null));
104             routingTable = rpcRouter.routingTable();
105             assertTrue(routingTable.getOperations().containsKey(TestModel.TEST2_QNAME));
106         }
107     }
108
109     @Test
110     public void testFailedInvokeRpc() {
111         try (DOMRpcRouter rpcRouter = new DOMRpcRouter()) {
112             final ListenableFuture<?> future = rpcRouter.getRpcService().invokeRpc(TestModel.TEST_QNAME, null);
113             final Throwable cause = assertThrows(ExecutionException.class, () -> Futures.getDone(future)).getCause();
114             assertThat(cause, instanceOf(DOMRpcImplementationNotAvailableException.class));
115             assertEquals("No implementation of RPC "
116                 + "(urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test?revision=2014-03-13)test "
117                 + "available", cause.getMessage());
118         }
119     }
120
121     @Test
122     public void testRpcListener() {
123         try (DOMRpcRouter rpcRouter = new DOMRpcRouter()) {
124             assertEquals(List.of(), rpcRouter.listeners());
125
126             final DOMRpcAvailabilityListener listener = mock(DOMRpcAvailabilityListener.class);
127             doCallRealMethod().when(listener).acceptsImplementation(any());
128             doNothing().when(listener).onRpcAvailable(any());
129             doNothing().when(listener).onRpcUnavailable(any());
130
131             final Registration reg = rpcRouter.getRpcService().registerRpcListener(listener);
132             assertNotNull(reg);
133             assertEquals(List.of(reg), rpcRouter.listeners());
134
135             final Registration implReg = rpcRouter.getRpcProviderService().registerRpcImplementation(
136                 getTestRpcImplementation(), DOMRpcIdentifier.create(TestModel.TEST_QNAME, null));
137             verify(listener, timeout(1000)).onRpcAvailable(any());
138
139             implReg.close();
140             verify(listener, timeout(1000)).onRpcUnavailable(any());
141
142             reg.close();
143             assertEquals(List.of(), rpcRouter.listeners());
144         }
145     }
146
147     @Test
148     public void testActionListener() {
149         try (DOMRpcRouter rpcRouter = new DOMRpcRouter()) {
150             assertEquals(List.of(), rpcRouter.actionListeners());
151
152             final AvailabilityListener listener = mock(AvailabilityListener.class);
153             final Registration reg = rpcRouter.getActionService().getExtensions()
154                 .getInstance(DOMActionAvailabilityExtension.class).registerAvailabilityListener(listener);
155             assertNotNull(reg);
156             assertEquals(List.of(reg), rpcRouter.actionListeners());
157
158             // FIXME: register implementation and verify notification
159
160             reg.close();
161             assertEquals(List.of(), rpcRouter.actionListeners());
162         }
163     }
164
165     @Test
166     public void onGlobalContextUpdated() {
167         try (DOMRpcRouter rpcRouter = new DOMRpcRouter()) {
168
169             final DOMRpcRoutingTable routingTableOriginal = rpcRouter.routingTable();
170
171             rpcRouter.onModelContextUpdated(TestModel.createTestContext());
172
173             final DOMRpcRoutingTable routingTableChanged = rpcRouter.routingTable();
174             assertNotEquals(routingTableOriginal, routingTableChanged);
175         }
176     }
177
178     @Test
179     public void testClose() {
180         final ListenerRegistration<EffectiveModelContextListener> reg = mock(ListenerRegistration.class);
181         doNothing().when(reg).close();
182         final DOMSchemaService schema = mock(DOMSchemaService.class);
183         doReturn(reg).when(schema).registerSchemaContextListener(any());
184
185         final DOMRpcRouter rpcRouter = new DOMRpcRouter(schema);
186         rpcRouter.close();
187
188         final DOMRpcProviderService svc = rpcRouter.getRpcProviderService();
189         assertThrows(RejectedExecutionException.class, () -> svc.registerRpcImplementation(getTestRpcImplementation(),
190             DOMRpcIdentifier.create(TestModel.TEST_QNAME, null)));
191     }
192
193     @Test
194     public void testActionInstanceRouting() throws ExecutionException {
195         try (DOMRpcRouter rpcRouter = new DOMRpcRouter()) {
196             rpcRouter.onModelContextUpdated(ACTIONS_CONTEXT);
197
198             final DOMActionProviderService actionProvider = rpcRouter.getActionProviderService();
199             assertNotNull(actionProvider);
200             final DOMActionService actionConsumer = rpcRouter.getActionService();
201             assertNotNull(actionConsumer);
202
203             try (ObjectRegistration<?> reg = actionProvider.registerActionImplementation(IMPL,
204                 DOMActionInstance.of(BAZ_TYPE, LogicalDatastoreType.OPERATIONAL, BAZ_PATH_GOOD))) {
205
206                 assertAvailable(actionConsumer, BAZ_PATH_GOOD);
207                 assertUnavailable(actionConsumer, BAZ_PATH_BAD);
208             }
209
210             assertUnavailable(actionConsumer, BAZ_PATH_BAD);
211             assertUnavailable(actionConsumer, BAZ_PATH_GOOD);
212         }
213     }
214
215     @Test
216     public void testActionDatastoreRouting() throws ExecutionException {
217         try (DOMRpcRouter rpcRouter = new DOMRpcRouter()) {
218             rpcRouter.onModelContextUpdated(ACTIONS_CONTEXT);
219
220             final DOMActionProviderService actionProvider = rpcRouter.getActionProviderService();
221             assertNotNull(actionProvider);
222             final DOMActionService actionConsumer = rpcRouter.getActionService();
223             assertNotNull(actionConsumer);
224
225             try (ObjectRegistration<?> reg = actionProvider.registerActionImplementation(IMPL,
226                 DOMActionInstance.of(BAZ_TYPE, LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.empty()))) {
227
228                 assertAvailable(actionConsumer, BAZ_PATH_GOOD);
229                 assertAvailable(actionConsumer, BAZ_PATH_BAD);
230             }
231
232             assertUnavailable(actionConsumer, BAZ_PATH_BAD);
233             assertUnavailable(actionConsumer, BAZ_PATH_GOOD);
234         }
235     }
236
237     private static void assertAvailable(final DOMActionService actionService, final YangInstanceIdentifier path) {
238         final DOMActionResult result;
239         try {
240             result = Futures.getDone(invokeBaz(actionService, path));
241         } catch (ExecutionException e) {
242             throw new AssertionError("Unexpected invocation failure", e);
243         }
244         assertEquals(List.of(), result.getErrors());
245     }
246
247     private static void assertUnavailable(final DOMActionService actionService, final YangInstanceIdentifier path) {
248         final ListenableFuture<? extends DOMActionResult> future = invokeBaz(actionService, path);
249         final ExecutionException ex = assertThrows(ExecutionException.class, () -> Futures.getDone(future));
250         assertThat(ex.getCause(), instanceOf(DOMActionNotAvailableException.class));
251     }
252
253     private static ListenableFuture<? extends DOMActionResult> invokeBaz(final DOMActionService actionService,
254             final YangInstanceIdentifier path) {
255         return actionService.invokeAction(BAZ_TYPE, new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, path),
256             Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(INPUT)).build());
257     }
258 }