Proxy DOMRpcRouter to the mdsal implementation
[controller.git] / opendaylight / md-sal / sal-dom-broker / src / test / java / org / opendaylight / controller / md / sal / dom / broker / impl / DOMRpcRouterTest.java
1 /*
2  * Copyright (c) 2018 Inocybe Technologies 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.controller.md.sal.dom.broker.impl;
9
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertNotNull;
12 import static org.junit.Assert.assertNull;
13 import static org.junit.Assert.assertSame;
14 import static org.junit.Assert.assertTrue;
15 import static org.junit.Assert.fail;
16 import static org.mockito.Matchers.any;
17 import static org.mockito.Mockito.after;
18 import static org.mockito.Mockito.doNothing;
19 import static org.mockito.Mockito.doReturn;
20 import static org.mockito.Mockito.mock;
21 import static org.mockito.Mockito.never;
22 import static org.mockito.Mockito.reset;
23 import static org.mockito.Mockito.timeout;
24 import static org.mockito.Mockito.verify;
25
26 import com.google.common.collect.ImmutableList;
27 import com.google.common.util.concurrent.CheckedFuture;
28 import com.google.common.util.concurrent.Futures;
29 import com.google.common.util.concurrent.ListenableFuture;
30 import java.util.AbstractMap.SimpleEntry;
31 import java.util.Collections;
32 import java.util.Map.Entry;
33 import java.util.concurrent.ExecutionException;
34 import org.junit.Before;
35 import org.junit.Test;
36 import org.opendaylight.controller.md.sal.dom.api.DOMRpcAvailabilityListener;
37 import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
38 import org.opendaylight.controller.md.sal.dom.api.DOMRpcIdentifier;
39 import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementation;
40 import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationNotAvailableException;
41 import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationRegistration;
42 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
43 import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult;
44 import org.opendaylight.controller.md.sal.dom.store.impl.TestModel;
45 import org.opendaylight.yangtools.concepts.ListenerRegistration;
46 import org.opendaylight.yangtools.yang.common.QName;
47 import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
48 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
49 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
50 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
51 import org.opendaylight.yangtools.yang.model.api.Module;
52 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
53 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
54 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
55
56 /**
57  * Unit tests for DOMRpcRouter.
58  *
59  * @author Thomas Pantelis
60  */
61 public class DOMRpcRouterTest {
62
63     private static final NormalizedNode<?, ?> RPC_INPUT = ImmutableNodes.leafNode(
64             QName.create(TestModel.TEST_QNAME.getModule(), "input-leaf"), "foo");
65     private static final NormalizedNode<?, ?> RPC_OUTPUT = ImmutableNodes.leafNode(
66             QName.create(TestModel.TEST_QNAME.getModule(), "output-leaf"), "bar");
67     private final TestLegacyDOMRpcImplementation testLegacyRpcImpl = new TestLegacyDOMRpcImplementation();
68     private final TestMdsalDOMRpcImplementation testMdsalRpcImpl = new TestMdsalDOMRpcImplementation();
69     private org.opendaylight.mdsal.dom.broker.DOMRpcRouter mdsalRpcRouter;
70     private DOMRpcRouter legacyRpcRouter;
71     private DOMRpcIdentifier legacyTestRpcIdentifier;
72     private DOMRpcIdentifier legacyTestRpcNoInputIdentifier;
73     private org.opendaylight.mdsal.dom.api.DOMRpcIdentifier mdsalTestRpcIdentifier;
74     private org.opendaylight.mdsal.dom.api.DOMRpcIdentifier mdsalTestRpcNoInputIdentifier;
75
76     @Before
77     public void setup() {
78         mdsalRpcRouter = new org.opendaylight.mdsal.dom.broker.DOMRpcRouter();
79         final SchemaContext schemaContext = TestModel.createTestContext();
80         mdsalRpcRouter.onGlobalContextUpdated(schemaContext);
81         legacyRpcRouter = new DOMRpcRouter(mdsalRpcRouter, mdsalRpcRouter);
82
83         legacyTestRpcIdentifier = DOMRpcIdentifier.create(findRpc(schemaContext, "test-rpc"));
84         legacyTestRpcNoInputIdentifier = DOMRpcIdentifier.create(findRpc(schemaContext, "test-rpc-no-input"));
85         mdsalTestRpcIdentifier = org.opendaylight.mdsal.dom.api.DOMRpcIdentifier.create(
86                 findRpc(schemaContext, "test-rpc"));
87         mdsalTestRpcNoInputIdentifier = org.opendaylight.mdsal.dom.api.DOMRpcIdentifier.create(
88                 findRpc(schemaContext, "test-rpc-no-input"));
89     }
90
91     @Test
92     public void testLegacyRegistrationAndInvocation() throws InterruptedException, ExecutionException {
93         final DOMRpcImplementationRegistration<TestLegacyDOMRpcImplementation> reg =
94             legacyRpcRouter.registerRpcImplementation(testLegacyRpcImpl, legacyTestRpcIdentifier,
95                     legacyTestRpcNoInputIdentifier);
96
97         // Test success
98
99         DefaultDOMRpcResult result = new DefaultDOMRpcResult(RPC_OUTPUT);
100         testLegacyRpcImpl.init(Futures.immediateCheckedFuture(result));
101
102         ListenableFuture<DOMRpcResult> future = legacyRpcRouter.invokeRpc(legacyTestRpcIdentifier.getType(), RPC_INPUT);
103
104         assertSame(result, future.get());
105         testLegacyRpcImpl.verifyInput(legacyTestRpcIdentifier, RPC_INPUT);
106
107         // Test exception returned
108
109         TestLegacyDOMRpcException rpcEx = new TestLegacyDOMRpcException();
110         testLegacyRpcImpl.init(Futures.immediateFailedCheckedFuture(rpcEx));
111
112         try {
113             legacyRpcRouter.invokeRpc(legacyTestRpcIdentifier.getType(), RPC_INPUT).get();
114             fail("Expected exception");
115         } catch (ExecutionException e) {
116             assertEquals(rpcEx, e.getCause());
117         }
118
119         // Test no input or output
120
121         testLegacyRpcImpl.init(Futures.immediateCheckedFuture(null));
122
123         future = legacyRpcRouter.invokeRpc(legacyTestRpcNoInputIdentifier.getType(), null);
124
125         assertNull(future.get());
126         testLegacyRpcImpl.verifyInput(legacyTestRpcNoInputIdentifier, null);
127
128         // Test close
129
130         reg.close();
131
132         try {
133             legacyRpcRouter.invokeRpc(legacyTestRpcIdentifier.getType(), RPC_INPUT).get();
134             fail("Expected exception");
135         } catch (ExecutionException e) {
136             assertTrue(e.getCause() instanceof DOMRpcImplementationNotAvailableException);
137         }
138     }
139
140     @Test
141     public void testLegacyRegistrationAndMdsalInvocation() throws InterruptedException, ExecutionException {
142         legacyRpcRouter.registerRpcImplementation(testLegacyRpcImpl, legacyTestRpcIdentifier,
143                 legacyTestRpcNoInputIdentifier);
144
145         // Test success
146
147         DefaultDOMRpcResult result = new DefaultDOMRpcResult(RPC_OUTPUT,
148                 Collections.singleton(RpcResultBuilder.newError(ErrorType.RPC, "tag", "message")));
149         testLegacyRpcImpl.init(Futures.immediateCheckedFuture(result));
150
151         ListenableFuture<org.opendaylight.mdsal.dom.api.DOMRpcResult> future =
152                 mdsalRpcRouter.invokeRpc(mdsalTestRpcIdentifier.getType(), RPC_INPUT);
153
154         assertEquals(RPC_OUTPUT, future.get().getResult());
155         assertEquals(1, future.get().getErrors().size());
156         assertEquals(ErrorType.RPC, future.get().getErrors().iterator().next().getErrorType());
157         assertEquals("tag", future.get().getErrors().iterator().next().getTag());
158         assertEquals("message", future.get().getErrors().iterator().next().getMessage());
159         testLegacyRpcImpl.verifyInput(legacyTestRpcIdentifier, RPC_INPUT);
160
161         // Test exception returned
162
163         TestLegacyDOMRpcException rpcEx = new TestLegacyDOMRpcException();
164         testLegacyRpcImpl.init(Futures.immediateFailedCheckedFuture(rpcEx));
165
166         try {
167             mdsalRpcRouter.invokeRpc(mdsalTestRpcIdentifier.getType(), RPC_INPUT).get();
168             fail("Expected exception");
169         } catch (ExecutionException e) {
170             assertEquals(rpcEx, e.getCause());
171         }
172
173         // Test no input or output
174
175         testLegacyRpcImpl.init(Futures.immediateCheckedFuture(null));
176
177         future = mdsalRpcRouter.invokeRpc(mdsalTestRpcNoInputIdentifier.getType(), null);
178
179         assertNull(future.get());
180         testLegacyRpcImpl.verifyInput(legacyTestRpcNoInputIdentifier, null);
181     }
182
183     @Test
184     public void testMdsalRegistrationAndLegacyInvocation() throws InterruptedException, ExecutionException {
185         mdsalRpcRouter.registerRpcImplementation(testMdsalRpcImpl, mdsalTestRpcIdentifier,
186                 mdsalTestRpcNoInputIdentifier);
187
188         // Test success
189
190         org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult result =
191             new org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult(RPC_OUTPUT,
192                 Collections.singleton(RpcResultBuilder.newError(ErrorType.RPC, "tag", "message")));
193         testMdsalRpcImpl.init(Futures.immediateCheckedFuture(result));
194
195         ListenableFuture<DOMRpcResult> future = legacyRpcRouter.invokeRpc(legacyTestRpcIdentifier.getType(), RPC_INPUT);
196
197         assertEquals(RPC_OUTPUT, future.get().getResult());
198         assertEquals(1, future.get().getErrors().size());
199         assertEquals(ErrorType.RPC, future.get().getErrors().iterator().next().getErrorType());
200         assertEquals("tag", future.get().getErrors().iterator().next().getTag());
201         assertEquals("message", future.get().getErrors().iterator().next().getMessage());
202         testMdsalRpcImpl.verifyInput(mdsalTestRpcIdentifier, RPC_INPUT);
203
204         // Test exception returned
205
206         TestMdsalDOMRpcException rpcEx = new TestMdsalDOMRpcException();
207         testMdsalRpcImpl.init(Futures.immediateFailedCheckedFuture(rpcEx));
208
209         try {
210             legacyRpcRouter.invokeRpc(legacyTestRpcIdentifier.getType(), RPC_INPUT).get();
211             fail("Expected exception");
212         } catch (ExecutionException e) {
213             assertTrue("Unexpected exception " + e.getCause(), e.getCause() instanceof DOMRpcException);
214             assertEquals(rpcEx, e.getCause().getCause());
215         }
216
217         // Test no input or output
218
219         testMdsalRpcImpl.init(Futures.immediateCheckedFuture(null));
220
221         future = legacyRpcRouter.invokeRpc(legacyTestRpcNoInputIdentifier.getType(), null);
222
223         assertNull(future.get());
224         testMdsalRpcImpl.verifyInput(mdsalTestRpcNoInputIdentifier, null);
225     }
226
227     @Test
228     public void testRegisterRpcListener() {
229         final TestLegacyDOMRpcImplementation2 testRpcImpl2 = new TestLegacyDOMRpcImplementation2();
230
231         DOMRpcAvailabilityListener listener = mock(DOMRpcAvailabilityListener.class);
232         doNothing().when(listener).onRpcAvailable(any());
233         doNothing().when(listener).onRpcUnavailable(any());
234         doReturn(true).when(listener).acceptsImplementation(any());
235         final ListenerRegistration<?> listenerReg = legacyRpcRouter.registerRpcListener(listener);
236
237         DOMRpcAvailabilityListener filteredListener = mock(DOMRpcAvailabilityListener.class);
238         doNothing().when(filteredListener).onRpcAvailable(any());
239         doNothing().when(filteredListener).onRpcUnavailable(any());
240         doReturn(true).when(filteredListener).acceptsImplementation(testLegacyRpcImpl);
241         doReturn(false).when(filteredListener).acceptsImplementation(testRpcImpl2);
242         final ListenerRegistration<?> filteredListenerReg = legacyRpcRouter.registerRpcListener(filteredListener);
243
244         final DOMRpcImplementationRegistration<?> testRpcReg =
245                 legacyRpcRouter.registerRpcImplementation(testLegacyRpcImpl, legacyTestRpcIdentifier);
246
247         verify(listener, timeout(5000)).onRpcAvailable(ImmutableList.of(legacyTestRpcIdentifier));
248         verify(filteredListener, timeout(5000)).onRpcAvailable(ImmutableList.of(legacyTestRpcIdentifier));
249
250         final DOMRpcImplementationRegistration<?> testRpcNoInputReg =
251                 legacyRpcRouter.registerRpcImplementation(testRpcImpl2, legacyTestRpcNoInputIdentifier);
252
253         verify(listener, timeout(5000)).onRpcAvailable(ImmutableList.of(legacyTestRpcNoInputIdentifier));
254         verify(filteredListener, after(200).never()).onRpcAvailable(ImmutableList.of(legacyTestRpcNoInputIdentifier));
255
256         testRpcReg.close();
257
258         verify(listener, timeout(5000)).onRpcUnavailable(ImmutableList.of(legacyTestRpcIdentifier));
259         verify(filteredListener, timeout(5000)).onRpcUnavailable(ImmutableList.of(legacyTestRpcIdentifier));
260
261         testRpcNoInputReg.close();
262
263         verify(listener, timeout(5000)).onRpcUnavailable(ImmutableList.of(legacyTestRpcNoInputIdentifier));
264         verify(filteredListener, after(200).never()).onRpcUnavailable(ImmutableList.of(legacyTestRpcNoInputIdentifier));
265
266         reset(listener, filteredListener);
267
268         listenerReg.close();
269         filteredListenerReg.close();
270
271         legacyRpcRouter.registerRpcImplementation(testLegacyRpcImpl, legacyTestRpcIdentifier);
272
273         verify(listener, after(200).never()).onRpcAvailable(ImmutableList.of(legacyTestRpcIdentifier));
274         verify(filteredListener, never()).onRpcAvailable(ImmutableList.of(legacyTestRpcIdentifier));
275     }
276
277     private static SchemaPath findRpc(SchemaContext schemaContext, String name) {
278         Module testModule = schemaContext.findModule("odl-datastore-test", TestModel.TEST_QNAME.getRevision()).get();
279         RpcDefinition rpcDefinition = null;
280         for (RpcDefinition def: testModule.getRpcs()) {
281             if (def.getQName().getLocalName().equals(name)) {
282                 rpcDefinition = def;
283                 break;
284             }
285         }
286
287         assertNotNull(name + " rpc not found in " + testModule.getRpcs(), rpcDefinition);
288         return rpcDefinition.getPath();
289     }
290
291     private abstract static class AbstractDOMRpcImplementation<T> {
292         Entry<T, NormalizedNode<?, ?>> rpcInput;
293
294         void verifyInput(T expRpc, NormalizedNode<?, ?> expInput) {
295             assertNotNull(rpcInput);
296             assertEquals(expRpc, rpcInput.getKey());
297             assertEquals(expInput, rpcInput.getValue());
298         }
299     }
300
301     private static class TestLegacyDOMRpcImplementation extends AbstractDOMRpcImplementation<DOMRpcIdentifier>
302             implements DOMRpcImplementation {
303         CheckedFuture<DOMRpcResult, DOMRpcException> returnFuture;
304
305         @Override
306         public CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(
307                 final DOMRpcIdentifier rpc, final NormalizedNode<?, ?> input) {
308             rpcInput = new SimpleEntry<>(rpc, input);
309             return returnFuture;
310         }
311
312         void init(CheckedFuture<DOMRpcResult, DOMRpcException> retFuture) {
313             this.returnFuture = retFuture;
314             rpcInput = null;
315         }
316     }
317
318     private static class TestMdsalDOMRpcImplementation
319             extends AbstractDOMRpcImplementation<org.opendaylight.mdsal.dom.api.DOMRpcIdentifier>
320             implements org.opendaylight.mdsal.dom.api.DOMRpcImplementation {
321         CheckedFuture<org.opendaylight.mdsal.dom.api.DOMRpcResult,
322                 org.opendaylight.mdsal.dom.api.DOMRpcException> returnFuture;
323
324         @Override
325         public CheckedFuture<org.opendaylight.mdsal.dom.api.DOMRpcResult,
326                 org.opendaylight.mdsal.dom.api.DOMRpcException> invokeRpc(
327                     final org.opendaylight.mdsal.dom.api.DOMRpcIdentifier rpc, final NormalizedNode<?, ?> input) {
328             rpcInput = new SimpleEntry<>(rpc, input);
329             return returnFuture;
330         }
331
332         void init(CheckedFuture<org.opendaylight.mdsal.dom.api.DOMRpcResult,
333                 org.opendaylight.mdsal.dom.api.DOMRpcException> retFuture) {
334             this.returnFuture = retFuture;
335             rpcInput = null;
336         }
337     }
338
339     private static class TestLegacyDOMRpcImplementation2 implements DOMRpcImplementation {
340         @Override
341         public CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(
342                 final DOMRpcIdentifier rpc, final NormalizedNode<?, ?> input) {
343             return null;
344         }
345     }
346
347     private static class TestLegacyDOMRpcException extends DOMRpcException {
348         private static final long serialVersionUID = 1L;
349
350         TestLegacyDOMRpcException() {
351             super("test");
352         }
353     }
354
355     private static class TestMdsalDOMRpcException extends org.opendaylight.mdsal.dom.api.DOMRpcException {
356         private static final long serialVersionUID = 1L;
357
358         TestMdsalDOMRpcException() {
359             super("test");
360         }
361     }
362 }