Prevent NPE for Credentials
[netconf.git] / plugins / netconf-client-mdsal / src / test / java / org / opendaylight / netconf / client / mdsal / NetconfDeviceTest.java
index a95eed54b784b1b8ef01ee7030ee60f5805df57e..0f73d5b850e17dd3f522da94f9647fe01dd63493 100644 (file)
@@ -7,16 +7,12 @@
  */
 package org.opendaylight.netconf.client.mdsal;
 
-import static org.hamcrest.CoreMatchers.instanceOf;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyCollection;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.after;
-import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
@@ -25,277 +21,168 @@ import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
-import com.google.common.collect.Sets;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.MoreExecutors;
 import com.google.common.util.concurrent.SettableFuture;
 import java.net.InetSocketAddress;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnitRunner;
+import org.mockito.junit.jupiter.MockitoExtension;
 import org.opendaylight.mdsal.dom.api.DOMNotification;
 import org.opendaylight.netconf.api.CapabilityURN;
 import org.opendaylight.netconf.api.messages.NetconfMessage;
 import org.opendaylight.netconf.api.xml.XmlUtil;
 import org.opendaylight.netconf.client.mdsal.NetconfDevice.EmptySchemaContextException;
-import org.opendaylight.netconf.client.mdsal.api.NetconfDeviceSchemasResolver;
+import org.opendaylight.netconf.client.mdsal.api.DeviceNetconfSchema;
+import org.opendaylight.netconf.client.mdsal.api.DeviceNetconfSchemaProvider;
 import org.opendaylight.netconf.client.mdsal.api.NetconfSessionPreferences;
 import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceHandler;
 import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceId;
 import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceServices;
+import org.opendaylight.netconf.client.mdsal.impl.DefaultDeviceNetconfSchemaProvider;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfState;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240120.connection.oper.available.capabilities.AvailableCapability;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240120.connection.oper.available.capabilities.AvailableCapability.CapabilityOrigin;
-import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240611.connection.oper.available.capabilities.AvailableCapability.CapabilityOrigin;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240611.connection.oper.available.capabilities.AvailableCapabilityBuilder;
 import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.model.api.Module;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 import org.opendaylight.yangtools.yang.model.api.source.SourceIdentifier;
 import org.opendaylight.yangtools.yang.model.api.source.YangTextSource;
 import org.opendaylight.yangtools.yang.model.repo.api.EffectiveModelContextFactory;
-import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaResolutionException;
 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
 
