1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2019 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle;
21  
22  import java.io.ByteArrayOutputStream;
23  import java.nio.charset.StandardCharsets;
24  import java.util.Set;
25  import java.util.function.BiPredicate;
26  
27  import javax.xml.parsers.ParserConfigurationException;
28  
29  import org.junit.Assert;
30  import org.w3c.dom.Document;
31  import org.w3c.dom.NamedNodeMap;
32  import org.w3c.dom.Node;
33  
34  import com.puppycrawl.tools.checkstyle.internal.utils.XmlUtil;
35  
36  public abstract class AbstractXmlTestSupport extends AbstractModuleTestSupport {
37  
38      protected static Document getOutputStreamXml(ByteArrayOutputStream outputStream)
39              throws ParserConfigurationException {
40          final String xml = new String(outputStream.toByteArray(), StandardCharsets.UTF_8);
41  
42          return XmlUtil.getRawXml("audit output", xml, xml);
43      }
44  
45      protected static void verifyXml(String expectedOutputFile,
46              ByteArrayOutputStream actualOutputStream, String... messages) throws Exception {
47          verifyXml(expectedOutputFile, actualOutputStream, null, messages);
48      }
49  
50      protected static void verifyXml(String expectedOutputFile,
51              ByteArrayOutputStream actualOutputStream,
52              BiPredicate<Node, Node> ordered, String... messages) throws Exception {
53          String expectedContents = readFile(expectedOutputFile);
54  
55          for (int i = 0; i < messages.length; i++) {
56              expectedContents = expectedContents.replace("$" + i, messages[i]);
57          }
58  
59          final Document expectedDocument = XmlUtil.getRawXml("audit output", expectedContents,
60                  expectedContents);
61          final Document actualDocument = getOutputStreamXml(actualOutputStream);
62  
63          Assert.assertEquals("xml encoding should be the same", expectedDocument.getXmlEncoding(),
64                  actualDocument.getXmlEncoding());
65          Assert.assertEquals("xml version should be the same", expectedDocument.getXmlVersion(),
66                  actualDocument.getXmlVersion());
67          verifyXmlNode(expectedDocument, actualDocument, "/", ordered);
68      }
69  
70      private static void verifyXmlNodes(Node expected, Node actual, String path,
71              BiPredicate<Node, Node> ordered) {
72          final Node expectedFirstChild = expected.getFirstChild();
73          final Node actualFirstChild = actual.getFirstChild();
74  
75          if (expectedFirstChild == null) {
76              Assert.assertNull("no children nodes should exist: " + path, actualFirstChild);
77              Assert.assertEquals("text should be the same: " + path, expected.getNodeValue(),
78                      actual.getNodeValue());
79          }
80          else {
81              Assert.assertNotNull("children nodes should exist: " + path, actualFirstChild);
82  
83              if (ordered == null) {
84                  Node actualChild = actualFirstChild;
85  
86                  for (Node expectedChild = expectedFirstChild; expectedChild != null;
87                          expectedChild = expectedChild.getNextSibling()) {
88                      verifyXmlNode(expectedChild, actualChild, path, ordered);
89  
90                      actualChild = actualChild.getNextSibling();
91                  }
92  
93                  Assert.assertNull("node have same number of children: " + path, actualChild);
94              }
95              else {
96                  final Set<Node> expectedChildren = XmlUtil.getChildrenElements(expected);
97                  final Set<Node> actualChildren = XmlUtil.getChildrenElements(actual);
98  
99                  Assert.assertEquals("node have same number of children: " + path,
100                         expectedChildren.size(), actualChildren.size());
101 
102                 for (Node expectedChild : expectedChildren) {
103                     Node foundChild = null;
104 
105                     for (Node actualChild : actualChildren) {
106                         if (ordered.test(expectedChild, actualChild)) {
107                             foundChild = actualChild;
108                             break;
109                         }
110                     }
111 
112                     Assert.assertNotNull("node should exist: " + path + expectedChild.getNodeName()
113                             + "/", foundChild);
114 
115                     verifyXmlNode(expectedChild, foundChild, path, ordered);
116                 }
117             }
118         }
119     }
120 
121     private static void verifyXmlNode(Node expected, Node actual, String path,
122             BiPredicate<Node, Node> ordered) {
123         if (expected == null) {
124             if (actual != null) {
125                 Assert.fail("no node should exist: " + path + actual.getNodeName() + "/");
126             }
127         }
128         else {
129             final String newPath = path + expected.getNodeName() + "/";
130 
131             Assert.assertNotNull("node should exist: " + newPath, actual);
132             Assert.assertEquals("node should have same name: " + newPath, expected.getNodeName(),
133                     actual.getNodeName());
134             Assert.assertEquals("node should have same type: " + newPath, expected.getNodeType(),
135                     actual.getNodeType());
136 
137             verifyXmlAttributes(expected.getAttributes(), actual.getAttributes(), newPath);
138 
139             verifyXmlNodes(expected, actual, newPath, ordered);
140         }
141     }
142 
143     private static void verifyXmlAttributes(NamedNodeMap expected, NamedNodeMap actual,
144             String path) {
145         if (expected == null) {
146             Assert.assertNull("no attributes should exist: " + path, actual);
147         }
148         else {
149             Assert.assertNotNull("attributes should exist: " + path, actual);
150 
151             for (int i = 0; i < expected.getLength(); i++) {
152                 verifyXmlAttribute(expected.item(i), actual.item(i), path);
153             }
154 
155             Assert.assertEquals("node have same number of attributes: " + path,
156                     expected.getLength(), actual.getLength());
157         }
158     }
159 
160     private static void verifyXmlAttribute(Node expected, Node actual, String path) {
161         final String expectedName = expected.getNodeName();
162 
163         Assert.assertNotNull("attribute value for '" + expectedName + "' should not be null: "
164                 + path, actual);
165 
166         Assert.assertEquals("attribute name should match: " + path, expectedName,
167                 actual.getNodeName());
168 
169         // ignore checkstyle version in xml as it changes each release
170         if (!"/#document/checkstyle".equals(path) && !"version".equals(expectedName)) {
171             Assert.assertEquals("attribute value for '" + expectedName + "' should match: " + path,
172                     expected.getNodeValue(), actual.getNodeValue());
173         }
174     }
175 
176 }