2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.yang.data.operations;
11 import java.io.IOException;
12 import java.io.InputStream;
13 import java.io.StringWriter;
15 import java.util.Arrays;
16 import java.util.Collection;
17 import java.util.Collections;
18 import java.util.List;
21 import javax.activation.UnsupportedDataTypeException;
22 import javax.xml.parsers.DocumentBuilder;
23 import javax.xml.parsers.DocumentBuilderFactory;
24 import javax.xml.parsers.ParserConfigurationException;
25 import javax.xml.transform.OutputKeys;
26 import javax.xml.transform.Transformer;
27 import javax.xml.transform.TransformerException;
28 import javax.xml.transform.TransformerFactory;
29 import javax.xml.transform.TransformerFactoryConfigurationError;
30 import javax.xml.transform.dom.DOMSource;
31 import javax.xml.transform.stream.StreamResult;
33 import org.junit.Assert;
34 import org.junit.Test;
35 import org.junit.runner.RunWith;
36 import org.junit.runners.Parameterized;
37 import org.opendaylight.yangtools.yang.data.api.ModifyAction;
38 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
39 import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.DomUtils;
40 import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser.DomToNormalizedNodeParserFactory;
41 import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.serializer.DomFromNormalizedNodeSerializerFactory;
42 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
43 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
44 import org.opendaylight.yangtools.yang.model.api.Module;
45 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
46 import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
47 import org.w3c.dom.Document;
48 import org.w3c.dom.Element;
49 import org.xml.sax.SAXException;
51 import com.google.common.base.Charsets;
52 import com.google.common.base.Function;
53 import com.google.common.base.Optional;
54 import com.google.common.base.Preconditions;
55 import com.google.common.collect.Collections2;
56 import com.google.common.collect.Lists;
57 import com.google.common.io.Files;
59 @RunWith(Parameterized.class)
60 public class YangDataOperationsTest {
62 public static final String CURRENT_XML_NAME = "/current.xml";
63 public static final String MODIFICATION_XML_NAME = "/merge.xml";
64 private static final String XML_FOLDER_NAME = "/xmls";
65 public static final String RESULT_XML_NAME = "/result.xml";
66 private static final Object OPERATION_XML_NAME = "/defaultOperation.txt";
68 protected final ContainerSchemaNode containerNode;
69 protected final String testDirName;
70 protected final Optional<ContainerNode> currentConfig;
71 protected final Optional<ContainerNode> modification;
72 protected final ModifyAction modifyAction;
74 @Parameterized.Parameters()
75 public static Collection<Object[]> data() {
76 return Arrays.asList(new Object[][] {
78 { "/containerTest_createContainer" },
79 { "/containerTest_deleteContainer" },
80 { "/containerTest_innerContainerContainer" },
81 { "/containerTest_innerLeavesBaseOperationsContainer" },
82 { "/containerTest_noneContainer" },
83 { "/containerTest_removeContainer"},
84 { "/containerTest_replaceContainer"},
85 { "/containerTest_choiceActualModificationSameCase"},
86 { "/containerTest_choiceActualModificationDifferentCases"},
87 { "/containerTest_choiceActualOneCaseModificationOtherCase"},
94 {"/listTest_alterInnerValue"}
98 public YangDataOperationsTest(String testDir) throws Exception {
99 SchemaContext schema = parseTestSchema();
100 containerNode = (ContainerSchemaNode) getSchemaNode(schema, "test", "container");
101 this.testDirName = testDir;
103 currentConfig = loadXmlToCompositeNode(getXmlFolderName() + testDirName + CURRENT_XML_NAME);
104 modification = loadXmlToCompositeNode(getXmlFolderName() + testDirName + MODIFICATION_XML_NAME);
105 Preconditions.checkState(modification.isPresent(), "Modification xml has to be present under "
106 + getXmlFolderName() + testDirName + MODIFICATION_XML_NAME);
108 modifyAction = loadModifyAction(getXmlFolderName() + testDirName + OPERATION_XML_NAME);
111 protected String getXmlFolderName() {
112 return XML_FOLDER_NAME;
115 // TODO unite testing resources e.g. schemas with yang-data-impl
116 // TODO create extract common testing infrastructure from this and yang-data-impl e.g. xml dom handling
119 public void testModification() throws Exception {
121 Optional<ContainerNode> result = DataOperations.modify(containerNode,
122 currentConfig.orNull(), modification.orNull(), modifyAction);
124 String expectedResultXmlPath = getXmlFolderName() + testDirName + RESULT_XML_NAME;
125 Optional<ContainerNode> expectedResult = loadXmlToCompositeNode(expectedResultXmlPath);
127 if (result.isPresent()) {
128 verifyModificationResult(result, expectedResult);
130 junit.framework.Assert.assertNull("Result of modification is empty node, result xml should not be present "
131 + expectedResultXmlPath, getClass().getResourceAsStream(expectedResultXmlPath));
136 private ModifyAction loadModifyAction(String path) throws Exception {
137 URL resource = getClass().getResource(path);
138 if (resource == null) {
139 return ModifyAction.MERGE;
142 return ModifyAction.fromXmlValue(Files.toString(new File(resource.toURI()), Charsets.UTF_8).trim());
145 private void verifyModificationResult(Optional<ContainerNode> result, Optional<ContainerNode> expectedResult)
146 throws UnsupportedDataTypeException {
149 "Test result %n %s %n Expected %n %s %n",
150 toString(toDom(result.get())),
151 toString(toDom(expectedResult.get()))), expectedResult.get(), result.get());
154 private Element toDom(ContainerNode container) {
155 Iterable<Element> a =
156 DomFromNormalizedNodeSerializerFactory.getInstance(newDocument(), DomUtils.defaultValueCodecProvider())
157 .getContainerNodeSerializer().serialize(containerNode, container);
158 return a.iterator().next();
161 private Document newDocument() {
163 return BUILDERFACTORY.newDocumentBuilder().newDocument();
164 } catch (ParserConfigurationException e) {
165 throw new RuntimeException("Failed to parse XML document", e);
169 private Optional<ContainerNode> loadXmlToCompositeNode(String xmlPath) throws IOException, SAXException {
170 InputStream resourceAsStream = getClass().getResourceAsStream(xmlPath);
171 if (resourceAsStream == null) {
172 return Optional.absent();
175 Document currentConfigElement = readXmlToDocument(resourceAsStream);
176 Preconditions.checkNotNull(currentConfigElement);
178 return Optional.of(DomToNormalizedNodeParserFactory.getInstance(DomUtils.defaultValueCodecProvider()).getContainerNodeParser().parse(
179 Collections.singletonList(currentConfigElement.getDocumentElement()), containerNode));
182 SchemaContext parseTestSchema() {
183 YangParserImpl yangParserImpl = new YangParserImpl();
184 Set<Module> modules = yangParserImpl.parseYangModelsFromStreams(getTestYangs());
185 return yangParserImpl.resolveSchemaContext(modules);
188 List<InputStream> getTestYangs() {
190 return Lists.newArrayList(Collections2.transform(Lists.newArrayList("/schemas/test.yang"),
191 new Function<String, InputStream>() {
193 public InputStream apply(String input) {
194 InputStream resourceAsStream = getClass().getResourceAsStream(input);
195 Preconditions.checkNotNull(resourceAsStream, "File %s was null", resourceAsStream);
196 return resourceAsStream;
201 DataSchemaNode getSchemaNode(SchemaContext context, String moduleName, String childNodeName) {
202 for (Module module : context.getModules()) {
203 if (module.getName().equals(moduleName)) {
204 for (DataSchemaNode dataSchemaNode : module.getChildNodes()) {
205 if (dataSchemaNode.getQName().getLocalName().equals(childNodeName))
206 return dataSchemaNode;
211 throw new IllegalStateException("Unable to find child node " + childNodeName);
214 private static final DocumentBuilderFactory BUILDERFACTORY;
217 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
218 factory.setNamespaceAware(true);
219 factory.setCoalescing(true);
220 factory.setIgnoringElementContentWhitespace(true);
221 factory.setIgnoringComments(true);
222 BUILDERFACTORY = factory;
225 private Document readXmlToDocument(InputStream xmlContent) throws IOException, SAXException {
226 DocumentBuilder dBuilder;
228 dBuilder = BUILDERFACTORY.newDocumentBuilder();
229 } catch (ParserConfigurationException e) {
230 throw new RuntimeException("Failed to parse XML document", e);
232 Document doc = dBuilder.parse(xmlContent);
234 doc.getDocumentElement().normalize();
238 public static String toString(Element xml) {
240 Transformer transformer = TransformerFactory.newInstance().newTransformer();
241 transformer.setOutputProperty(OutputKeys.INDENT, "yes");
243 StreamResult result = new StreamResult(new StringWriter());
244 DOMSource source = new DOMSource(xml);
245 transformer.transform(source, result);
247 return result.getWriter().toString();
248 } catch (IllegalArgumentException | TransformerFactoryConfigurationError | TransformerException e) {
249 throw new RuntimeException("Unable to serialize xml element " + xml, e);