Schema determination is asynchronous
[netconf.git] / plugins / netconf-client-mdsal / src / test / java / org / opendaylight / netconf / client / mdsal / NetconfDeviceTest.java
1 /*
2  * Copyright (c) 2014, 2015 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.netconf.client.mdsal;
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.assertTrue;
15 import static org.mockito.ArgumentMatchers.any;
16 import static org.mockito.ArgumentMatchers.anyCollection;
17 import static org.mockito.ArgumentMatchers.eq;
18 import static org.mockito.Mockito.after;
19 import static org.mockito.Mockito.doAnswer;
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.spy;
24 import static org.mockito.Mockito.timeout;
25 import static org.mockito.Mockito.times;
26 import static org.mockito.Mockito.verify;
27
28 import com.google.common.util.concurrent.Futures;
29 import com.google.common.util.concurrent.MoreExecutors;
30 import com.google.common.util.concurrent.SettableFuture;
31 import java.net.InetSocketAddress;
32 import java.util.ArrayList;
33 import java.util.Collection;
34 import java.util.HashMap;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.Set;
38 import org.junit.BeforeClass;
39 import org.junit.Test;
40 import org.junit.runner.RunWith;
41 import org.mockito.ArgumentCaptor;
42 import org.mockito.Mock;
43 import org.mockito.junit.MockitoJUnitRunner;
44 import org.opendaylight.mdsal.dom.api.DOMNotification;
45 import org.opendaylight.netconf.api.CapabilityURN;
46 import org.opendaylight.netconf.api.messages.NetconfMessage;
47 import org.opendaylight.netconf.api.xml.XmlUtil;
48 import org.opendaylight.netconf.client.mdsal.NetconfDevice.EmptySchemaContextException;
49 import org.opendaylight.netconf.client.mdsal.api.NetconfDeviceSchemasResolver;
50 import org.opendaylight.netconf.client.mdsal.api.NetconfSessionPreferences;
51 import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceHandler;
52 import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceId;
53 import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceServices;
54 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfState;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240120.connection.oper.available.capabilities.AvailableCapability;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240120.connection.oper.available.capabilities.AvailableCapability.CapabilityOrigin;
57 import org.opendaylight.yangtools.concepts.Registration;
58 import org.opendaylight.yangtools.yang.common.QName;
59 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
60 import org.opendaylight.yangtools.yang.model.api.source.SourceIdentifier;
61 import org.opendaylight.yangtools.yang.model.api.source.YangTextSource;
62 import org.opendaylight.yangtools.yang.model.repo.api.EffectiveModelContextFactory;
63 import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
64 import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
65 import org.opendaylight.yangtools.yang.model.repo.api.SchemaResolutionException;
66 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
67
68 @RunWith(MockitoJUnitRunner.StrictStubs.class)
69 public class NetconfDeviceTest extends AbstractTestModelTest {
70     public static final String TEST_NAMESPACE = "test:namespace";
71     public static final String TEST_MODULE = "test-module";
72     public static final String TEST_REVISION = "2013-07-22";
73     public static final SourceIdentifier TEST_SID = new SourceIdentifier(TEST_MODULE, TEST_REVISION);
74     public static final String TEST_CAPABILITY =
75             TEST_NAMESPACE + "?module=" + TEST_MODULE + "&revision=" + TEST_REVISION;
76
77     public static final SourceIdentifier TEST_SID2 = new SourceIdentifier(TEST_MODULE + "2", TEST_REVISION);
78     public static final String TEST_CAPABILITY2 =
79             TEST_NAMESPACE + "?module=" + TEST_MODULE + "2" + "&revision=" + TEST_REVISION;
80
81     private static final NetconfDeviceSchemasResolver STATE_SCHEMAS_RESOLVER =
82         (deviceRpc, remoteSessionCapabilities, id, schemaContext) -> Futures.immediateFuture(NetconfStateSchemas.EMPTY);
83
84     private static NetconfMessage NOTIFICATION;
85
86     @Mock
87     private SchemaSourceRegistry schemaRegistry;
88
89     @BeforeClass
90     public static final void setupNotification() throws Exception {
91         NOTIFICATION = new NetconfMessage(XmlUtil.readXmlToDocument(
92             NetconfDeviceTest.class.getResourceAsStream("/notification-payload.xml")));
93     }
94
95     @Test
96     public void testNetconfDeviceFlawedModelFailedResolution() throws Exception {
97         final RemoteDeviceHandler facade = getFacade();
98         final NetconfDeviceCommunicator listener = getListener();
99
100         final EffectiveModelContextFactory schemaFactory = getSchemaFactory();
101         final SchemaRepository schemaRepository = getSchemaRepository();
102
103         final SchemaResolutionException schemaResolutionException =
104                 new SchemaResolutionException("fail first", TEST_SID, new Throwable("YangTools parser fail"));
105         doAnswer(invocation -> {
106             if (((Collection<?>) invocation.getArguments()[0]).size() == 2) {
107                 return Futures.immediateFailedFuture(schemaResolutionException);
108             } else {
109                 return Futures.immediateFuture(SCHEMA_CONTEXT);
110             }
111         }).when(schemaFactory).createEffectiveModelContext(anyCollection());
112
113         final NetconfDeviceSchemasResolver stateSchemasResolver =
114             (deviceRpc, remoteSessionCapabilities, id, schemaContext) -> {
115                 final var first = SCHEMA_CONTEXT.getModules().iterator().next();
116                 final var qName = QName.create(first.getQNameModule(), first.getName());
117                 return Futures.immediateFuture(new NetconfStateSchemas(
118                     Set.of(qName, QName.create(first.getQNameModule(), "test-module2"))));
119             };
120
121         doReturn(mock(Registration.class)).when(schemaRegistry).registerSchemaSource(any(), any());
122         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = new NetconfDevice
123                 .SchemaResourcesDTO(schemaRegistry, schemaRepository, schemaFactory, stateSchemasResolver);
124
125         final NetconfDevice device = new NetconfDeviceBuilder()
126                 .setReconnectOnSchemasChange(true)
127                 .setSchemaResourcesDTO(schemaResourcesDTO)
128                 .setGlobalProcessingExecutor(MoreExecutors.directExecutor())
129                 .setId(getId())
130                 .setSalFacade(facade)
131                 .setBaseSchemas(BASE_SCHEMAS)
132                 .build();
133         // Monitoring supported
134         final NetconfSessionPreferences sessionCaps = getSessionCaps(true, List.of(TEST_CAPABILITY, TEST_CAPABILITY2));
135         device.onRemoteSessionUp(sessionCaps, listener);
136
137         verify(facade, timeout(5000)).onDeviceConnected(any(NetconfDeviceSchema.class),
138             any(NetconfSessionPreferences.class), any(RemoteDeviceServices.class));
139         verify(schemaFactory, times(2)).createEffectiveModelContext(anyCollection());
140     }
141
142     @Test
143     public void testNetconfDeviceFailFirstSchemaFailSecondEmpty() throws Exception {
144         final RemoteDeviceHandler facade = getFacade();
145         final NetconfDeviceCommunicator listener = getListener();
146
147         final EffectiveModelContextFactory schemaFactory = getSchemaFactory();
148         final SchemaRepository schemaRepository = getSchemaRepository();
149
150         // Make fallback attempt to fail due to empty resolved sources
151         final SchemaResolutionException schemaResolutionException = new SchemaResolutionException("fail first",
152             new SourceIdentifier("test-module", "2013-07-22"), new Throwable());
153         doReturn(Futures.immediateFailedFuture(schemaResolutionException))
154                 .when(schemaFactory).createEffectiveModelContext(anyCollection());
155
156         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = new NetconfDevice
157                 .SchemaResourcesDTO(schemaRegistry, schemaRepository, schemaFactory, STATE_SCHEMAS_RESOLVER);
158         final NetconfDevice device = new NetconfDeviceBuilder()
159                 .setReconnectOnSchemasChange(true)
160                 .setSchemaResourcesDTO(schemaResourcesDTO)
161                 .setGlobalProcessingExecutor(MoreExecutors.directExecutor())
162                 .setId(getId())
163                 .setSalFacade(facade)
164                 .setBaseSchemas(BASE_SCHEMAS)
165                 .build();
166
167         // Monitoring not supported
168         final NetconfSessionPreferences sessionCaps = getSessionCaps(false, List.of(TEST_CAPABILITY));
169         device.onRemoteSessionUp(sessionCaps, listener);
170
171         final var captor = ArgumentCaptor.forClass(Throwable.class);
172         verify(facade, timeout(5000)).onDeviceFailed(captor.capture());
173         assertThat(captor.getValue(), instanceOf(EmptySchemaContextException.class));
174
175         verify(listener, timeout(5000)).close();
176         verify(schemaFactory, times(1)).createEffectiveModelContext(anyCollection());
177     }
178
179     @Test
180     public void testNetconfDeviceMissingSource() throws Exception {
181         final RemoteDeviceHandler facade = getFacade();
182         final NetconfDeviceCommunicator listener = getListener();
183
184         final EffectiveModelContextFactory schemaFactory = getSchemaFactory();
185         final SchemaRepository schemaRepository = getSchemaRepository();
186
187         // Make fallback attempt to fail due to empty resolved sources
188         final MissingSchemaSourceException schemaResolutionException =
189                 new MissingSchemaSourceException(TEST_SID, "fail first");
190         doReturn(Futures.immediateFailedFuture(schemaResolutionException))
191                 .when(schemaRepository).getSchemaSource(eq(TEST_SID), eq(YangTextSource.class));
192         doAnswer(invocation -> {
193             if (invocation.getArgument(0, Collection.class).size() == 2) {
194                 return Futures.immediateFailedFuture(schemaResolutionException);
195             } else {
196                 return Futures.immediateFuture(SCHEMA_CONTEXT);
197             }
198         }).when(schemaFactory).createEffectiveModelContext(anyCollection());
199
200         final NetconfDeviceSchemasResolver stateSchemasResolver =
201             (deviceRpc, remoteSessionCapabilities, id, schemaContext) -> {
202                 final var first = SCHEMA_CONTEXT.getModules().iterator().next();
203                 final var qName = QName.create(first.getQNameModule(), first.getName());
204                 return Futures.immediateFuture(new NetconfStateSchemas(
205                     Set.of(qName, QName.create(first.getQNameModule(), "test-module2"))));
206             };
207
208         doReturn(mock(Registration.class)).when(schemaRegistry).registerSchemaSource(any(), any());
209         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = new NetconfDevice
210                 .SchemaResourcesDTO(schemaRegistry, schemaRepository, schemaFactory, stateSchemasResolver);
211
212         final NetconfDevice device = new NetconfDeviceBuilder()
213                 .setReconnectOnSchemasChange(true)
214                 .setSchemaResourcesDTO(schemaResourcesDTO)
215                 .setGlobalProcessingExecutor(MoreExecutors.directExecutor())
216                 .setBaseSchemas(BASE_SCHEMAS)
217                 .setId(getId())
218                 .setSalFacade(facade)
219                 .build();
220         // Monitoring supported
221         final NetconfSessionPreferences sessionCaps =
222                 getSessionCaps(true, List.of(TEST_CAPABILITY, TEST_CAPABILITY2));
223         device.onRemoteSessionUp(sessionCaps, listener);
224
225         verify(facade, timeout(5000)).onDeviceConnected(any(NetconfDeviceSchema.class),
226             any(NetconfSessionPreferences.class), any(RemoteDeviceServices.class));
227         verify(schemaFactory, times(1)).createEffectiveModelContext(anyCollection());
228     }
229
230     private static SchemaRepository getSchemaRepository() {
231         final SchemaRepository mock = mock(SchemaRepository.class);
232         final YangTextSource mockRep = mock(YangTextSource.class);
233         doReturn(Futures.immediateFuture(mockRep))
234                 .when(mock).getSchemaSource(any(SourceIdentifier.class), eq(YangTextSource.class));
235         return mock;
236     }
237
238     @Test
239     public void testNotificationBeforeSchema() throws Exception {
240         final RemoteDeviceHandler facade = getFacade();
241         final NetconfDeviceCommunicator listener = getListener();
242         final EffectiveModelContextFactory schemaContextProviderFactory = mock(EffectiveModelContextFactory.class);
243         final SettableFuture<SchemaContext> schemaFuture = SettableFuture.create();
244         doReturn(schemaFuture).when(schemaContextProviderFactory).createEffectiveModelContext(anyCollection());
245         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry,
246             getSchemaRepository(), schemaContextProviderFactory, STATE_SCHEMAS_RESOLVER);
247         final NetconfDevice device = new NetconfDeviceBuilder()
248                 .setReconnectOnSchemasChange(true)
249                 .setSchemaResourcesDTO(schemaResourcesDTO)
250                 .setGlobalProcessingExecutor(MoreExecutors.directExecutor())
251                 .setId(getId())
252                 .setSalFacade(facade)
253                 .setBaseSchemas(BASE_SCHEMAS)
254                 .build();
255
256         final NetconfSessionPreferences sessionCaps = getSessionCaps(true, List.of(TEST_CAPABILITY));
257         device.onRemoteSessionUp(sessionCaps, listener);
258
259         device.onNotification(NOTIFICATION);
260         device.onNotification(NOTIFICATION);
261         verify(facade, times(0)).onNotification(any(DOMNotification.class));
262
263         verify(facade, times(0)).onNotification(any(DOMNotification.class));
264         schemaFuture.set(NetconfToNotificationTest.getNotificationSchemaContext(getClass(), false));
265         verify(facade, timeout(10000).times(2)).onNotification(any(DOMNotification.class));
266
267         device.onNotification(NOTIFICATION);
268         verify(facade, timeout(10000).times(3)).onNotification(any(DOMNotification.class));
269     }
270
271     @Test
272     public void testNetconfDeviceReconnect() throws Exception {
273         final RemoteDeviceHandler facade = getFacade();
274         final NetconfDeviceCommunicator listener = getListener();
275
276         final EffectiveModelContextFactory schemaContextProviderFactory = getSchemaFactory();
277
278         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(
279                 schemaRegistry, getSchemaRepository(), schemaContextProviderFactory, STATE_SCHEMAS_RESOLVER);
280         final NetconfDevice device = new NetconfDeviceBuilder()
281                 .setReconnectOnSchemasChange(true)
282                 .setSchemaResourcesDTO(schemaResourcesDTO)
283                 .setGlobalProcessingExecutor(MoreExecutors.directExecutor())
284                 .setId(getId())
285                 .setSalFacade(facade)
286                 .setBaseSchemas(BASE_SCHEMAS)
287                 .build();
288         final NetconfSessionPreferences sessionCaps = getSessionCaps(true,
289                 List.of(TEST_NAMESPACE + "?module=" + TEST_MODULE + "&amp;revision=" + TEST_REVISION));
290         device.onRemoteSessionUp(sessionCaps, listener);
291
292         verify(schemaContextProviderFactory, timeout(5000)).createEffectiveModelContext(anyCollection());
293         verify(facade, timeout(5000)).onDeviceConnected(
294                 any(NetconfDeviceSchema.class), any(NetconfSessionPreferences.class), any(RemoteDeviceServices.class));
295
296         device.onRemoteSessionDown();
297         verify(facade, timeout(5000)).onDeviceDisconnected();
298
299         device.onRemoteSessionUp(sessionCaps, listener);
300
301         verify(schemaContextProviderFactory, timeout(5000).times(2)).createEffectiveModelContext(anyCollection());
302         verify(facade, timeout(5000).times(2)).onDeviceConnected(
303                 any(NetconfDeviceSchema.class), any(NetconfSessionPreferences.class), any(RemoteDeviceServices.class));
304     }
305
306     @Test
307     public void testNetconfDeviceDisconnectListenerCallCancellation() throws Exception {
308         final RemoteDeviceHandler facade = getFacade();
309         final NetconfDeviceCommunicator listener = getListener();
310         final EffectiveModelContextFactory schemaContextProviderFactory = mock(EffectiveModelContextFactory.class);
311         final SettableFuture<SchemaContext> schemaFuture = SettableFuture.create();
312         doReturn(schemaFuture).when(schemaContextProviderFactory).createEffectiveModelContext(anyCollection());
313         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry,
314             getSchemaRepository(), schemaContextProviderFactory, STATE_SCHEMAS_RESOLVER);
315         final NetconfDevice device = new NetconfDeviceBuilder()
316                 .setReconnectOnSchemasChange(true)
317                 .setSchemaResourcesDTO(schemaResourcesDTO)
318                 .setGlobalProcessingExecutor(MoreExecutors.directExecutor())
319                 .setId(getId())
320                 .setSalFacade(facade)
321                 .setBaseSchemas(BASE_SCHEMAS)
322                 .build();
323         final NetconfSessionPreferences sessionCaps = getSessionCaps(true,
324                 List.of(TEST_NAMESPACE + "?module=" + TEST_MODULE + "&amp;revision=" + TEST_REVISION));
325         //session up, start schema resolution
326         device.onRemoteSessionUp(sessionCaps, listener);
327         //session closed
328         device.onRemoteSessionDown();
329         verify(facade, timeout(5000)).onDeviceDisconnected();
330         //complete schema setup
331         schemaFuture.set(SCHEMA_CONTEXT);
332         //facade.onDeviceDisconnected() was called, so facade.onDeviceConnected() shouldn't be called anymore
333         verify(facade, after(1000).never()).onDeviceConnected(any(), any(), any(RemoteDeviceServices.class));
334     }
335
336     @Test
337     public void testNetconfDeviceReconnectBeforeSchemaSetup() throws Exception {
338         final RemoteDeviceHandler facade = getFacade();
339
340         final EffectiveModelContextFactory schemaContextProviderFactory = mock(EffectiveModelContextFactory.class);
341         final SettableFuture<SchemaContext> schemaFuture = SettableFuture.create();
342         doReturn(schemaFuture).when(schemaContextProviderFactory).createEffectiveModelContext(anyCollection());
343
344         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(
345             schemaRegistry, getSchemaRepository(), schemaContextProviderFactory, STATE_SCHEMAS_RESOLVER);
346         final NetconfDevice device = new NetconfDeviceBuilder()
347             .setReconnectOnSchemasChange(true)
348             .setSchemaResourcesDTO(schemaResourcesDTO)
349             .setGlobalProcessingExecutor(MoreExecutors.directExecutor())
350             .setId(getId())
351             .setSalFacade(facade)
352             .setBaseSchemas(BASE_SCHEMAS)
353             .build();
354         final NetconfSessionPreferences sessionCaps = getSessionCaps(true,
355             List.of(TEST_NAMESPACE + "?module=" + TEST_MODULE + "&amp;revision=" + TEST_REVISION));
356
357         final NetconfDeviceCommunicator listener = getListener();
358         // session up, start schema resolution
359         device.onRemoteSessionUp(sessionCaps, listener);
360         // session down
361         device.onRemoteSessionDown();
362         verify(facade, timeout(5000)).onDeviceDisconnected();
363         // session back up, start another schema resolution
364         device.onRemoteSessionUp(sessionCaps, listener);
365         // complete schema setup
366         schemaFuture.set(SCHEMA_CONTEXT);
367         // schema setup performed twice
368         verify(schemaContextProviderFactory, timeout(5000).times(2)).createEffectiveModelContext(anyCollection());
369         // onDeviceConnected called once
370         verify(facade, timeout(5000)).onDeviceConnected(
371             any(NetconfDeviceSchema.class), any(NetconfSessionPreferences.class), any(RemoteDeviceServices.class));
372     }
373
374     @Test
375     public void testNetconfDeviceAvailableCapabilitiesBuilding() throws Exception {
376         final RemoteDeviceHandler facade = getFacade();
377         final NetconfDeviceCommunicator listener = getListener();
378
379         final EffectiveModelContextFactory schemaContextProviderFactory = getSchemaFactory();
380
381         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry,
382             getSchemaRepository(), schemaContextProviderFactory, STATE_SCHEMAS_RESOLVER);
383         final NetconfDevice device = new NetconfDeviceBuilder()
384                 .setReconnectOnSchemasChange(true)
385                 .setSchemaResourcesDTO(schemaResourcesDTO)
386                 .setGlobalProcessingExecutor(MoreExecutors.directExecutor())
387                 .setId(getId())
388                 .setSalFacade(facade)
389                 .setBaseSchemas(BASE_SCHEMAS)
390                 .build();
391         final NetconfDevice netconfSpy = spy(device);
392
393         final NetconfSessionPreferences sessionCaps = getSessionCaps(true,
394                 List.of(TEST_NAMESPACE + "?module=" + TEST_MODULE + "&amp;revision=" + TEST_REVISION));
395         final Map<QName, CapabilityOrigin> moduleBasedCaps = new HashMap<>();
396         moduleBasedCaps.putAll(sessionCaps.moduleBasedCaps());
397         moduleBasedCaps
398                 .put(QName.create("(test:qname:side:loading)test"), CapabilityOrigin.UserDefined);
399
400         netconfSpy.onRemoteSessionUp(sessionCaps.replaceModuleCaps(moduleBasedCaps), listener);
401
402         final ArgumentCaptor<NetconfDeviceSchema> argument = ArgumentCaptor.forClass(NetconfDeviceSchema.class);
403         verify(facade, timeout(5000)).onDeviceConnected(argument.capture(), any(NetconfSessionPreferences.class),
404             any(RemoteDeviceServices.class));
405         argument.getValue().capabilities().resolvedCapabilities()
406                 .forEach(entry -> assertEquals("Builded 'AvailableCapability' schemas should match input capabilities.",
407                         moduleBasedCaps.get(
408                                 QName.create(entry.getCapability())).getName(), entry.getCapabilityOrigin().getName()));
409     }
410
411     @Test
412     public void testNetconfDeviceNotificationsModelNotPresentWithCapability() throws Exception {
413         final RemoteDeviceHandler facade = getFacade();
414         final NetconfDeviceCommunicator listener = getListener();
415         final EffectiveModelContextFactory schemaContextProviderFactory = getSchemaFactory();
416
417         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry,
418             getSchemaRepository(), schemaContextProviderFactory, STATE_SCHEMAS_RESOLVER);
419         final NetconfDevice device = new NetconfDeviceBuilder()
420                 .setSchemaResourcesDTO(schemaResourcesDTO)
421                 .setGlobalProcessingExecutor(MoreExecutors.directExecutor())
422                 .setId(getId())
423                 .setSalFacade(facade)
424                 .setBaseSchemas(BASE_SCHEMAS)
425                 .build();
426         final NetconfDevice netconfSpy = spy(device);
427
428         final NetconfSessionPreferences sessionCaps = getSessionCaps(false, List.of(CapabilityURN.NOTIFICATION));
429
430         netconfSpy.onRemoteSessionUp(sessionCaps, listener);
431
432         final ArgumentCaptor<NetconfDeviceSchema> argument = ArgumentCaptor.forClass(NetconfDeviceSchema.class);
433         verify(facade, timeout(5000)).onDeviceConnected(argument.capture(), any(NetconfSessionPreferences.class),
434                 any(RemoteDeviceServices.class));
435
436         List<String> notificationModulesName = List.of(
437                 org.opendaylight.yang.svc.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714
438                         .YangModuleInfoImpl.getInstance().getName().toString(),
439                 org.opendaylight.yang.svc.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715
440                         .YangModuleInfoImpl.getInstance().getName().toString());
441
442         final Set<AvailableCapability> resolvedCapabilities = argument.getValue().capabilities().resolvedCapabilities();
443
444         assertEquals(2, resolvedCapabilities.size());
445         assertTrue(resolvedCapabilities.stream().anyMatch(entry -> notificationModulesName
446                 .contains(entry.getCapability())));
447     }
448
449     @Test
450     public void testNetconfDeviceNotificationsCapabilityIsNotPresent() throws Exception {
451         final RemoteDeviceHandler facade = getFacade();
452         final NetconfDeviceCommunicator listener = getListener();
453         final EffectiveModelContextFactory schemaContextProviderFactory = getSchemaFactory();
454
455         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry,
456             getSchemaRepository(), schemaContextProviderFactory, STATE_SCHEMAS_RESOLVER);
457         final NetconfDevice device = new NetconfDeviceBuilder()
458                 .setSchemaResourcesDTO(schemaResourcesDTO)
459                 .setGlobalProcessingExecutor(MoreExecutors.directExecutor())
460                 .setId(getId())
461                 .setSalFacade(facade)
462                 .setBaseSchemas(BASE_SCHEMAS)
463                 .build();
464         final NetconfDevice netconfSpy = spy(device);
465
466         final NetconfSessionPreferences sessionCaps = getSessionCaps(false,
467                 List.of(TEST_NAMESPACE + "?module=" + TEST_MODULE + "&amp;revision=" + TEST_REVISION));
468
469         netconfSpy.onRemoteSessionUp(sessionCaps, listener);
470
471         final ArgumentCaptor<NetconfDeviceSchema> argument = ArgumentCaptor.forClass(NetconfDeviceSchema.class);
472         verify(facade, timeout(5000)).onDeviceConnected(argument.capture(), any(NetconfSessionPreferences.class),
473                 any(RemoteDeviceServices.class));
474         final NetconfDeviceCapabilities netconfDeviceCaps = argument.getValue().capabilities();
475
476         List<String> notificationModulesName = List.of(
477                 org.opendaylight.yang.svc.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714
478                         .YangModuleInfoImpl.getInstance().getName().toString(),
479                 org.opendaylight.yang.svc.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715
480                         .YangModuleInfoImpl.getInstance().getName().toString());
481
482         assertFalse(netconfDeviceCaps.resolvedCapabilities().stream()
483             .anyMatch(entry -> notificationModulesName.contains(entry.getCapability())));
484     }
485
486     @Test
487     public void testNetconfDeviceNotificationsModelIsPresent() throws Exception {
488         final RemoteDeviceHandler facade = getFacade();
489         final NetconfDeviceCommunicator listener = getListener();
490         final EffectiveModelContextFactory schemaContextProviderFactory = getSchemaFactory();
491
492         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry,
493             getSchemaRepository(), schemaContextProviderFactory, STATE_SCHEMAS_RESOLVER);
494         final NetconfDevice device = new NetconfDeviceBuilder()
495                 .setSchemaResourcesDTO(schemaResourcesDTO)
496                 .setGlobalProcessingExecutor(MoreExecutors.directExecutor())
497                 .setId(getId())
498                 .setSalFacade(facade)
499                 .setBaseSchemas(BASE_SCHEMAS)
500                 .build();
501         final NetconfDevice netconfSpy = spy(device);
502
503         final NetconfSessionPreferences sessionCaps = getSessionCaps(false, List.of());
504
505         final var moduleBasedCaps = new HashMap<QName, CapabilityOrigin>();
506         moduleBasedCaps.put(org.opendaylight.yang.svc.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714
507                         .YangModuleInfoImpl.getInstance().getName(),
508                 CapabilityOrigin.DeviceAdvertised);
509         moduleBasedCaps.put(org.opendaylight.yang.svc.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715
510                         .YangModuleInfoImpl.getInstance().getName(),
511                 CapabilityOrigin.DeviceAdvertised);
512
513
514         netconfSpy.onRemoteSessionUp(sessionCaps.replaceModuleCaps(moduleBasedCaps), listener);
515
516         final var argument = ArgumentCaptor.forClass(NetconfDeviceSchema.class);
517         verify(facade, timeout(5000)).onDeviceConnected(argument.capture(), any(NetconfSessionPreferences.class),
518                 any(RemoteDeviceServices.class));
519         final Set<AvailableCapability> resolvedCapabilities = argument.getValue().capabilities().resolvedCapabilities();
520
521         final var notificationModulesName = List.of(
522                 org.opendaylight.yang.svc.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714
523                         .YangModuleInfoImpl.getInstance().getName().toString(),
524                 org.opendaylight.yang.svc.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715
525                         .YangModuleInfoImpl.getInstance().getName().toString());
526
527         assertEquals(2, resolvedCapabilities.size());
528         assertTrue(resolvedCapabilities.stream().anyMatch(entry -> notificationModulesName
529                 .contains(entry.getCapability())));
530     }
531
532     private static EffectiveModelContextFactory getSchemaFactory() throws Exception {
533         final EffectiveModelContextFactory schemaFactory = mock(EffectiveModelContextFactory.class);
534         doReturn(Futures.immediateFuture(SCHEMA_CONTEXT))
535                 .when(schemaFactory).createEffectiveModelContext(anyCollection());
536         return schemaFactory;
537     }
538
539     private static RemoteDeviceHandler getFacade() throws Exception {
540         final RemoteDeviceHandler remoteDeviceHandler = mockCloseableClass(RemoteDeviceHandler.class);
541         doNothing().when(remoteDeviceHandler).onDeviceConnected(
542                 any(NetconfDeviceSchema.class), any(NetconfSessionPreferences.class), any(RemoteDeviceServices.class));
543         doNothing().when(remoteDeviceHandler).onDeviceDisconnected();
544         doNothing().when(remoteDeviceHandler).onNotification(any(DOMNotification.class));
545         return remoteDeviceHandler;
546     }
547
548     private static <T extends AutoCloseable> T mockCloseableClass(final Class<T> remoteDeviceHandlerClass)
549             throws Exception {
550         final T mock = mock(remoteDeviceHandlerClass);
551         doNothing().when(mock).close();
552         return mock;
553     }
554
555     public RemoteDeviceId getId() {
556         return new RemoteDeviceId("test-D", InetSocketAddress.createUnresolved("localhost", 22));
557     }
558
559     public NetconfSessionPreferences getSessionCaps(final boolean addMonitor,
560                                                     final Collection<String> additionalCapabilities) {
561         final var capabilities = new ArrayList<String>();
562         capabilities.add(CapabilityURN.BASE);
563         capabilities.add(CapabilityURN.BASE_1_1);
564         if (addMonitor) {
565             capabilities.add(NetconfState.QNAME.getNamespace().toString());
566         }
567         capabilities.addAll(additionalCapabilities);
568         return NetconfSessionPreferences.fromStrings(capabilities);
569     }
570
571     public NetconfDeviceCommunicator getListener() throws Exception {
572         return mockCloseableClass(NetconfDeviceCommunicator.class);
573     }
574 }