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