Merge "Add more unit tests for sal-netconf-connector"
authorTomas Cere <tcere@cisco.com>
Thu, 20 Oct 2016 13:02:06 +0000 (13:02 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Thu, 20 Oct 2016 13:02:06 +0000 (13:02 +0000)
netconf/netconf-util/src/test/resources/netconfMessages/copy-config.xml [new file with mode: 0644]
netconf/netconf-util/src/test/resources/netconfMessages/edit-config-test-module-running.xml [new file with mode: 0644]
netconf/netconf-util/src/test/resources/netconfMessages/edit-config-test-module.xml [new file with mode: 0644]
netconf/netconf-util/src/test/resources/netconfMessages/getConfig_candidate-filter.xml [new file with mode: 0644]
netconf/netconf-util/src/test/resources/netconfMessages/lock-running.xml [new file with mode: 0644]
netconf/netconf-util/src/test/resources/netconfMessages/rpc-reply_get.xml [new file with mode: 0644]
netconf/netconf-util/src/test/resources/netconfMessages/unlock-running.xml [new file with mode: 0644]
netconf/netconf-util/src/test/resources/netconfMessages/validate-running.xml [new file with mode: 0644]
netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/util/NetconfBaseOpsTest.java [new file with mode: 0644]
netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/util/NodeContainerProxyTest.java [new file with mode: 0644]

diff --git a/netconf/netconf-util/src/test/resources/netconfMessages/copy-config.xml b/netconf/netconf-util/src/test/resources/netconfMessages/copy-config.xml
new file mode 100644 (file)
index 0000000..ec32fc1
--- /dev/null
@@ -0,0 +1,18 @@
+<!--
+  ~ 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
+  -->
+
+<rpc message-id="m-0" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <copy-config>
+        <target>
+            <candidate/>
+        </target>
+        <source>
+            <running/>
+        </source>
+    </copy-config>
+</rpc>
\ No newline at end of file
diff --git a/netconf/netconf-util/src/test/resources/netconfMessages/edit-config-test-module-running.xml b/netconf/netconf-util/src/test/resources/netconfMessages/edit-config-test-module-running.xml
new file mode 100644 (file)
index 0000000..107121d
--- /dev/null
@@ -0,0 +1,22 @@
+<!--
+  ~ 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
+  -->
+
+<rpc message-id="m-0" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <edit-config>
+        <target>
+            <running/>
+        </target>
+        <default-operation>merge</default-operation>
+        <error-option>rollback-on-error</error-option>
+        <config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+            <c xmlns="test:namespace">
+                <a xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0" a:operation="replace">leaf-value</a>
+            </c>
+        </config>
+    </edit-config>
+</rpc>
\ No newline at end of file
diff --git a/netconf/netconf-util/src/test/resources/netconfMessages/edit-config-test-module.xml b/netconf/netconf-util/src/test/resources/netconfMessages/edit-config-test-module.xml
new file mode 100644 (file)
index 0000000..81907d3
--- /dev/null
@@ -0,0 +1,21 @@
+<!--
+  ~ 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
+  -->
+
+<rpc message-id="m-0" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <edit-config>
+        <target>
+            <candidate/>
+        </target>
+        <error-option>rollback-on-error</error-option>
+        <config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+            <c xmlns="test:namespace">
+                <a xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0" a:operation="replace">leaf-value</a>
+            </c>
+        </config>
+    </edit-config>
+</rpc>
\ No newline at end of file
diff --git a/netconf/netconf-util/src/test/resources/netconfMessages/getConfig_candidate-filter.xml b/netconf/netconf-util/src/test/resources/netconfMessages/getConfig_candidate-filter.xml
new file mode 100644 (file)
index 0000000..7ebbe15
--- /dev/null
@@ -0,0 +1,18 @@
+<!--
+  ~ 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
+  -->
+
+<rpc message-id="m-0" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <get-config>
+        <source>
+            <candidate/>
+        </source>
+        <filter xmlns:ns0="urn:ietf:params:xml:ns:netconf:base:1.0" ns0:type="subtree">
+            <c xmlns="test:namespace"/>
+        </filter>
+    </get-config>
+</rpc>
\ No newline at end of file
diff --git a/netconf/netconf-util/src/test/resources/netconfMessages/lock-running.xml b/netconf/netconf-util/src/test/resources/netconfMessages/lock-running.xml
new file mode 100644 (file)
index 0000000..9c202ec
--- /dev/null
@@ -0,0 +1,16 @@
+<!--
+  ~ 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
+  -->
+
+<rpc message-id="101"
+     xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <lock>
+        <target>
+            <running/>
+        </target>
+    </lock>
+</rpc>
\ No newline at end of file
diff --git a/netconf/netconf-util/src/test/resources/netconfMessages/rpc-reply_get.xml b/netconf/netconf-util/src/test/resources/netconfMessages/rpc-reply_get.xml
new file mode 100644 (file)
index 0000000..b3209d7
--- /dev/null
@@ -0,0 +1,11 @@
+<!--
+  ~ 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
+  -->
+
+<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" a="64" message-id="a">
+    <data/>
+</rpc-reply>
\ No newline at end of file
diff --git a/netconf/netconf-util/src/test/resources/netconfMessages/unlock-running.xml b/netconf/netconf-util/src/test/resources/netconfMessages/unlock-running.xml
new file mode 100644 (file)
index 0000000..a74377a
--- /dev/null
@@ -0,0 +1,16 @@
+<!--
+  ~ 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
+  -->
+
+<rpc message-id="101"
+     xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <unlock>
+        <target>
+            <running/>
+        </target>
+    </unlock>
+</rpc>
\ No newline at end of file
diff --git a/netconf/netconf-util/src/test/resources/netconfMessages/validate-running.xml b/netconf/netconf-util/src/test/resources/netconfMessages/validate-running.xml
new file mode 100644 (file)
index 0000000..783192d
--- /dev/null
@@ -0,0 +1,15 @@
+<!--
+  ~ 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
+  -->
+
+<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101">
+    <validate>
+        <source>
+            <running/>
+        </source>
+    </validate>
+</rpc>
diff --git a/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/util/NetconfBaseOpsTest.java b/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/util/NetconfBaseOpsTest.java
new file mode 100644 (file)
index 0000000..6b47a72
--- /dev/null
@@ -0,0 +1,330 @@
+/*
+ * 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.util;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import com.google.common.base.Optional;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import org.custommonkey.xmlunit.Diff;
+import org.custommonkey.xmlunit.XMLUnit;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+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.dom.api.DOMRpcService;
+import org.opendaylight.netconf.api.NetconfMessage;
+import org.opendaylight.netconf.sal.connect.api.MessageTransformer;
+import org.opendaylight.netconf.sal.connect.api.RemoteDeviceCommunicator;
+import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfDeviceRpc;
+import org.opendaylight.netconf.sal.connect.netconf.schema.mapping.NetconfMessageTransformer;
+import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.opendaylight.yangtools.yang.data.api.ModifyAction;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.xml.sax.SAXException;
+
+public class NetconfBaseOpsTest {
+
+    static {
+        XMLUnit.setIgnoreWhitespace(true);
+        XMLUnit.setIgnoreComments(true);
+    }
+
+    private static final QName CONTAINER_Q_NAME = QName.create("test:namespace", "2013-07-22", "c");
+
+    @Mock
+    private RemoteDeviceCommunicator<NetconfMessage> listener;
+    private NetconfRpcFutureCallback callback;
+    private NetconfBaseOps baseOps;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        final InputStream okStream = getClass().getResourceAsStream("/netconfMessages/rpc-reply_ok.xml");
+        final InputStream dataStream = getClass().getResourceAsStream("/netconfMessages/rpc-reply_get.xml");
+        final NetconfMessage ok = new NetconfMessage(XmlUtil.readXmlToDocument(okStream));
+        final NetconfMessage data = new NetconfMessage(XmlUtil.readXmlToDocument(dataStream));
+        when(listener.sendRequest(any(), eq(NetconfMessageTransformUtil.NETCONF_GET_CONFIG_QNAME)))
+                .thenReturn(RpcResultBuilder.success(data).buildFuture());
+        when(listener.sendRequest(any(), eq(NetconfMessageTransformUtil.NETCONF_GET_QNAME)))
+                .thenReturn(RpcResultBuilder.success(data).buildFuture());
+        when(listener.sendRequest(any(), eq(NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME)))
+                .thenReturn(RpcResultBuilder.success(ok).buildFuture());
+        when(listener.sendRequest(any(), eq(NetconfMessageTransformUtil.NETCONF_COPY_CONFIG_QNAME)))
+                .thenReturn(RpcResultBuilder.success(ok).buildFuture());
+        when(listener.sendRequest(any(), eq(NetconfMessageTransformUtil.NETCONF_DISCARD_CHANGES_QNAME)))
+                .thenReturn(RpcResultBuilder.success(ok).buildFuture());
+        when(listener.sendRequest(any(), eq(NetconfMessageTransformUtil.NETCONF_VALIDATE_QNAME)))
+                .thenReturn(RpcResultBuilder.success(ok).buildFuture());
+        when(listener.sendRequest(any(), eq(NetconfMessageTransformUtil.NETCONF_LOCK_QNAME)))
+                .thenReturn(RpcResultBuilder.success(ok).buildFuture());
+        when(listener.sendRequest(any(), eq(NetconfMessageTransformUtil.NETCONF_UNLOCK_QNAME)))
+                .thenReturn(RpcResultBuilder.success(ok).buildFuture());
+        when(listener.sendRequest(any(), eq(NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME)))
+                .thenReturn(RpcResultBuilder.success(ok).buildFuture());
+        final SchemaContext schemaContext =
+                parseYangStreams(getClass().getResourceAsStream("/schemas/test-module.yang"));
+        final MessageTransformer<NetconfMessage> transformer = new NetconfMessageTransformer(schemaContext, true);
+        final DOMRpcService rpc = new NetconfDeviceRpc(schemaContext, listener, transformer);
+        final RemoteDeviceId id =
+                new RemoteDeviceId("device-1", InetSocketAddress.createUnresolved("localhost", 17830));
+        callback = new NetconfRpcFutureCallback("prefix", id);
+        baseOps = new NetconfBaseOps(rpc, schemaContext);
+    }
+
+    @Test
+    public void testLock() throws Exception {
+        baseOps.lock(callback, NetconfMessageTransformUtil.NETCONF_CANDIDATE_QNAME);
+        verifyMessageSent("lock", NetconfMessageTransformUtil.NETCONF_LOCK_QNAME);
+    }
+
+    @Test
+    public void testLockCandidate() throws Exception {
+        baseOps.lockCandidate(callback);
+        verifyMessageSent("lock", NetconfMessageTransformUtil.NETCONF_LOCK_QNAME);
+    }
+
+    @Test
+    public void testUnlock() throws Exception {
+        baseOps.unlock(callback, NetconfMessageTransformUtil.NETCONF_CANDIDATE_QNAME);
+        verifyMessageSent("unlock", NetconfMessageTransformUtil.NETCONF_UNLOCK_QNAME);
+    }
+
+    @Test
+    public void testUnlockCandidate() throws Exception {
+        baseOps.unlockCandidate(callback);
+        verifyMessageSent("unlock", NetconfMessageTransformUtil.NETCONF_UNLOCK_QNAME);
+    }
+
+    @Test
+    public void testLockRunning() throws Exception {
+        baseOps.lockRunning(callback);
+        verifyMessageSent("lock-running", NetconfMessageTransformUtil.NETCONF_LOCK_QNAME);
+    }
+
+    @Test
+    public void testUnlockRunning() throws Exception {
+        baseOps.unlockRunning(callback);
+        verifyMessageSent("unlock-running", NetconfMessageTransformUtil.NETCONF_UNLOCK_QNAME);
+    }
+
+    @Test
+    public void testDiscardChanges() throws Exception {
+        baseOps.discardChanges(callback);
+        verifyMessageSent("discardChanges", NetconfMessageTransformUtil.NETCONF_DISCARD_CHANGES_QNAME);
+    }
+
+    @Test
+    public void testCommit() throws Exception {
+        baseOps.commit(callback);
+        verifyMessageSent("commit", NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME);
+    }
+
+    @Test
+    public void testValidateCandidate() throws Exception {
+        baseOps.validateCandidate(callback);
+        verifyMessageSent("validate", NetconfMessageTransformUtil.NETCONF_VALIDATE_QNAME);
+    }
+
+    @Test
+    public void testValidateRunning() throws Exception {
+        baseOps.validateRunning(callback);
+        verifyMessageSent("validate-running", NetconfMessageTransformUtil.NETCONF_VALIDATE_QNAME);
+    }
+
+
+    @Test
+    public void testCopyConfig() throws Exception {
+        baseOps.copyConfig(callback, NetconfMessageTransformUtil.NETCONF_RUNNING_QNAME,
+                NetconfMessageTransformUtil.NETCONF_CANDIDATE_QNAME);
+        verifyMessageSent("copy-config", NetconfMessageTransformUtil.NETCONF_COPY_CONFIG_QNAME);
+    }
+
+    @Test
+    public void testCopyRunningToCandidate() throws Exception {
+        baseOps.copyRunningToCandidate(callback);
+        verifyMessageSent("copy-config", NetconfMessageTransformUtil.NETCONF_COPY_CONFIG_QNAME);
+    }
+
+
+    @Test
+    public void testGetConfigRunningData() throws Exception {
+        final Optional<NormalizedNode<?, ?>> dataOpt =
+                baseOps.getConfigRunningData(callback, Optional.of(YangInstanceIdentifier.EMPTY)).get();
+        Assert.assertTrue(dataOpt.isPresent());
+        Assert.assertEquals(NetconfMessageTransformUtil.NETCONF_DATA_QNAME, dataOpt.get().getNodeType());
+    }
+
+    @Test
+    public void testGetData() throws Exception {
+        final Optional<NormalizedNode<?, ?>> dataOpt =
+                baseOps.getData(callback, Optional.of(YangInstanceIdentifier.EMPTY)).get();
+        Assert.assertTrue(dataOpt.isPresent());
+        Assert.assertEquals(NetconfMessageTransformUtil.NETCONF_DATA_QNAME, dataOpt.get().getNodeType());
+    }
+
+    @Test
+    public void testGetConfigRunning() throws Exception {
+        baseOps.getConfigRunning(callback, Optional.absent());
+        verifyMessageSent("getConfig", NetconfMessageTransformUtil.NETCONF_GET_CONFIG_QNAME);
+    }
+
+    @Test
+    public void testGetConfigCandidate() throws Exception {
+        baseOps.getConfigCandidate(callback, Optional.absent());
+        verifyMessageSent("getConfig_candidate", NetconfMessageTransformUtil.NETCONF_GET_CONFIG_QNAME);
+    }
+
+    @Test
+    public void testGetConfigCandidateWithFilter() throws Exception {
+        final YangInstanceIdentifier id = YangInstanceIdentifier.builder()
+                .node(CONTAINER_Q_NAME)
+                .build();
+        baseOps.getConfigCandidate(callback, Optional.of(id));
+        verifyMessageSent("getConfig_candidate-filter", NetconfMessageTransformUtil.NETCONF_GET_CONFIG_QNAME);
+    }
+
+    @Test
+    public void testGet() throws Exception {
+        baseOps.get(callback, Optional.absent());
+        verifyMessageSent("get", NetconfMessageTransformUtil.NETCONF_GET_QNAME);
+    }
+
+    @Test
+    public void testEditConfigCandidate() throws Exception {
+        final QName leafQName = QName.create(CONTAINER_Q_NAME, "a");
+        final LeafNode<Object> leaf = Builders.leafBuilder()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(leafQName))
+                .withValue("leaf-value")
+                .build();
+        final YangInstanceIdentifier leafId = YangInstanceIdentifier.builder()
+                .node(CONTAINER_Q_NAME)
+                .node(leafQName)
+                .build();
+        final DataContainerChild<?, ?> structure = baseOps.createEditConfigStrcture(Optional.of(leaf),
+                Optional.of(ModifyAction.REPLACE), leafId);
+        baseOps.editConfigCandidate(callback, structure, true);
+        verifyMessageSent("edit-config-test-module", NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME);
+    }
+
+    @Test
+    public void testEditConfigRunning() throws Exception {
+        final QName containerQName = QName.create("test:namespace", "2013-07-22", "c");
+        final QName leafQName = QName.create(containerQName, "a");
+        final LeafNode<Object> leaf = Builders.leafBuilder()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(leafQName))
+                .withValue("leaf-value")
+                .build();
+        final YangInstanceIdentifier leafId = YangInstanceIdentifier.builder()
+                .node(containerQName)
+                .node(leafQName)
+                .build();
+        final DataContainerChild<?, ?> structure = baseOps.createEditConfigStrcture(Optional.of(leaf),
+                Optional.of(ModifyAction.REPLACE), leafId);
+        baseOps.editConfigRunning(callback, structure, ModifyAction.MERGE, true);
+        verifyMessageSent("edit-config-test-module-running", NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME);
+    }
+
+    private static SchemaContext parseYangStreams(final InputStream... streams) {
+        final CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR
+                .newBuild();
+        final SchemaContext schemaContext;
+        try {
+            schemaContext = reactor.buildEffective(Arrays.asList(streams));
+        } catch (final ReactorException e) {
+            throw new RuntimeException("Unable to build schema context from " + streams, e);
+        }
+        return schemaContext;
+    }
+
+    private void verifyMessageSent(final String fileName, final QName name) {
+        final String path = "/netconfMessages/" + fileName + ".xml";
+        verify(listener).sendRequest(msg(path), eq(name));
+    }
+
+    private static NetconfMessage msg(final String name) {
+        final InputStream stream = NetconfBaseOpsTest.class.getResourceAsStream(name);
+        try {
+            return argThat(new NetconfMessageMatcher(XmlUtil.readXmlToDocument(stream)));
+        } catch (SAXException | IOException e) {
+            throw new IllegalStateException("Failed to read xml file " + name, e);
+        }
+    }
+
+    private static class NetconfMessageMatcher extends BaseMatcher<NetconfMessage> {
+
+        private final Document expected;
+
+        private NetconfMessageMatcher(final Document expected) {
+            this.expected = removeAttrs(expected);
+        }
+
+        @Override
+        public boolean matches(final Object item) {
+            if (!(item instanceof NetconfMessage)) {
+                return false;
+            }
+            final NetconfMessage message = (NetconfMessage) item;
+            final Document actualDoc = removeAttrs(message.getDocument());
+            actualDoc.normalizeDocument();
+            expected.normalizeDocument();
+            final Diff diff = XMLUnit.compareXML(expected, actualDoc);
+            return diff.similar();
+        }
+
+        @Override
+        public void describeTo(final Description description) {
+            description.appendText(XmlUtil.toString(expected));
+        }
+
+        private static Document removeAttrs(final Document input) {
+            final Document copy = XmlUtil.newDocument();
+            copy.appendChild(copy.importNode(input.getDocumentElement(), true));
+            final Element element = copy.getDocumentElement();
+            final List<String> attrNames = new ArrayList<>();
+            final NamedNodeMap attributes = element.getAttributes();
+            for (int i = 0; i < attributes.getLength(); i++) {
+                final String nodeName = attributes.item(i).getNodeName();
+                if ("xmlns".equals(nodeName)) {
+                    continue;
+                }
+                attrNames.add(nodeName);
+            }
+            attrNames.forEach(element::removeAttribute);
+            return copy;
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/util/NodeContainerProxyTest.java b/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/util/NodeContainerProxyTest.java
new file mode 100644 (file)
index 0000000..534a952
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * 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.util;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+public class NodeContainerProxyTest {
+
+    private static final QName QNAME = QName.create("ns", "2016-10-19", "name");
+    private static final QName NODE_1_QNAME = QName.create(QNAME, "node-1");
+    private static final QName NODE_2_QNAME = QName.create(QNAME, "node-2");
+    @Mock
+    private AugmentationSchema augSchema1;
+    @Mock
+    private AugmentationSchema augSchema2;
+    @Mock
+    private DataSchemaNode schemaNode1;
+    @Mock
+    private DataSchemaNode schemaNode2;
+    private NodeContainerProxy proxy;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        final Map<QName, DataSchemaNode> childNodes = new HashMap<>();
+        childNodes.put(NODE_1_QNAME, schemaNode1);
+        childNodes.put(NODE_2_QNAME, schemaNode2);
+        final Set<AugmentationSchema> augmentations = new HashSet<>();
+        augmentations.add(augSchema1);
+        augmentations.add(augSchema2);
+        proxy = new NodeContainerProxy(QNAME, childNodes, augmentations);
+    }
+
+    @Test
+    public void testGetQName() throws Exception {
+        Assert.assertEquals(QNAME, proxy.getQName());
+    }
+
+    @Test
+    public void testGetChildNodes() throws Exception {
+        Assert.assertEquals(2, proxy.getChildNodes().size());
+    }
+
+    @Test
+    public void testGetAvailableAugmentations() throws Exception {
+        final Set<AugmentationSchema> augmentations = proxy.getAvailableAugmentations();
+        Assert.assertEquals(2, augmentations.size());
+        Assert.assertTrue(augmentations.contains(augSchema1));
+        Assert.assertTrue(augmentations.contains(augSchema2));
+    }
+
+    @Test
+    public void testGetDataChildByName() throws Exception {
+        final DataSchemaNode schemaNode = proxy.getDataChildByName(NODE_1_QNAME);
+        Assert.assertEquals(schemaNode1, schemaNode);
+    }
+
+    @Test
+    public void testGetTypeDefinitions() throws Exception {
+        Assert.assertTrue(proxy.getTypeDefinitions().isEmpty());
+    }
+
+    @Test
+    public void testGetGroupings() throws Exception {
+        Assert.assertTrue(proxy.getGroupings().isEmpty());
+    }
+
+    @Test
+    public void testGetUses() throws Exception {
+        Assert.assertTrue(proxy.getUses().isEmpty());
+    }
+
+    @Test
+    public void testGetUnknownSchemaNodes() throws Exception {
+        Assert.assertTrue(proxy.getUnknownSchemaNodes().isEmpty());
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testIsPresenceContainer() throws Exception {
+        proxy.isPresenceContainer();
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testIsAugmenting() throws Exception {
+        proxy.isAugmenting();
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testIsAddedByUses() throws Exception {
+        proxy.isAddedByUses();
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testIsConfiguration() throws Exception {
+        proxy.isConfiguration();
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testGetConstraints() throws Exception {
+        proxy.getConstraints();
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testGetPath() throws Exception {
+        proxy.getPath();
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testGetDescription() throws Exception {
+        proxy.getDescription();
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testGetReference() throws Exception {
+        proxy.getReference();
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testGetStatus() throws Exception {
+        proxy.getStatus();
+    }
+
+}
\ No newline at end of file