f05db1e5da95e06c6c40afc4acf67d1020467368
[yangtools.git] / yang / yang-data-operations / src / test / java / org / opendaylight / yangtools / yang / data / operations / YangDataOperationsTest.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang.data.operations;
9
10 import static org.junit.Assert.assertNull;
11
12 import com.google.common.base.Charsets;
13 import com.google.common.base.Function;
14 import com.google.common.base.Optional;
15 import com.google.common.base.Preconditions;
16 import com.google.common.collect.Collections2;
17 import com.google.common.collect.Lists;
18 import com.google.common.io.Files;
19
20 import java.io.File;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.StringWriter;
24 import java.net.URL;
25 import java.util.Arrays;
26 import java.util.Collection;
27 import java.util.Collections;
28 import java.util.List;
29 import java.util.Set;
30
31 import javax.activation.UnsupportedDataTypeException;
32 import javax.xml.parsers.DocumentBuilder;
33 import javax.xml.parsers.DocumentBuilderFactory;
34 import javax.xml.parsers.ParserConfigurationException;
35 import javax.xml.transform.OutputKeys;
36 import javax.xml.transform.Transformer;
37 import javax.xml.transform.TransformerException;
38 import javax.xml.transform.TransformerFactory;
39 import javax.xml.transform.TransformerFactoryConfigurationError;
40 import javax.xml.transform.dom.DOMSource;
41 import javax.xml.transform.stream.StreamResult;
42
43 import org.junit.Assert;
44 import org.junit.Test;
45 import org.junit.runner.RunWith;
46 import org.junit.runners.Parameterized;
47 import org.opendaylight.yangtools.yang.data.api.ModifyAction;
48 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
49 import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.DomUtils;
50 import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser.DomToNormalizedNodeParserFactory;
51 import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.serializer.DomFromNormalizedNodeSerializerFactory;
52 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
53 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
54 import org.opendaylight.yangtools.yang.model.api.Module;
55 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
56 import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
57 import org.w3c.dom.Document;
58 import org.w3c.dom.Element;
59 import org.xml.sax.SAXException;
60
61 @RunWith(Parameterized.class)
62 public class YangDataOperationsTest {
63
64     public static final String CURRENT_XML_NAME = "/current.xml";
65     public static final String MODIFICATION_XML_NAME = "/merge.xml";
66     private static final String XML_FOLDER_NAME = "/xmls";
67     public static final String RESULT_XML_NAME = "/result.xml";
68     private static final Object OPERATION_XML_NAME = "/defaultOperation.txt";
69
70     protected final ContainerSchemaNode containerNode;
71     protected final String testDirName;
72     protected final Optional<ContainerNode> currentConfig;
73     protected final Optional<ContainerNode> modification;
74     protected final ModifyAction modifyAction;
75     private final SchemaContext schema;
76
77     @Parameterized.Parameters()
78     public static Collection<Object[]> data() {
79         return Arrays.asList(new Object[][] {
80                 // Container
81                 { "/containerTest_createContainer" },
82                 { "/containerTest_deleteContainer" },
83                 { "/containerTest_innerContainerContainer" },
84                 { "/containerTest_innerLeavesBaseOperationsContainer" },
85                 { "/containerTest_noneContainer" },
86                 { "/containerTest_removeContainer"},
87                 { "/containerTest_replaceContainer"},
88                 { "/containerTest_choiceActualModificationSameCase"},
89                 { "/containerTest_choiceActualModificationDifferentCases"},
90                 { "/containerTest_choiceActualOneCaseModificationOtherCase"},
91                 //            LeafList
92                 { "/leafListTest" },
93                 // List
94                 { "/listTest" },
95                 // Additional
96                 {"/none_NoChange"},
97                 {"/listTest_alterInnerValue"}
98         });
99     }
100
101     public YangDataOperationsTest(final String testDir) throws Exception {
102         schema = parseTestSchema();
103         containerNode = (ContainerSchemaNode) getSchemaNode(schema, "test", "container");
104         this.testDirName = testDir;
105
106         currentConfig = loadXmlToCompositeNode(getXmlFolderName() + testDirName + CURRENT_XML_NAME);
107         modification = loadXmlToCompositeNode(getXmlFolderName() + testDirName + MODIFICATION_XML_NAME);
108         Preconditions.checkState(modification.isPresent(), "Modification xml has to be present under "
109                 + getXmlFolderName() + testDirName + MODIFICATION_XML_NAME);
110
111         modifyAction = loadModifyAction(getXmlFolderName() + testDirName + OPERATION_XML_NAME);
112     }
113
114     protected String getXmlFolderName() {
115         return XML_FOLDER_NAME;
116     }
117
118     // TODO unite testing resources e.g. schemas with yang-data-impl
119     // TODO create extract common testing infrastructure from this and yang-data-impl e.g. xml dom handling
120
121     @Test
122     public void testModification() throws Exception {
123
124         Optional<ContainerNode> result = DataOperations.modify(containerNode,
125                 currentConfig.orNull(), modification.orNull(), modifyAction);
126
127         String expectedResultXmlPath = getXmlFolderName() + testDirName + RESULT_XML_NAME;
128         Optional<ContainerNode> expectedResult = loadXmlToCompositeNode(expectedResultXmlPath);
129
130         if (result.isPresent()) {
131             verifyModificationResult(result, expectedResult);
132         } else {
133             assertNull("Result of modification is empty node, result xml should not be present "
134                     + expectedResultXmlPath, getClass().getResourceAsStream(expectedResultXmlPath));
135         }
136
137     }
138
139     private ModifyAction loadModifyAction(final String path) throws Exception {
140         URL resource = getClass().getResource(path);
141         if (resource == null) {
142             return ModifyAction.MERGE;
143         }
144
145         return ModifyAction.fromXmlValue(Files.toString(new File(resource.toURI()), Charsets.UTF_8).trim());
146     }
147
148     private void verifyModificationResult(final Optional<ContainerNode> result, final Optional<ContainerNode> expectedResult)
149             throws UnsupportedDataTypeException {
150         Assert.assertEquals(
151                 String.format(
152                         "Test result %n %s %n Expected %n %s %n",
153                         toString(toDom(result.get())),
154                         toString(toDom(expectedResult.get()))), expectedResult.get(), result.get());
155     }
156
157     private Element toDom(final ContainerNode container) {
158         Iterable<Element> a =
159                 DomFromNormalizedNodeSerializerFactory.getInstance(newDocument(), DomUtils.defaultValueCodecProvider())
160                 .getContainerNodeSerializer().serialize(containerNode, container);
161         return a.iterator().next();
162     }
163
164     private Document newDocument() {
165         try {
166             return BUILDERFACTORY.newDocumentBuilder().newDocument();
167         } catch (ParserConfigurationException e) {
168             throw new RuntimeException("Failed to parse XML document", e);
169         }
170     }
171
172     private Optional<ContainerNode> loadXmlToCompositeNode(final String xmlPath) throws IOException, SAXException {
173         InputStream resourceAsStream = getClass().getResourceAsStream(xmlPath);
174         if (resourceAsStream == null) {
175             return Optional.absent();
176         }
177
178         Document currentConfigElement = readXmlToDocument(resourceAsStream);
179         Preconditions.checkNotNull(currentConfigElement);
180
181         return Optional.of(DomToNormalizedNodeParserFactory.getInstance(DomUtils.defaultValueCodecProvider(), schema).getContainerNodeParser().parse(
182                 Collections.singletonList(currentConfigElement.getDocumentElement()), containerNode));
183     }
184
185     SchemaContext parseTestSchema() {
186         YangParserImpl yangParserImpl = new YangParserImpl();
187         Set<Module> modules = yangParserImpl.parseYangModelsFromStreams(getTestYangs());
188         return yangParserImpl.resolveSchemaContext(modules);
189     }
190
191     List<InputStream> getTestYangs() {
192
193         return Lists.newArrayList(Collections2.transform(Lists.newArrayList("/schemas/test.yang"),
194                 new Function<String, InputStream>() {
195             @Override
196             public InputStream apply(final String input) {
197                 InputStream resourceAsStream = getClass().getResourceAsStream(input);
198                 Preconditions.checkNotNull(resourceAsStream, "File %s was null", resourceAsStream);
199                 return resourceAsStream;
200             }
201         }));
202     }
203
204     DataSchemaNode getSchemaNode(final SchemaContext context, final String moduleName, final String childNodeName) {
205         for (Module module : context.getModules()) {
206             if (module.getName().equals(moduleName)) {
207                 for (DataSchemaNode dataSchemaNode : module.getChildNodes()) {
208                     if (dataSchemaNode.getQName().getLocalName().equals(childNodeName)) {
209                         return dataSchemaNode;
210                     }
211                 }
212             }
213         }
214
215         throw new IllegalStateException("Unable to find child node " + childNodeName);
216     }
217
218     private static final DocumentBuilderFactory BUILDERFACTORY;
219
220     static {
221         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
222         factory.setNamespaceAware(true);
223         factory.setCoalescing(true);
224         factory.setIgnoringElementContentWhitespace(true);
225         factory.setIgnoringComments(true);
226         BUILDERFACTORY = factory;
227     }
228
229     private Document readXmlToDocument(final InputStream xmlContent) throws IOException, SAXException {
230         DocumentBuilder dBuilder;
231         try {
232             dBuilder = BUILDERFACTORY.newDocumentBuilder();
233         } catch (ParserConfigurationException e) {
234             throw new RuntimeException("Failed to parse XML document", e);
235         }
236         Document doc = dBuilder.parse(xmlContent);
237
238         doc.getDocumentElement().normalize();
239         return doc;
240     }
241
242     public static String toString(final Element xml) {
243         try {
244             Transformer transformer = TransformerFactory.newInstance().newTransformer();
245             transformer.setOutputProperty(OutputKeys.INDENT, "yes");
246
247             StreamResult result = new StreamResult(new StringWriter());
248             DOMSource source = new DOMSource(xml);
249             transformer.transform(source, result);
250
251             return result.getWriter().toString();
252         } catch (IllegalArgumentException | TransformerFactoryConfigurationError | TransformerException e) {
253             throw new RuntimeException("Unable to serialize xml element " + xml, e);
254         }
255     }
256
257 }