From 1e1e99672bf3450a165b3ee0be5d38e856586da4 Mon Sep 17 00:00:00 2001 From: Andrej Mak Date: Thu, 13 Oct 2016 13:18:58 +0200 Subject: [PATCH] Add sal-netconf-connector unit tests Change-Id: I6fa10f4a84da2d61cdea57d700195c0eca017f3c Signed-off-by: Andrej Mak --- .../connect/netconf/NetconfStateSchemas.java | 4 +- .../netconf/NetconfStateSchemasTest.java | 134 +++++++++++++++++- .../netconf/sal/MountInstanceTest.java | 15 +- .../sal/NetconfDeviceSalProviderTest.java | 97 +++++++++++++ 4 files changed, 242 insertions(+), 8 deletions(-) create mode 100644 netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceSalProviderTest.java diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/NetconfStateSchemas.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/NetconfStateSchemas.java index 7f4ecd96db..d9ca7df3e9 100644 --- a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/NetconfStateSchemas.java +++ b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/NetconfStateSchemas.java @@ -25,9 +25,9 @@ import java.util.Collections; import java.util.Set; import java.util.concurrent.ExecutionException; import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcService; import org.opendaylight.netconf.sal.connect.api.NetconfDeviceSchemas; import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences; -import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfDeviceRpc; import org.opendaylight.netconf.sal.connect.netconf.schema.mapping.BaseSchema; import org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil; import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; @@ -92,7 +92,7 @@ public final class NetconfStateSchemas implements NetconfDeviceSchemas { /** * Issue get request to remote device and parse response to find all schemas under netconf-state/schemas */ - static NetconfStateSchemas create(final NetconfDeviceRpc deviceRpc, final NetconfSessionPreferences remoteSessionCapabilities, final RemoteDeviceId id) { + static NetconfStateSchemas create(final DOMRpcService deviceRpc, final NetconfSessionPreferences remoteSessionCapabilities, final RemoteDeviceId id) { if(remoteSessionCapabilities.isMonitoringSupported() == false) { // TODO - need to search for get-schema support, not just ietf-netconf-monitoring support // issue might be a deviation to ietf-netconf-monitoring where get-schema is unsupported... diff --git a/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/NetconfStateSchemasTest.java b/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/NetconfStateSchemasTest.java index 97b9ef3370..c8564975b6 100644 --- a/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/NetconfStateSchemasTest.java +++ b/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/NetconfStateSchemasTest.java @@ -11,19 +11,48 @@ package org.opendaylight.netconf.sal.connect.netconf; import static org.hamcrest.CoreMatchers.hasItem; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_GET_QNAME; +import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.toPath; +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; import java.net.InetSocketAddress; import java.util.Collections; import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import org.junit.Assert; +import org.junit.Before; import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import org.opendaylight.controller.config.util.xml.XmlUtil; +import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcException; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationNotAvailableException; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcService; +import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult; +import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences; import org.opendaylight.netconf.sal.connect.netconf.schema.mapping.BaseSchema; +import org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil; import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfState; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Schemas; import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.RpcError; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlUtils; +import org.opendaylight.yangtools.yang.data.impl.schema.Builders; import org.opendaylight.yangtools.yang.data.impl.schema.transform.ToNormalizedNodeParser; import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser.DomToNormalizedNodeParserFactory; import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; @@ -34,17 +63,28 @@ import org.w3c.dom.Element; public class NetconfStateSchemasTest { - @Test - public void testCreate() throws Exception { + private static final NetconfSessionPreferences CAPS = NetconfSessionPreferences.fromStrings(Collections.singleton("urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?module=ietf-netconf-monitoring&revision=2010-10-04")); + private final RemoteDeviceId deviceId = new RemoteDeviceId("device", new InetSocketAddress(99)); + private ContainerNode compositeNodeSchemas; + @Mock + private DOMRpcService rpc; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); final SchemaContext schemaContext = BaseSchema.BASE_NETCONF_CTX_WITH_NOTIFICATIONS.getSchemaContext(); final DataSchemaNode schemasNode = ((ContainerSchemaNode) schemaContext .getDataChildByName(NetconfState.QNAME)).getDataChildByName(Schemas.QNAME); - final Document schemasXml = XmlUtil.readXmlToDocument(getClass().getResourceAsStream("/netconf-state.schemas.payload.xml")); final ToNormalizedNodeParser containerNodeParser = DomToNormalizedNodeParserFactory.getInstance(XmlUtils.DEFAULT_XML_CODEC_PROVIDER, schemaContext, false).getContainerNodeParser(); - final ContainerNode compositeNodeSchemas = containerNodeParser.parse(Collections.singleton(schemasXml.getDocumentElement()), (ContainerSchemaNode) schemasNode); - final NetconfStateSchemas schemas = NetconfStateSchemas.create(new RemoteDeviceId("device", new InetSocketAddress(99)), compositeNodeSchemas); + compositeNodeSchemas = containerNodeParser.parse(Collections.singleton(schemasXml.getDocumentElement()), (ContainerSchemaNode) schemasNode); + + } + + @Test + public void testCreate() throws Exception { + final NetconfStateSchemas schemas = NetconfStateSchemas.create(deviceId, compositeNodeSchemas); final Set availableYangSchemasQNames = schemas.getAvailableYangSchemasQNames(); assertEquals(73, availableYangSchemasQNames.size()); @@ -52,4 +92,88 @@ public class NetconfStateSchemasTest { assertThat(availableYangSchemasQNames, hasItem(QName.create("urn:TBD:params:xml:ns:yang:network-topology", "2013-07-12", "network-topology"))); } + + @Test + public void testCreate2() throws Exception { + final ContainerNode netconfState = Builders.containerBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(NetconfState.QNAME)) + .withChild(compositeNodeSchemas) + .build(); + final ContainerNode data = Builders.containerBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(NetconfMessageTransformUtil.NETCONF_DATA_QNAME)) + .withChild(netconfState) + .build(); + final ContainerNode rpcReply = Builders.containerBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(NetconfMessageTransformUtil.NETCONF_RPC_REPLY_QNAME)) + .withChild(data) + .build(); + when(rpc.invokeRpc(eq(toPath(NETCONF_GET_QNAME)), any())).thenReturn(Futures.immediateCheckedFuture(new DefaultDOMRpcResult(rpcReply))); + final NetconfStateSchemas stateSchemas = NetconfStateSchemas.create(rpc, CAPS, deviceId); + final Set availableYangSchemasQNames = stateSchemas.getAvailableYangSchemasQNames(); + assertEquals(73, availableYangSchemasQNames.size()); + + assertThat(availableYangSchemasQNames, + hasItem(QName.create("urn:TBD:params:xml:ns:yang:network-topology", "2013-07-12", "network-topology"))); + } + + @Test + public void testCreateMonitoringNotSupported() throws Exception { + final NetconfSessionPreferences caps = NetconfSessionPreferences.fromStrings(Collections.emptySet()); + final NetconfStateSchemas stateSchemas = NetconfStateSchemas.create(rpc, caps, deviceId); + final Set availableYangSchemasQNames = stateSchemas.getAvailableYangSchemasQNames(); + Assert.assertTrue(availableYangSchemasQNames.isEmpty()); + } + + @Test + public void testCreateFail() throws Exception { + final CheckedFuture resultFuture = + Futures.immediateFailedCheckedFuture(new DOMRpcImplementationNotAvailableException("not available")); + when(rpc.invokeRpc(eq(toPath(NETCONF_GET_QNAME)), any())).thenReturn(resultFuture); + final NetconfStateSchemas stateSchemas = NetconfStateSchemas.create(rpc, CAPS, deviceId); + final Set availableYangSchemasQNames = stateSchemas.getAvailableYangSchemasQNames(); + Assert.assertTrue(availableYangSchemasQNames.isEmpty()); + } + + @Test + public void testCreateRpcError() throws Exception { + final RpcError rpcError = RpcResultBuilder.newError(RpcError.ErrorType.RPC, "fail", "fail"); + when(rpc.invokeRpc(eq(toPath(NETCONF_GET_QNAME)), any())).thenReturn(Futures.immediateCheckedFuture(new DefaultDOMRpcResult(rpcError))); + final NetconfStateSchemas stateSchemas = NetconfStateSchemas.create(rpc, CAPS, deviceId); + final Set availableYangSchemasQNames = stateSchemas.getAvailableYangSchemasQNames(); + Assert.assertTrue(availableYangSchemasQNames.isEmpty()); + } + + @Test(expected = RuntimeException.class) + public void testCreateInterrupted() throws Throwable { + //NetconfStateSchemas.create calls Thread.currentThread().interrupt(), so it must run in its own thread + final Future testFuture = Executors.newSingleThreadExecutor().submit(() -> { + final ListenableFuture interruptedFuture = mock(ListenableFuture.class); + try { + when(interruptedFuture.get()).thenThrow(new InterruptedException("interrupted")); + final CheckedFuture checkedFuture = Futures.makeChecked(interruptedFuture, ReadFailedException.MAPPER); + when(rpc.invokeRpc(eq(toPath(NETCONF_GET_QNAME)), any())).thenReturn(checkedFuture); + NetconfStateSchemas.create(rpc, CAPS, deviceId); + } catch (final InterruptedException | ExecutionException e) { + e.printStackTrace(); + } + + }); + try { + testFuture.get(3, TimeUnit.SECONDS); + } catch (final ExecutionException e) { + throw e.getCause(); + } + } + + @Test + public void testRemoteYangSchemaEquals() throws Exception { + final NetconfStateSchemas.RemoteYangSchema schema1 = new NetconfStateSchemas.RemoteYangSchema(NetconfState.QNAME); + final NetconfStateSchemas.RemoteYangSchema schema2 = new NetconfStateSchemas.RemoteYangSchema(NetconfState.QNAME); + final NetconfStateSchemas.RemoteYangSchema schema3 = new NetconfStateSchemas.RemoteYangSchema(Schemas.QNAME); + Assert.assertEquals(schema1, schema2); + Assert.assertEquals(schema2, schema1); + Assert.assertNotEquals(schema1, schema3); + Assert.assertNotEquals(schema2, schema3); + + } } diff --git a/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/MountInstanceTest.java b/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/MountInstanceTest.java index 7e62990bc7..41305d9c1f 100644 --- a/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/MountInstanceTest.java +++ b/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/MountInstanceTest.java @@ -22,6 +22,7 @@ import org.mockito.MockitoAnnotations; import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint; import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService; +import org.opendaylight.controller.md.sal.dom.api.DOMNotification; import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService; import org.opendaylight.controller.md.sal.dom.api.DOMRpcService; import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; @@ -46,6 +47,8 @@ public class MountInstanceTest { private DOMMountPointService.DOMMountPointBuilder mountPointBuilder; @Mock private ObjectRegistration registration; + @Mock + private DOMNotification notification; private NetconfDeviceSalProvider.MountInstance mountInstance; @@ -84,7 +87,7 @@ public class MountInstanceTest { verify(registration).close(); try { mountInstance.onTopologyDeviceConnected(SCHEMA_CONTEXT, broker, rpcService, notificationService); - } catch (IllegalStateException e) { + } catch (final IllegalStateException e) { e.printStackTrace(); Assert.fail("Topology registration still present after disconnect "); } @@ -97,4 +100,14 @@ public class MountInstanceTest { verify(registration).close(); } + @Test + public void testPublishNotification() throws Exception { + mountInstance.onTopologyDeviceConnected(SCHEMA_CONTEXT, broker, rpcService, notificationService); + verify(mountPointBuilder).addInitialSchemaContext(SCHEMA_CONTEXT); + verify(mountPointBuilder).addService(DOMNotificationService.class, notificationService); + mountInstance.publish(notification); + verify(notificationService).publishNotification(notification); + } + + } \ No newline at end of file diff --git a/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceSalProviderTest.java b/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceSalProviderTest.java new file mode 100644 index 0000000000..7fd6b96957 --- /dev/null +++ b/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceSalProviderTest.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.netconf.sal.connect.netconf.sal; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.google.common.util.concurrent.Futures; +import java.net.InetSocketAddress; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; +import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener; +import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService; +import org.opendaylight.controller.sal.binding.api.BindingAwareBroker; +import org.opendaylight.controller.sal.core.api.Broker; +import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; + +public class NetconfDeviceSalProviderTest { + + @Mock + private Broker.ProviderSession session; + @Mock + private DOMMountPointService mountpointService; + @Mock + private BindingAwareBroker.ProviderContext context; + @Mock + private WriteTransaction tx; + @Mock + private DataBroker dataBroker; + @Mock + private BindingTransactionChain chain; + private NetconfDeviceSalProvider provider; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + provider = new NetconfDeviceSalProvider(new RemoteDeviceId("device1", InetSocketAddress.createUnresolved("localhost", 17830))); + when(session.getService(DOMMountPointService.class)).thenReturn(mountpointService); + when(context.getSALService(DataBroker.class)).thenReturn(dataBroker); + when(dataBroker.createTransactionChain(any())).thenReturn(chain); + when(chain.newWriteOnlyTransaction()).thenReturn(tx); + when(tx.submit()).thenReturn(Futures.immediateCheckedFuture(null)); + when(tx.getIdentifier()).thenReturn(tx); + } + + @Test + public void onSessionInitiated() throws Exception { + provider.onSessionInitiated(session); + provider.onSessionInitiated(context); + Assert.assertNotNull(provider.getMountInstance()); + Assert.assertNotNull(provider.getTopologyDatastoreAdapter()); + } + + @Test + public void getProviderFunctionality() throws Exception { + Assert.assertTrue(provider.getProviderFunctionality().isEmpty()); + } + + @Test + public void replaceChainIfFailed() throws Exception { + provider.onSessionInitiated(session); + provider.onSessionInitiated(context); + Assert.assertNotNull(provider.getMountInstance()); + final ArgumentCaptor captor = ArgumentCaptor.forClass(TransactionChainListener.class); + verify(dataBroker).createTransactionChain(captor.capture()); + try { + captor.getValue().onTransactionChainFailed(chain, tx, new Exception("chain failed")); + } catch (final IllegalStateException e) { + //expected + } + verify(dataBroker, times(2)).createTransactionChain(any()); + } + + @Test + public void close() throws Exception { + provider.onSessionInitiated(session); + provider.onSessionInitiated(context); + provider.close(); + verify(chain).close(); + } + +} \ No newline at end of file -- 2.36.6