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