Add more unit tests for sal-netconf-connector
[netconf.git] / netconf / sal-netconf-connector / src / test / java / org / opendaylight / netconf / sal / connect / netconf / util / NetconfBaseOpsTest.java
1 /*
2  * Copyright (c) 2016 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
9 package org.opendaylight.netconf.sal.connect.netconf.util;
10
11 import static org.mockito.Matchers.any;
12 import static org.mockito.Matchers.argThat;
13 import static org.mockito.Matchers.eq;
14 import static org.mockito.Mockito.verify;
15 import static org.mockito.Mockito.when;
16
17 import com.google.common.base.Optional;
18 import java.io.IOException;
19 import java.io.InputStream;
20 import java.net.InetSocketAddress;
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.List;
24 import org.custommonkey.xmlunit.Diff;
25 import org.custommonkey.xmlunit.XMLUnit;
26 import org.hamcrest.BaseMatcher;
27 import org.hamcrest.Description;
28 import org.junit.Assert;
29 import org.junit.Before;
30 import org.junit.Test;
31 import org.mockito.Mock;
32 import org.mockito.MockitoAnnotations;
33 import org.opendaylight.controller.config.util.xml.XmlUtil;
34 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
35 import org.opendaylight.netconf.api.NetconfMessage;
36 import org.opendaylight.netconf.sal.connect.api.MessageTransformer;
37 import org.opendaylight.netconf.sal.connect.api.RemoteDeviceCommunicator;
38 import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfDeviceRpc;
39 import org.opendaylight.netconf.sal.connect.netconf.schema.mapping.NetconfMessageTransformer;
40 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
41 import org.opendaylight.yangtools.yang.common.QName;
42 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
43 import org.opendaylight.yangtools.yang.data.api.ModifyAction;
44 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
45 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
46 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
47 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
48 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
49 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
50 import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
51 import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
52 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline;
53 import org.w3c.dom.Document;
54 import org.w3c.dom.Element;
55 import org.w3c.dom.NamedNodeMap;
56 import org.xml.sax.SAXException;
57
58 public class NetconfBaseOpsTest {
59
60     static {
61         XMLUnit.setIgnoreWhitespace(true);
62         XMLUnit.setIgnoreComments(true);
63     }
64
65     private static final QName CONTAINER_Q_NAME = QName.create("test:namespace", "2013-07-22", "c");
66
67     @Mock
68     private RemoteDeviceCommunicator<NetconfMessage> listener;
69     private NetconfRpcFutureCallback callback;
70     private NetconfBaseOps baseOps;
71
72     @Before
73     public void setUp() throws Exception {
74         MockitoAnnotations.initMocks(this);
75         final InputStream okStream = getClass().getResourceAsStream("/netconfMessages/rpc-reply_ok.xml");
76         final InputStream dataStream = getClass().getResourceAsStream("/netconfMessages/rpc-reply_get.xml");
77         final NetconfMessage ok = new NetconfMessage(XmlUtil.readXmlToDocument(okStream));
78         final NetconfMessage data = new NetconfMessage(XmlUtil.readXmlToDocument(dataStream));
79         when(listener.sendRequest(any(), eq(NetconfMessageTransformUtil.NETCONF_GET_CONFIG_QNAME)))
80                 .thenReturn(RpcResultBuilder.success(data).buildFuture());
81         when(listener.sendRequest(any(), eq(NetconfMessageTransformUtil.NETCONF_GET_QNAME)))
82                 .thenReturn(RpcResultBuilder.success(data).buildFuture());
83         when(listener.sendRequest(any(), eq(NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME)))
84                 .thenReturn(RpcResultBuilder.success(ok).buildFuture());
85         when(listener.sendRequest(any(), eq(NetconfMessageTransformUtil.NETCONF_COPY_CONFIG_QNAME)))
86                 .thenReturn(RpcResultBuilder.success(ok).buildFuture());
87         when(listener.sendRequest(any(), eq(NetconfMessageTransformUtil.NETCONF_DISCARD_CHANGES_QNAME)))
88                 .thenReturn(RpcResultBuilder.success(ok).buildFuture());
89         when(listener.sendRequest(any(), eq(NetconfMessageTransformUtil.NETCONF_VALIDATE_QNAME)))
90                 .thenReturn(RpcResultBuilder.success(ok).buildFuture());
91         when(listener.sendRequest(any(), eq(NetconfMessageTransformUtil.NETCONF_LOCK_QNAME)))
92                 .thenReturn(RpcResultBuilder.success(ok).buildFuture());
93         when(listener.sendRequest(any(), eq(NetconfMessageTransformUtil.NETCONF_UNLOCK_QNAME)))
94                 .thenReturn(RpcResultBuilder.success(ok).buildFuture());
95         when(listener.sendRequest(any(), eq(NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME)))
96                 .thenReturn(RpcResultBuilder.success(ok).buildFuture());
97         final SchemaContext schemaContext =
98                 parseYangStreams(getClass().getResourceAsStream("/schemas/test-module.yang"));
99         final MessageTransformer<NetconfMessage> transformer = new NetconfMessageTransformer(schemaContext, true);
100         final DOMRpcService rpc = new NetconfDeviceRpc(schemaContext, listener, transformer);
101         final RemoteDeviceId id =
102                 new RemoteDeviceId("device-1", InetSocketAddress.createUnresolved("localhost", 17830));
103         callback = new NetconfRpcFutureCallback("prefix", id);
104         baseOps = new NetconfBaseOps(rpc, schemaContext);
105     }
106
107     @Test
108     public void testLock() throws Exception {
109         baseOps.lock(callback, NetconfMessageTransformUtil.NETCONF_CANDIDATE_QNAME);
110         verifyMessageSent("lock", NetconfMessageTransformUtil.NETCONF_LOCK_QNAME);
111     }
112
113     @Test
114     public void testLockCandidate() throws Exception {
115         baseOps.lockCandidate(callback);
116         verifyMessageSent("lock", NetconfMessageTransformUtil.NETCONF_LOCK_QNAME);
117     }
118
119     @Test
120     public void testUnlock() throws Exception {
121         baseOps.unlock(callback, NetconfMessageTransformUtil.NETCONF_CANDIDATE_QNAME);
122         verifyMessageSent("unlock", NetconfMessageTransformUtil.NETCONF_UNLOCK_QNAME);
123     }
124
125     @Test
126     public void testUnlockCandidate() throws Exception {
127         baseOps.unlockCandidate(callback);
128         verifyMessageSent("unlock", NetconfMessageTransformUtil.NETCONF_UNLOCK_QNAME);
129     }
130
131     @Test
132     public void testLockRunning() throws Exception {
133         baseOps.lockRunning(callback);
134         verifyMessageSent("lock-running", NetconfMessageTransformUtil.NETCONF_LOCK_QNAME);
135     }
136
137     @Test
138     public void testUnlockRunning() throws Exception {
139         baseOps.unlockRunning(callback);
140         verifyMessageSent("unlock-running", NetconfMessageTransformUtil.NETCONF_UNLOCK_QNAME);
141     }
142
143     @Test
144     public void testDiscardChanges() throws Exception {
145         baseOps.discardChanges(callback);
146         verifyMessageSent("discardChanges", NetconfMessageTransformUtil.NETCONF_DISCARD_CHANGES_QNAME);
147     }
148
149     @Test
150     public void testCommit() throws Exception {
151         baseOps.commit(callback);
152         verifyMessageSent("commit", NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME);
153     }
154
155     @Test
156     public void testValidateCandidate() throws Exception {
157         baseOps.validateCandidate(callback);
158         verifyMessageSent("validate", NetconfMessageTransformUtil.NETCONF_VALIDATE_QNAME);
159     }
160
161     @Test
162     public void testValidateRunning() throws Exception {
163         baseOps.validateRunning(callback);
164         verifyMessageSent("validate-running", NetconfMessageTransformUtil.NETCONF_VALIDATE_QNAME);
165     }
166
167
168     @Test
169     public void testCopyConfig() throws Exception {
170         baseOps.copyConfig(callback, NetconfMessageTransformUtil.NETCONF_RUNNING_QNAME,
171                 NetconfMessageTransformUtil.NETCONF_CANDIDATE_QNAME);
172         verifyMessageSent("copy-config", NetconfMessageTransformUtil.NETCONF_COPY_CONFIG_QNAME);
173     }
174
175     @Test
176     public void testCopyRunningToCandidate() throws Exception {
177         baseOps.copyRunningToCandidate(callback);
178         verifyMessageSent("copy-config", NetconfMessageTransformUtil.NETCONF_COPY_CONFIG_QNAME);
179     }
180
181
182     @Test
183     public void testGetConfigRunningData() throws Exception {
184         final Optional<NormalizedNode<?, ?>> dataOpt =
185                 baseOps.getConfigRunningData(callback, Optional.of(YangInstanceIdentifier.EMPTY)).get();
186         Assert.assertTrue(dataOpt.isPresent());
187         Assert.assertEquals(NetconfMessageTransformUtil.NETCONF_DATA_QNAME, dataOpt.get().getNodeType());
188     }
189
190     @Test
191     public void testGetData() throws Exception {
192         final Optional<NormalizedNode<?, ?>> dataOpt =
193                 baseOps.getData(callback, Optional.of(YangInstanceIdentifier.EMPTY)).get();
194         Assert.assertTrue(dataOpt.isPresent());
195         Assert.assertEquals(NetconfMessageTransformUtil.NETCONF_DATA_QNAME, dataOpt.get().getNodeType());
196     }
197
198     @Test
199     public void testGetConfigRunning() throws Exception {
200         baseOps.getConfigRunning(callback, Optional.absent());
201         verifyMessageSent("getConfig", NetconfMessageTransformUtil.NETCONF_GET_CONFIG_QNAME);
202     }
203
204     @Test
205     public void testGetConfigCandidate() throws Exception {
206         baseOps.getConfigCandidate(callback, Optional.absent());
207         verifyMessageSent("getConfig_candidate", NetconfMessageTransformUtil.NETCONF_GET_CONFIG_QNAME);
208     }
209
210     @Test
211     public void testGetConfigCandidateWithFilter() throws Exception {
212         final YangInstanceIdentifier id = YangInstanceIdentifier.builder()
213                 .node(CONTAINER_Q_NAME)
214                 .build();
215         baseOps.getConfigCandidate(callback, Optional.of(id));
216         verifyMessageSent("getConfig_candidate-filter", NetconfMessageTransformUtil.NETCONF_GET_CONFIG_QNAME);
217     }
218
219     @Test
220     public void testGet() throws Exception {
221         baseOps.get(callback, Optional.absent());
222         verifyMessageSent("get", NetconfMessageTransformUtil.NETCONF_GET_QNAME);
223     }
224
225     @Test
226     public void testEditConfigCandidate() throws Exception {
227         final QName leafQName = QName.create(CONTAINER_Q_NAME, "a");
228         final LeafNode<Object> leaf = Builders.leafBuilder()
229                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(leafQName))
230                 .withValue("leaf-value")
231                 .build();
232         final YangInstanceIdentifier leafId = YangInstanceIdentifier.builder()
233                 .node(CONTAINER_Q_NAME)
234                 .node(leafQName)
235                 .build();
236         final DataContainerChild<?, ?> structure = baseOps.createEditConfigStrcture(Optional.of(leaf),
237                 Optional.of(ModifyAction.REPLACE), leafId);
238         baseOps.editConfigCandidate(callback, structure, true);
239         verifyMessageSent("edit-config-test-module", NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME);
240     }
241
242     @Test
243     public void testEditConfigRunning() throws Exception {
244         final QName containerQName = QName.create("test:namespace", "2013-07-22", "c");
245         final QName leafQName = QName.create(containerQName, "a");
246         final LeafNode<Object> leaf = Builders.leafBuilder()
247                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(leafQName))
248                 .withValue("leaf-value")
249                 .build();
250         final YangInstanceIdentifier leafId = YangInstanceIdentifier.builder()
251                 .node(containerQName)
252                 .node(leafQName)
253                 .build();
254         final DataContainerChild<?, ?> structure = baseOps.createEditConfigStrcture(Optional.of(leaf),
255                 Optional.of(ModifyAction.REPLACE), leafId);
256         baseOps.editConfigRunning(callback, structure, ModifyAction.MERGE, true);
257         verifyMessageSent("edit-config-test-module-running", NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME);
258     }
259
260     private static SchemaContext parseYangStreams(final InputStream... streams) {
261         final CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR
262                 .newBuild();
263         final SchemaContext schemaContext;
264         try {
265             schemaContext = reactor.buildEffective(Arrays.asList(streams));
266         } catch (final ReactorException e) {
267             throw new RuntimeException("Unable to build schema context from " + streams, e);
268         }
269         return schemaContext;
270     }
271
272     private void verifyMessageSent(final String fileName, final QName name) {
273         final String path = "/netconfMessages/" + fileName + ".xml";
274         verify(listener).sendRequest(msg(path), eq(name));
275     }
276
277     private static NetconfMessage msg(final String name) {
278         final InputStream stream = NetconfBaseOpsTest.class.getResourceAsStream(name);
279         try {
280             return argThat(new NetconfMessageMatcher(XmlUtil.readXmlToDocument(stream)));
281         } catch (SAXException | IOException e) {
282             throw new IllegalStateException("Failed to read xml file " + name, e);
283         }
284     }
285
286     private static class NetconfMessageMatcher extends BaseMatcher<NetconfMessage> {
287
288         private final Document expected;
289
290         private NetconfMessageMatcher(final Document expected) {
291             this.expected = removeAttrs(expected);
292         }
293
294         @Override
295         public boolean matches(final Object item) {
296             if (!(item instanceof NetconfMessage)) {
297                 return false;
298             }
299             final NetconfMessage message = (NetconfMessage) item;
300             final Document actualDoc = removeAttrs(message.getDocument());
301             actualDoc.normalizeDocument();
302             expected.normalizeDocument();
303             final Diff diff = XMLUnit.compareXML(expected, actualDoc);
304             return diff.similar();
305         }
306
307         @Override
308         public void describeTo(final Description description) {
309             description.appendText(XmlUtil.toString(expected));
310         }
311
312         private static Document removeAttrs(final Document input) {
313             final Document copy = XmlUtil.newDocument();
314             copy.appendChild(copy.importNode(input.getDocumentElement(), true));
315             final Element element = copy.getDocumentElement();
316             final List<String> attrNames = new ArrayList<>();
317             final NamedNodeMap attributes = element.getAttributes();
318             for (int i = 0; i < attributes.getLength(); i++) {
319                 final String nodeName = attributes.item(i).getNodeName();
320                 if ("xmlns".equals(nodeName)) {
321                     continue;
322                 }
323                 attrNames.add(nodeName);
324             }
325             attrNames.forEach(element::removeAttribute);
326             return copy;
327         }
328     }
329
330 }