-@RunWith(MockitoJUnitRunner.StrictStubs.class)
-public class NetconfDeviceTest extends AbstractTestModelTest {
-    public static final String TEST_NAMESPACE = "test:namespace";
-    public static final String TEST_MODULE = "test-module";
-    public static final String TEST_REVISION = "2013-07-22";
-    public static final SourceIdentifier TEST_SID = new SourceIdentifier(TEST_MODULE, TEST_REVISION);
-    public static final String TEST_CAPABILITY =
+@ExtendWith(MockitoExtension.class)
+class NetconfDeviceTest extends AbstractTestModelTest {
+    private static final String TEST_NAMESPACE = "test:namespace";
+    private static final String TEST_MODULE = "test-module";
+    private static final String TEST_REVISION = "2013-07-22";
+    private static final SourceIdentifier TEST_SID = new SourceIdentifier(TEST_MODULE, TEST_REVISION);
+    private static final String TEST_CAPABILITY =
             TEST_NAMESPACE + "?module=" + TEST_MODULE + "&revision=" + TEST_REVISION;
 
-    public static final SourceIdentifier TEST_SID2 = new SourceIdentifier(TEST_MODULE + "2", TEST_REVISION);
-    public static final String TEST_CAPABILITY2 =
-            TEST_NAMESPACE + "?module=" + TEST_MODULE + "2" + "&revision=" + TEST_REVISION;
-
-    private static final NetconfDeviceSchemasResolver STATE_SCHEMAS_RESOLVER =
-        (deviceRpc, remoteSessionCapabilities, id, schemaContext) -> NetconfStateSchemas.EMPTY;
-
     private static NetconfMessage NOTIFICATION;
 
     @Mock
     private SchemaSourceRegistry schemaRegistry;
+    @Mock
+    private DeviceNetconfSchemaProvider schemaProvider;
+    @Mock
+    private RemoteDeviceHandler facade;
+    @Mock
+    private NetconfDeviceCommunicator listener;
+    @Mock
+    private EffectiveModelContextFactory schemaFactory;
+    @Mock
+    private DeviceNetconfSchemaProvider deviceSchemaProvider;
 
-    @BeforeClass
-    public static final void setupNotification() throws Exception {
+    @BeforeAll
+    static void setupNotification() throws Exception {
         NOTIFICATION = new NetconfMessage(XmlUtil.readXmlToDocument(
             NetconfDeviceTest.class.getResourceAsStream("/notification-payload.xml")));
     }
 
     @Test
-    public void testNetconfDeviceFlawedModelFailedResolution() throws Exception {
-        final RemoteDeviceHandler facade = getFacade();
-        final NetconfDeviceCommunicator listener = getListener();
-
-        final EffectiveModelContextFactory schemaFactory = getSchemaFactory();
-        final SchemaRepository schemaRepository = getSchemaRepository();
-
-        final SchemaResolutionException schemaResolutionException =
-                new SchemaResolutionException("fail first", TEST_SID, new Throwable("YangTools parser fail"));
-        doAnswer(invocation -> {
-            if (((Collection<?>) invocation.getArguments()[0]).size() == 2) {
-                return Futures.immediateFailedFuture(schemaResolutionException);
-            } else {
-                return Futures.immediateFuture(SCHEMA_CONTEXT);
-            }
-        }).when(schemaFactory).createEffectiveModelContext(anyCollection());
-
-        final NetconfDeviceSchemasResolver stateSchemasResolver = (deviceRpc, remoteSessionCapabilities, id,
-                schemaContext) -> {
-            final Module first = SCHEMA_CONTEXT.getModules().iterator().next();
-            final QName qName = QName.create(first.getQNameModule(), first.getName());
-            final NetconfStateSchemas.RemoteYangSchema source1 = new NetconfStateSchemas.RemoteYangSchema(qName);
-            final NetconfStateSchemas.RemoteYangSchema source2 =
-                    new NetconfStateSchemas.RemoteYangSchema(QName.create(first.getQNameModule(), "test-module2"));
-            return new NetconfStateSchemas(Sets.newHashSet(source1, source2));
-        };
-
-        doReturn(mock(Registration.class)).when(schemaRegistry).registerSchemaSource(any(), any());
-        final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = new NetconfDevice
-                .SchemaResourcesDTO(schemaRegistry, schemaRepository, schemaFactory, stateSchemasResolver);
-
-        final NetconfDevice device = new NetconfDeviceBuilder()
-                .setReconnectOnSchemasChange(true)
-                .setSchemaResourcesDTO(schemaResourcesDTO)
-                .setGlobalProcessingExecutor(MoreExecutors.directExecutor())
-                .setId(getId())
-                .setSalFacade(facade)
-                .setBaseSchemas(BASE_SCHEMAS)
-                .build();
-        // Monitoring supported
-        final NetconfSessionPreferences sessionCaps = getSessionCaps(true, List.of(TEST_CAPABILITY, TEST_CAPABILITY2));
-        device.onRemoteSessionUp(sessionCaps, listener);
-
-        verify(facade, timeout(5000)).onDeviceConnected(any(NetconfDeviceSchema.class),
-            any(NetconfSessionPreferences.class), any(RemoteDeviceServices.class));
-        verify(schemaFactory, times(2)).createEffectiveModelContext(anyCollection());
-    }
-
-    @Test
-    public void testNetconfDeviceFailFirstSchemaFailSecondEmpty() throws Exception {
-        final RemoteDeviceHandler facade = getFacade();
-        final NetconfDeviceCommunicator listener = getListener();
-
-        final EffectiveModelContextFactory schemaFactory = getSchemaFactory();
-        final SchemaRepository schemaRepository = getSchemaRepository();
-
+    void testNetconfDeviceFailFirstSchemaFailSecondEmpty() {
         // Make fallback attempt to fail due to empty resolved sources
-        final SchemaResolutionException schemaResolutionException = new SchemaResolutionException("fail first",
+        final var schemaResolutionException = new SchemaResolutionException("fail first",
             new SourceIdentifier("test-module", "2013-07-22"), new Throwable());
         doReturn(Futures.immediateFailedFuture(schemaResolutionException))
                 .when(schemaFactory).createEffectiveModelContext(anyCollection());
 
-        final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = new NetconfDevice
-                .SchemaResourcesDTO(schemaRegistry, schemaRepository, schemaFactory, STATE_SCHEMAS_RESOLVER);
-        final NetconfDevice device = new NetconfDeviceBuilder()
-                .setReconnectOnSchemasChange(true)
-                .setSchemaResourcesDTO(schemaResourcesDTO)
-                .setGlobalProcessingExecutor(MoreExecutors.directExecutor())
-                .setId(getId())
-                .setSalFacade(facade)
-                .setBaseSchemas(BASE_SCHEMAS)
-                .build();
+        final var device = new NetconfDeviceBuilder()
+            .setReconnectOnSchemasChange(true)
+            .setDeviceSchemaProvider(mockDeviceNetconfSchemaProvider(getSchemaRepository(), schemaFactory))
+            .setProcessingExecutor(MoreExecutors.directExecutor())
+            .setId(getId())
+            .setSalFacade(facade)
+            .setBaseSchemaProvider(BASE_SCHEMAS)
+            .build();
 
         // Monitoring not supported
-        final NetconfSessionPreferences sessionCaps = getSessionCaps(false, List.of(TEST_CAPABILITY));
-        device.onRemoteSessionUp(sessionCaps, listener);
+        device.onRemoteSessionUp(getSessionCaps(false, TEST_CAPABILITY), listener);
 
         final var captor = ArgumentCaptor.forClass(Throwable.class);
         verify(facade, timeout(5000)).onDeviceFailed(captor.capture());
-        assertThat(captor.getValue(), instanceOf(EmptySchemaContextException.class));
+        assertInstanceOf(EmptySchemaContextException.class, captor.getValue());
 
         verify(listener, timeout(5000)).close();
         verify(schemaFactory, times(1)).createEffectiveModelContext(anyCollection());
     }
 
-    @Test
-    public void testNetconfDeviceMissingSource() throws Exception {
-        final RemoteDeviceHandler facade = getFacade();
-        final NetconfDeviceCommunicator listener = getListener();
-
-        final EffectiveModelContextFactory schemaFactory = getSchemaFactory();
-        final SchemaRepository schemaRepository = getSchemaRepository();
-
-        // Make fallback attempt to fail due to empty resolved sources
-        final MissingSchemaSourceException schemaResolutionException =
-                new MissingSchemaSourceException(TEST_SID, "fail first");
-        doReturn(Futures.immediateFailedFuture(schemaResolutionException))
-                .when(schemaRepository).getSchemaSource(eq(TEST_SID), eq(YangTextSource.class));
-        doAnswer(invocation -> {
-            if (invocation.getArgument(0, Collection.class).size() == 2) {
-                return Futures.immediateFailedFuture(schemaResolutionException);
-            } else {
-                return Futures.immediateFuture(SCHEMA_CONTEXT);
-            }
-        }).when(schemaFactory).createEffectiveModelContext(anyCollection());
-
-        final NetconfDeviceSchemasResolver stateSchemasResolver = (deviceRpc, remoteSessionCapabilities, id,
-            schemaContext) -> {
-            final Module first = SCHEMA_CONTEXT.getModules().iterator().next();
-            final QName qName = QName.create(first.getQNameModule(), first.getName());
-            final NetconfStateSchemas.RemoteYangSchema source1 = new NetconfStateSchemas.RemoteYangSchema(qName);
-            final NetconfStateSchemas.RemoteYangSchema source2 =
-                    new NetconfStateSchemas.RemoteYangSchema(QName.create(first.getQNameModule(), "test-module2"));
-            return new NetconfStateSchemas(Sets.newHashSet(source1, source2));
-        };
-
-        doReturn(mock(Registration.class)).when(schemaRegistry).registerSchemaSource(any(), any());
-        final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = new NetconfDevice
-                .SchemaResourcesDTO(schemaRegistry, schemaRepository, schemaFactory, stateSchemasResolver);
-
-        final NetconfDevice device = new NetconfDeviceBuilder()
-                .setReconnectOnSchemasChange(true)
-                .setSchemaResourcesDTO(schemaResourcesDTO)
-                .setGlobalProcessingExecutor(MoreExecutors.directExecutor())
-                .setBaseSchemas(BASE_SCHEMAS)
-                .setId(getId())
-                .setSalFacade(facade)
-                .build();
-        // Monitoring supported
-        final NetconfSessionPreferences sessionCaps =
-                getSessionCaps(true, List.of(TEST_CAPABILITY, TEST_CAPABILITY2));
-        device.onRemoteSessionUp(sessionCaps, listener);
-
-        verify(facade, timeout(5000)).onDeviceConnected(any(NetconfDeviceSchema.class),
-            any(NetconfSessionPreferences.class), any(RemoteDeviceServices.class));
-        verify(schemaFactory, times(1)).createEffectiveModelContext(anyCollection());
-    }
-
     private static SchemaRepository getSchemaRepository() {
-        final SchemaRepository mock = mock(SchemaRepository.class);
-        final YangTextSource mockRep = mock(YangTextSource.class);
+        final var mock = mock(SchemaRepository.class);
+        final var mockRep = mock(YangTextSource.class);
         doReturn(Futures.immediateFuture(mockRep))
                 .when(mock).getSchemaSource(any(SourceIdentifier.class), eq(YangTextSource.class));
         return mock;
     }
 
     @Test
-    public void testNotificationBeforeSchema() throws Exception {
-        final RemoteDeviceHandler facade = getFacade();
-        final NetconfDeviceCommunicator listener = getListener();
-        final EffectiveModelContextFactory schemaContextProviderFactory = mock(EffectiveModelContextFactory.class);
-        final SettableFuture<SchemaContext> schemaFuture = SettableFuture.create();
-        doReturn(schemaFuture).when(schemaContextProviderFactory).createEffectiveModelContext(anyCollection());
-        final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry,
-            getSchemaRepository(), schemaContextProviderFactory, STATE_SCHEMAS_RESOLVER);
-        final NetconfDevice device = new NetconfDeviceBuilder()
-                .setReconnectOnSchemasChange(true)
-                .setSchemaResourcesDTO(schemaResourcesDTO)
-                .setGlobalProcessingExecutor(MoreExecutors.directExecutor())
-                .setId(getId())
-                .setSalFacade(facade)
-                .setBaseSchemas(BASE_SCHEMAS)
-                .build();
-
-        final NetconfSessionPreferences sessionCaps = getSessionCaps(true, List.of(TEST_CAPABILITY));
+    void testNotificationBeforeSchema() {
+        final var remoteDeviceHandler = mockRemoteDeviceHandler();
+        doNothing().when(remoteDeviceHandler).onNotification(any(DOMNotification.class));
+        final var schemaFuture = SettableFuture.<DeviceNetconfSchema>create();
+        doReturn(schemaFuture).when(deviceSchemaProvider).deviceNetconfSchemaFor(any(), any(), any(), any(), any());
+
+        final var device = new NetconfDeviceBuilder()
+            .setReconnectOnSchemasChange(true)
+            .setDeviceSchemaProvider(deviceSchemaProvider)
+            .setProcessingExecutor(MoreExecutors.directExecutor())
+            .setId(getId())
+            .setSalFacade(remoteDeviceHandler)
+            .setBaseSchemaProvider(BASE_SCHEMAS)
+            .build();
+
+        final var sessionCaps = getSessionCaps(true, TEST_CAPABILITY);
         device.onRemoteSessionUp(sessionCaps, listener);
 
         device.onNotification(NOTIFICATION);
         device.onNotification(NOTIFICATION);
-        verify(facade, times(0)).onNotification(any(DOMNotification.class));
+        verify(remoteDeviceHandler, times(0)).onNotification(any(DOMNotification.class));
+
+        // Now enable schema
+        schemaFuture.set(new DeviceNetconfSchema(NetconfDeviceCapabilities.empty(),
+            NetconfToNotificationTest.getNotificationSchemaContext(NetconfDeviceTest.class, false)));
 
-        verify(facade, times(0)).onNotification(any(DOMNotification.class));
-        schemaFuture.set(NetconfToNotificationTest.getNotificationSchemaContext(getClass(), false));
-        verify(facade, timeout(10000).times(2)).onNotification(any(DOMNotification.class));
+        verify(remoteDeviceHandler, timeout(10000).times(2))
+            .onNotification(any(DOMNotification.class));
 
         device.onNotification(NOTIFICATION);
-        verify(facade, timeout(10000).times(3)).onNotification(any(DOMNotification.class));
+        verify(remoteDeviceHandler, times(3)).onNotification(any(DOMNotification.class));
+    }
+
+    private RemoteDeviceHandler mockRemoteDeviceHandler() {
+        doNothing().when(facade).onDeviceConnected(
+            any(NetconfDeviceSchema.class), any(NetconfSessionPreferences.class), any(RemoteDeviceServices.class));
+        return facade;
     }
 
     @Test
-    public void testNetconfDeviceReconnect() throws Exception {
-        final RemoteDeviceHandler facade = getFacade();
-        final NetconfDeviceCommunicator listener = getListener();
-
-        final EffectiveModelContextFactory schemaContextProviderFactory = getSchemaFactory();
-
-        final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(
-                schemaRegistry, getSchemaRepository(), schemaContextProviderFactory, STATE_SCHEMAS_RESOLVER);
-        final NetconfDevice device = new NetconfDeviceBuilder()
-                .setReconnectOnSchemasChange(true)
-                .setSchemaResourcesDTO(schemaResourcesDTO)
-                .setGlobalProcessingExecutor(MoreExecutors.directExecutor())
-                .setId(getId())
-                .setSalFacade(facade)
-                .setBaseSchemas(BASE_SCHEMAS)
-                .build();
-        final NetconfSessionPreferences sessionCaps = getSessionCaps(true,
-                List.of(TEST_NAMESPACE + "?module=" + TEST_MODULE + "&amp;revision=" + TEST_REVISION));
+    void testNetconfDeviceReconnect() {
+        doReturn(RpcResultBuilder.failed().buildFuture()).when(listener).sendRequest(any());
+
+        final var device = new NetconfDeviceBuilder()
+            .setReconnectOnSchemasChange(true)
+            .setDeviceSchemaProvider(mockDeviceNetconfSchemaProvider())
+            .setProcessingExecutor(MoreExecutors.directExecutor())
+            .setId(getId())
+            .setSalFacade(facade)
+            .setBaseSchemaProvider(BASE_SCHEMAS)
+            .build();
+        final var sessionCaps = getSessionCaps(true,
+                TEST_NAMESPACE + "?module=" + TEST_MODULE + "&amp;revision=" + TEST_REVISION);
         device.onRemoteSessionUp(sessionCaps, listener);
 
-        verify(schemaContextProviderFactory, timeout(5000)).createEffectiveModelContext(anyCollection());
         verify(facade, timeout(5000)).onDeviceConnected(
                 any(NetconfDeviceSchema.class), any(NetconfSessionPreferences.class), any(RemoteDeviceServices.class));
 
@@ -304,63 +191,56 @@ public class NetconfDeviceTest extends AbstractTestModelTest {
 
         device.onRemoteSessionUp(sessionCaps, listener);
 
-        verify(schemaContextProviderFactory, timeout(5000).times(2)).createEffectiveModelContext(anyCollection());
         verify(facade, timeout(5000).times(2)).onDeviceConnected(
                 any(NetconfDeviceSchema.class), any(NetconfSessionPreferences.class), any(RemoteDeviceServices.class));
     }
 
     @Test
-    public void testNetconfDeviceDisconnectListenerCallCancellation() throws Exception {
-        final RemoteDeviceHandler facade = getFacade();
-        final NetconfDeviceCommunicator listener = getListener();
-        final EffectiveModelContextFactory schemaContextProviderFactory = mock(EffectiveModelContextFactory.class);
-        final SettableFuture<SchemaContext> schemaFuture = SettableFuture.create();
-        doReturn(schemaFuture).when(schemaContextProviderFactory).createEffectiveModelContext(anyCollection());
-        final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry,
-            getSchemaRepository(), schemaContextProviderFactory, STATE_SCHEMAS_RESOLVER);
-        final NetconfDevice device = new NetconfDeviceBuilder()
-                .setReconnectOnSchemasChange(true)
-                .setSchemaResourcesDTO(schemaResourcesDTO)
-                .setGlobalProcessingExecutor(MoreExecutors.directExecutor())
-                .setId(getId())
-                .setSalFacade(facade)
-                .setBaseSchemas(BASE_SCHEMAS)
-                .build();
-        final NetconfSessionPreferences sessionCaps = getSessionCaps(true,
-                List.of(TEST_NAMESPACE + "?module=" + TEST_MODULE + "&amp;revision=" + TEST_REVISION));
+    void testNetconfDeviceDisconnectListenerCallCancellation() {
+        doNothing().when(facade).onDeviceDisconnected();
+        final var schemaFuture = SettableFuture.<DeviceNetconfSchema>create();
+        doReturn(schemaFuture).when(schemaProvider).deviceNetconfSchemaFor(any(), any(), any(), any(), any());
+
+        final var device = new NetconfDeviceBuilder()
+            .setReconnectOnSchemasChange(true)
+            .setDeviceSchemaProvider(schemaProvider)
+            .setProcessingExecutor(MoreExecutors.directExecutor())
+            .setId(getId())
+            .setSalFacade(facade)
+            .setBaseSchemaProvider(BASE_SCHEMAS)
+            .build();
         //session up, start schema resolution
-        device.onRemoteSessionUp(sessionCaps, listener);
+        device.onRemoteSessionUp(getSessionCaps(true,
+            TEST_NAMESPACE + "?module=" + TEST_MODULE + "&amp;revision=" + TEST_REVISION),
+            listener);
         //session closed
         device.onRemoteSessionDown();
         verify(facade, timeout(5000)).onDeviceDisconnected();
         //complete schema setup
-        schemaFuture.set(SCHEMA_CONTEXT);
+        schemaFuture.set(new DeviceNetconfSchema(NetconfDeviceCapabilities.empty(), SCHEMA_CONTEXT));
         //facade.onDeviceDisconnected() was called, so facade.onDeviceConnected() shouldn't be called anymore
         verify(facade, after(1000).never()).onDeviceConnected(any(), any(), any(RemoteDeviceServices.class));
     }
 
     @Test
-    public void testNetconfDeviceReconnectBeforeSchemaSetup() throws Exception {
-        final RemoteDeviceHandler facade = getFacade();
+    void testNetconfDeviceReconnectBeforeSchemaSetup() {
+        final var schemaFuture = SettableFuture.<EffectiveModelContext>create();
+        doReturn(schemaFuture).when(schemaFactory).createEffectiveModelContext(anyCollection());
 
-        final EffectiveModelContextFactory schemaContextProviderFactory = mock(EffectiveModelContextFactory.class);
-        final SettableFuture<SchemaContext> schemaFuture = SettableFuture.create();
-        doReturn(schemaFuture).when(schemaContextProviderFactory).createEffectiveModelContext(anyCollection());
+        doReturn(RpcResultBuilder.failed().buildFuture()).when(listener).sendRequest(any());
 
-        final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(
-            schemaRegistry, getSchemaRepository(), schemaContextProviderFactory, STATE_SCHEMAS_RESOLVER);
-        final NetconfDevice device = new NetconfDeviceBuilder()
+        final var device = new NetconfDeviceBuilder()
             .setReconnectOnSchemasChange(true)
-            .setSchemaResourcesDTO(schemaResourcesDTO)
-            .setGlobalProcessingExecutor(MoreExecutors.directExecutor())
+            .setDeviceSchemaProvider(mockDeviceNetconfSchemaProvider(getSchemaRepository(),
+                schemaFactory))
+            .setProcessingExecutor(MoreExecutors.directExecutor())
             .setId(getId())
             .setSalFacade(facade)
-            .setBaseSchemas(BASE_SCHEMAS)
+            .setBaseSchemaProvider(BASE_SCHEMAS)
             .build();
-        final NetconfSessionPreferences sessionCaps = getSessionCaps(true,
-            List.of(TEST_NAMESPACE + "?module=" + TEST_MODULE + "&amp;revision=" + TEST_REVISION));
+        final var sessionCaps = getSessionCaps(true,
+            TEST_NAMESPACE + "?module=" + TEST_MODULE + "&amp;revision=" + TEST_REVISION);
 
-        final NetconfDeviceCommunicator listener = getListener();
         // session up, start schema resolution
         device.onRemoteSessionUp(sessionCaps, listener);
         // session down
@@ -371,210 +251,134 @@ public class NetconfDeviceTest extends AbstractTestModelTest {
         // complete schema setup
         schemaFuture.set(SCHEMA_CONTEXT);
         // schema setup performed twice
-        verify(schemaContextProviderFactory, timeout(5000).times(2)).createEffectiveModelContext(anyCollection());
+        verify(schemaFactory, timeout(5000).times(2)).createEffectiveModelContext(anyCollection());
         // onDeviceConnected called once
         verify(facade, timeout(5000)).onDeviceConnected(
             any(NetconfDeviceSchema.class), any(NetconfSessionPreferences.class), any(RemoteDeviceServices.class));
     }
 
     @Test
-    public void testNetconfDeviceAvailableCapabilitiesBuilding() throws Exception {
-        final RemoteDeviceHandler facade = getFacade();
-        final NetconfDeviceCommunicator listener = getListener();
-
-        final EffectiveModelContextFactory schemaContextProviderFactory = getSchemaFactory();
-
-        final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry,
-            getSchemaRepository(), schemaContextProviderFactory, STATE_SCHEMAS_RESOLVER);
-        final NetconfDevice device = new NetconfDeviceBuilder()
-                .setReconnectOnSchemasChange(true)
-                .setSchemaResourcesDTO(schemaResourcesDTO)
-                .setGlobalProcessingExecutor(MoreExecutors.directExecutor())
-                .setId(getId())
-                .setSalFacade(facade)
-                .setBaseSchemas(BASE_SCHEMAS)
-                .build();
-        final NetconfDevice netconfSpy = spy(device);
-
-        final NetconfSessionPreferences sessionCaps = getSessionCaps(true,
-                List.of(TEST_NAMESPACE + "?module=" + TEST_MODULE + "&amp;revision=" + TEST_REVISION));
-        final Map<QName, CapabilityOrigin> moduleBasedCaps = new HashMap<>();
+    void testNetconfDeviceAvailableCapabilitiesBuilding() {
+        doReturn(RpcResultBuilder.failed().buildFuture()).when(listener).sendRequest(any());
+
+        final var netconfSpy = spy(new NetconfDeviceBuilder()
+            .setReconnectOnSchemasChange(true)
+            .setDeviceSchemaProvider(mockDeviceNetconfSchemaProvider())
+            .setProcessingExecutor(MoreExecutors.directExecutor())
+            .setId(getId())
+            .setSalFacade(facade)
+            .setBaseSchemaProvider(BASE_SCHEMAS)
+            .build());
+
+        final var sessionCaps = getSessionCaps(true,
+            TEST_NAMESPACE + "?module=" + TEST_MODULE + "&amp;revision=" + TEST_REVISION);
+        final var moduleBasedCaps = new HashMap<QName, CapabilityOrigin>();
         moduleBasedCaps.putAll(sessionCaps.moduleBasedCaps());
-        moduleBasedCaps
-                .put(QName.create("(test:qname:side:loading)test"), CapabilityOrigin.UserDefined);
+        moduleBasedCaps.put(QName.create("(test:qname:side:loading)test"), CapabilityOrigin.UserDefined);
 
         netconfSpy.onRemoteSessionUp(sessionCaps.replaceModuleCaps(moduleBasedCaps), listener);
 
-        final ArgumentCaptor<NetconfDeviceSchema> argument = ArgumentCaptor.forClass(NetconfDeviceSchema.class);
-        verify(facade, timeout(5000)).onDeviceConnected(argument.capture(), any(NetconfSessionPreferences.class),
-            any(RemoteDeviceServices.class));
-        argument.getValue().capabilities().resolvedCapabilities()
-                .forEach(entry -> assertEquals("Builded 'AvailableCapability' schemas should match input capabilities.",
-                        moduleBasedCaps.get(
-                                QName.create(entry.getCapability())).getName(), entry.getCapabilityOrigin().getName()));
+        final var argument = ArgumentCaptor.forClass(NetconfDeviceSchema.class);
+        verify(facade, timeout(5000)).onDeviceConnected(argument.capture(),
+            any(NetconfSessionPreferences.class), any(RemoteDeviceServices.class));
+
+        assertEquals(Set.of(
+            new AvailableCapabilityBuilder()
+                .setCapability("(test:namespace?revision=2013-07-22)test-module")
+                .setCapabilityOrigin(CapabilityOrigin.DeviceAdvertised)
+                .build(),
+            new AvailableCapabilityBuilder()
+                .setCapability("(test:qname:side:loading)test")
+                .setCapabilityOrigin(CapabilityOrigin.UserDefined)
+                .build()), argument.getValue().capabilities().resolvedCapabilities());
     }
 
     @Test
-    public void testNetconfDeviceNotificationsModelNotPresentWithCapability() throws Exception {
-        final RemoteDeviceHandler facade = getFacade();
-        final NetconfDeviceCommunicator listener = getListener();
-        final EffectiveModelContextFactory schemaContextProviderFactory = getSchemaFactory();
-
-        final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry,
-            getSchemaRepository(), schemaContextProviderFactory, STATE_SCHEMAS_RESOLVER);
-        final NetconfDevice device = new NetconfDeviceBuilder()
-                .setSchemaResourcesDTO(schemaResourcesDTO)
-                .setGlobalProcessingExecutor(MoreExecutors.directExecutor())
-                .setId(getId())
-                .setSalFacade(facade)
-                .setBaseSchemas(BASE_SCHEMAS)
-                .build();
-        final NetconfDevice netconfSpy = spy(device);
-
-        final NetconfSessionPreferences sessionCaps = getSessionCaps(false, List.of(CapabilityURN.NOTIFICATION));
-
-        netconfSpy.onRemoteSessionUp(sessionCaps, listener);
-
-        final ArgumentCaptor<NetconfDeviceSchema> argument = ArgumentCaptor.forClass(NetconfDeviceSchema.class);
-        verify(facade, timeout(5000)).onDeviceConnected(argument.capture(), any(NetconfSessionPreferences.class),
-                any(RemoteDeviceServices.class));
-
-        List<String> notificationModulesName = List.of(
-                org.opendaylight.yang.svc.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714
-                        .YangModuleInfoImpl.getInstance().getName().toString(),
-                org.opendaylight.yang.svc.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715
-                        .YangModuleInfoImpl.getInstance().getName().toString());
-
-        final Set<AvailableCapability> resolvedCapabilities = argument.getValue().capabilities().resolvedCapabilities();
+    void testNetconfDeviceNotificationsModelNotPresentWithCapability() {
+        final var netconfSpy = spy(new NetconfDeviceBuilder()
+            .setDeviceSchemaProvider(mockDeviceNetconfSchemaProvider())
+            .setProcessingExecutor(MoreExecutors.directExecutor())
+            .setId(getId())
+            .setSalFacade(facade)
+            .setBaseSchemaProvider(BASE_SCHEMAS)
+            .build());
 
-        assertEquals(2, resolvedCapabilities.size());
-        assertTrue(resolvedCapabilities.stream().anyMatch(entry -> notificationModulesName
-                .contains(entry.getCapability())));
-    }
+        netconfSpy.onRemoteSessionUp(getSessionCaps(false, CapabilityURN.NOTIFICATION),
+            listener);
 
-    @Test
-    public void testNetconfDeviceNotificationsCapabilityIsNotPresent() throws Exception {
-        final RemoteDeviceHandler facade = getFacade();
-        final NetconfDeviceCommunicator listener = getListener();
-        final EffectiveModelContextFactory schemaContextProviderFactory = getSchemaFactory();
-
-        final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry,
-            getSchemaRepository(), schemaContextProviderFactory, STATE_SCHEMAS_RESOLVER);
-        final NetconfDevice device = new NetconfDeviceBuilder()
-                .setSchemaResourcesDTO(schemaResourcesDTO)
-                .setGlobalProcessingExecutor(MoreExecutors.directExecutor())
-                .setId(getId())
-                .setSalFacade(facade)
-                .setBaseSchemas(BASE_SCHEMAS)
-                .build();
-        final NetconfDevice netconfSpy = spy(device);
-
-        final NetconfSessionPreferences sessionCaps = getSessionCaps(false,
-                List.of(TEST_NAMESPACE + "?module=" + TEST_MODULE + "&amp;revision=" + TEST_REVISION));
-
-        netconfSpy.onRemoteSessionUp(sessionCaps, listener);
-
-        final ArgumentCaptor<NetconfDeviceSchema> argument = ArgumentCaptor.forClass(NetconfDeviceSchema.class);
+        final var argument = ArgumentCaptor.forClass(NetconfDeviceSchema.class);
         verify(facade, timeout(5000)).onDeviceConnected(argument.capture(), any(NetconfSessionPreferences.class),
                 any(RemoteDeviceServices.class));
-        final NetconfDeviceCapabilities netconfDeviceCaps = argument.getValue().capabilities();
-
-        List<String> notificationModulesName = List.of(
-                org.opendaylight.yang.svc.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714
-                        .YangModuleInfoImpl.getInstance().getName().toString(),
-                org.opendaylight.yang.svc.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715
-                        .YangModuleInfoImpl.getInstance().getName().toString());
 
-        assertFalse(netconfDeviceCaps.resolvedCapabilities().stream()
-            .anyMatch(entry -> notificationModulesName.contains(entry.getCapability())));
+        assertEquals(Set.of(
+            new AvailableCapabilityBuilder()
+                .setCapability("(urn:ietf:params:xml:ns:yang:ietf-yang-types?revision=2013-07-15)ietf-yang-types")
+                .build(),
+            new AvailableCapabilityBuilder()
+                .setCapability("(urn:ietf:params:xml:ns:netconf:notification:1.0?revision=2008-07-14)notifications")
+                .build()), argument.getValue().capabilities().resolvedCapabilities());
     }
 
     @Test
-    public void testNetconfDeviceNotificationsModelIsPresent() throws Exception {
-        final RemoteDeviceHandler facade = getFacade();
-        final NetconfDeviceCommunicator listener = getListener();
-        final EffectiveModelContextFactory schemaContextProviderFactory = getSchemaFactory();
-
-        final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry,
-            getSchemaRepository(), schemaContextProviderFactory, STATE_SCHEMAS_RESOLVER);
-        final NetconfDevice device = new NetconfDeviceBuilder()
-                .setSchemaResourcesDTO(schemaResourcesDTO)
-                .setGlobalProcessingExecutor(MoreExecutors.directExecutor())
-                .setId(getId())
-                .setSalFacade(facade)
-                .setBaseSchemas(BASE_SCHEMAS)
-                .build();
-        final NetconfDevice netconfSpy = spy(device);
-
-        final NetconfSessionPreferences sessionCaps = getSessionCaps(false, List.of());
-
-        final var moduleBasedCaps = new HashMap<QName, CapabilityOrigin>();
-        moduleBasedCaps.put(org.opendaylight.yang.svc.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714
-                        .YangModuleInfoImpl.getInstance().getName(),
-                CapabilityOrigin.DeviceAdvertised);
-        moduleBasedCaps.put(org.opendaylight.yang.svc.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715
-                        .YangModuleInfoImpl.getInstance().getName(),
-                CapabilityOrigin.DeviceAdvertised);
+    void testNetconfDeviceNotificationsModelIsPresent() {
+        final var netconfSpy = spy(new NetconfDeviceBuilder()
+            .setDeviceSchemaProvider(mockDeviceNetconfSchemaProvider())
+            .setProcessingExecutor(MoreExecutors.directExecutor())
+            .setId(getId())
+            .setSalFacade(facade)
+            .setBaseSchemaProvider(BASE_SCHEMAS)
+            .build());
 
-
-        netconfSpy.onRemoteSessionUp(sessionCaps.replaceModuleCaps(moduleBasedCaps), listener);
+        netconfSpy.onRemoteSessionUp(getSessionCaps(false).replaceModuleCaps(Map.of(
+            org.opendaylight.yang.svc.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714
+                .YangModuleInfoImpl.getInstance().getName(), CapabilityOrigin.DeviceAdvertised,
+            org.opendaylight.yang.svc.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715
+                .YangModuleInfoImpl.getInstance().getName(), CapabilityOrigin.DeviceAdvertised
+            )), listener);
 
         final var argument = ArgumentCaptor.forClass(NetconfDeviceSchema.class);
         verify(facade, timeout(5000)).onDeviceConnected(argument.capture(), any(NetconfSessionPreferences.class),
                 any(RemoteDeviceServices.class));
-        final Set<AvailableCapability> resolvedCapabilities = argument.getValue().capabilities().resolvedCapabilities();
-
-        final var notificationModulesName = List.of(
-                org.opendaylight.yang.svc.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714
-                        .YangModuleInfoImpl.getInstance().getName().toString(),
-                org.opendaylight.yang.svc.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715
-                        .YangModuleInfoImpl.getInstance().getName().toString());
 
-        assertEquals(2, resolvedCapabilities.size());
-        assertTrue(resolvedCapabilities.stream().anyMatch(entry -> notificationModulesName
-                .contains(entry.getCapability())));
+        assertEquals(Set.of(
+            new AvailableCapabilityBuilder()
+                .setCapability("(urn:ietf:params:xml:ns:yang:ietf-yang-types?revision=2013-07-15)ietf-yang-types")
+                .setCapabilityOrigin(CapabilityOrigin.DeviceAdvertised)
+                .build(),
+            new AvailableCapabilityBuilder()
+                .setCapability("(urn:ietf:params:xml:ns:netconf:notification:1.0?revision=2008-07-14)notifications")
+                .setCapabilityOrigin(CapabilityOrigin.DeviceAdvertised)
+                .build()), argument.getValue().capabilities().resolvedCapabilities());
     }
 
-    private static EffectiveModelContextFactory getSchemaFactory() throws Exception {
-        final EffectiveModelContextFactory schemaFactory = mock(EffectiveModelContextFactory.class);
+    private EffectiveModelContextFactory getSchemaFactory() {
         doReturn(Futures.immediateFuture(SCHEMA_CONTEXT))
                 .when(schemaFactory).createEffectiveModelContext(anyCollection());
         return schemaFactory;
     }
 
-    private static RemoteDeviceHandler getFacade() throws Exception {
-        final RemoteDeviceHandler remoteDeviceHandler = mockCloseableClass(RemoteDeviceHandler.class);
-        doNothing().when(remoteDeviceHandler).onDeviceConnected(
-                any(NetconfDeviceSchema.class), any(NetconfSessionPreferences.class), any(RemoteDeviceServices.class));
-        doNothing().when(remoteDeviceHandler).onDeviceDisconnected();
-        doNothing().when(remoteDeviceHandler).onNotification(any(DOMNotification.class));
-        return remoteDeviceHandler;
+    private DeviceNetconfSchemaProvider mockDeviceNetconfSchemaProvider() {
+        return mockDeviceNetconfSchemaProvider(getSchemaRepository(), getSchemaFactory());
     }
 
-    private static <T extends AutoCloseable> T mockCloseableClass(final Class<T> remoteDeviceHandlerClass)
-            throws Exception {
-        final T mock = mock(remoteDeviceHandlerClass);
-        doNothing().when(mock).close();
-        return mock;
+    private DeviceNetconfSchemaProvider mockDeviceNetconfSchemaProvider(final SchemaRepository schemaRepository,
+            final EffectiveModelContextFactory modelContextFactory) {
+        return new DefaultDeviceNetconfSchemaProvider(schemaRegistry, schemaRepository, modelContextFactory);
     }
 
-    public RemoteDeviceId getId() {
+    private static RemoteDeviceId getId() {
         return new RemoteDeviceId("test-D", InetSocketAddress.createUnresolved("localhost", 22));
     }
 
-    public NetconfSessionPreferences getSessionCaps(final boolean addMonitor,
-                                                    final Collection<String> additionalCapabilities) {
+    private static NetconfSessionPreferences getSessionCaps(final boolean addMonitor,
+            final String... additionalCapabilities) {
         final var capabilities = new ArrayList<String>();
         capabilities.add(CapabilityURN.BASE);
         capabilities.add(CapabilityURN.BASE_1_1);
         if (addMonitor) {
             capabilities.add(NetconfState.QNAME.getNamespace().toString());
         }
-        capabilities.addAll(additionalCapabilities);
+        capabilities.addAll(List.of(additionalCapabilities));
         return NetconfSessionPreferences.fromStrings(capabilities);
     }
-
-    public NetconfDeviceCommunicator getListener() throws Exception {
-        return mockCloseableClass(NetconfDeviceCommunicator.class);
-    }
 }