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