View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2024 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 static com.google.common.truth.Truth.assertThat;
23  import static com.google.common.truth.Truth.assertWithMessage;
24  
25  import java.io.ByteArrayOutputStream;
26  import java.nio.charset.StandardCharsets;
27  import java.util.Set;
28  import java.util.function.BiPredicate;
29  
30  import javax.xml.parsers.ParserConfigurationException;
31  
32  import org.w3c.dom.Document;
33  import org.w3c.dom.NamedNodeMap;
34  import org.w3c.dom.Node;
35  
36  import com.puppycrawl.tools.checkstyle.internal.utils.XmlUtil;
37  
38  public abstract class AbstractXmlTestSupport extends AbstractModuleTestSupport {
39  
40      /**
41       * Returns the output stream xml.
42       *
43       * @param outputStream the byte array output stream.
44       * @return returns a raw xml document of the interface {@link Document}.
45       * @throws ParserConfigurationException can cause exception when parsing output stream.
46       */
47      protected static Document getOutputStreamXml(ByteArrayOutputStream outputStream)
48              throws ParserConfigurationException {
49          final String xml = outputStream.toString(StandardCharsets.UTF_8);
50  
51          return XmlUtil.getRawXml("audit output", xml, xml);
52      }
53  
54      /**
55       * Verifies the generated xml.
56       *
57       * @param expectedOutputFile the expected output file.
58       * @param actualOutputStream the byte array output stream.
59       * @param messages an array of expected messages/content of xml document.
60       * @throws Exception can throw ComparisonFailure exception if xml encoding,
61       *     version don't match.
62       */
63      protected static void verifyXml(String expectedOutputFile,
64              ByteArrayOutputStream actualOutputStream, String... messages) throws Exception {
65          verifyXml(expectedOutputFile, actualOutputStream, null, messages);
66      }
67  
68      /**
69       * Verifies the actual generated xml document by comparing with expected document.
70       *
71       * @param expectedOutputFile the expected output file.
72       * @param actualOutputStream the actual byte array output stream.
73       * @param ordered an ordered predicate for xml nodes.
74       * @param messages an array of expected messages/content of xml document.
75       * @throws Exception can throw ComparisonFailure exception if xml encoding,
76       *     version do not match.
77       */
78      protected static void verifyXml(String expectedOutputFile,
79              ByteArrayOutputStream actualOutputStream,
80              BiPredicate<Node, Node> ordered, String... messages) throws Exception {
81          String expectedContents = readFile(expectedOutputFile);
82  
83          for (int i = 0; i < messages.length; i++) {
84              expectedContents = expectedContents.replace("$" + i, messages[i]);
85          }
86  
87          final Document expectedDocument = XmlUtil.getRawXml("audit output", expectedContents,
88                  expectedContents);
89          final Document actualDocument = getOutputStreamXml(actualOutputStream);
90  
91          assertWithMessage("xml encoding should be the same")
92                  .that(actualDocument.getXmlEncoding())
93                  .isEqualTo(expectedDocument.getXmlEncoding());
94  
95          assertWithMessage("xml version should be the same")
96                  .that(actualDocument.getXmlVersion())
97                  .isEqualTo(expectedDocument.getXmlVersion());
98  
99          verifyXmlNode(expectedDocument, actualDocument, "/", ordered);
100     }
101 
102     /**
103      * Verifies if xml nodes in actual xml document and expected xml document match or not.
104      *
105      * @param expected the expected xml node. A {@link Node} interface.
106      * @param actual the actual xml node. A {@link Node} interface.
107      * @param path the path to the current xml node that are compared.
108      * @param ordered an ordered predicate for xml nodes.
109      */
110     private static void verifyXmlNodes(Node expected, Node actual, String path,
111             BiPredicate<Node, Node> ordered) {
112         final Node expectedFirstChild = expected.getFirstChild();
113         final Node actualFirstChild = actual.getFirstChild();
114 
115         if (expectedFirstChild == null) {
116             assertWithMessage("no children nodes should exist: %s", path)
117                     .that(actualFirstChild)
118                     .isNull();
119             assertWithMessage("text should be the same: %s", path)
120                     .that(actual.getNodeValue())
121                     .isEqualTo(expected.getNodeValue());
122         }
123         else {
124             assertWithMessage("children nodes should exist: %s", path)
125                     .that(actualFirstChild)
126                     .isNotNull();
127 
128             if (ordered == null) {
129                 Node actualChild = actualFirstChild;
130 
131                 for (Node expectedChild = expectedFirstChild; expectedChild != null;
132                         expectedChild = expectedChild.getNextSibling()) {
133                     verifyXmlNode(expectedChild, actualChild, path, ordered);
134 
135                     actualChild = actualChild.getNextSibling();
136                 }
137 
138                 assertWithMessage("node have same number of children: %s", path)
139                         .that(actualChild)
140                         .isNull();
141             }
142             else {
143                 final Set<Node> expectedChildren = XmlUtil.getChildrenElements(expected);
144                 final Set<Node> actualChildren = XmlUtil.getChildrenElements(actual);
145 
146                 assertWithMessage("node have same number of children: %s", path)
147                         .that(actualChildren)
148                         .hasSize(expectedChildren.size());
149 
150                 for (Node expectedChild : expectedChildren) {
151                     Node foundChild = null;
152 
153                     for (Node actualChild : actualChildren) {
154                         if (ordered.test(expectedChild, actualChild)) {
155                             foundChild = actualChild;
156                             break;
157                         }
158                     }
159 
160                     assertWithMessage("node should exist: %s%s/", path, expectedChild.getNodeName())
161                             .that(foundChild)
162                             .isNotNull();
163 
164                     verifyXmlNode(expectedChild, foundChild, path, ordered);
165                 }
166             }
167         }
168     }
169 
170     /**
171      * Verifies if a xml node in actual xml document and expected xml document match or not based on
172      * their name, type, and attributes.
173      *
174      * @param expected the expected xml node. A {@link Node} interface.
175      * @param actual the actual xml node. A {@link Node} interface.
176      * @param path the path to the current xml nodes that are compared.
177      * @param ordered an ordered predicate for xml nodes.
178      */
179     private static void verifyXmlNode(Node expected, Node actual, String path,
180             BiPredicate<Node, Node> ordered) {
181         if (expected == null) {
182             if (actual != null) {
183                 assertWithMessage("no node should exist: %s%s/", path, actual.getNodeName())
184                         .fail();
185             }
186         }
187         else {
188             final String newPath = path + expected.getNodeName() + "/";
189 
190             assertWithMessage("node should exist: %s", newPath)
191                     .that(actual)
192                     .isNotNull();
193             assertWithMessage("node should have same name: %s", newPath)
194                     .that(actual.getNodeName())
195                     .isEqualTo(expected.getNodeName());
196             assertWithMessage("node should have same type: %s", newPath)
197                     .that(actual.getNodeType())
198                     .isEqualTo(expected.getNodeType());
199 
200             verifyXmlAttributes(expected.getAttributes(), actual.getAttributes(), newPath);
201 
202             verifyXmlNodes(expected, actual, newPath, ordered);
203         }
204     }
205 
206     /**
207      * Verifies xml attributes of collection of actual nodes by comparing with collection
208      * of expected node attributes.
209      *
210      * @param expected collection of expected nodes. A {@link NamedNodeMap} interface.
211      * @param actual collection of actual nodes. A {@link NamedNodeMap} interface.
212      * @param path the path to these xml nodes that are compared.
213      */
214     private static void verifyXmlAttributes(NamedNodeMap expected, NamedNodeMap actual,
215             String path) {
216         if (expected == null) {
217             assertWithMessage("no attributes should exist: %s", path)
218                     .that(actual)
219                     .isNull();
220         }
221         else {
222             assertWithMessage("attributes should exist: %s", path)
223                     .that(actual)
224                     .isNotNull();
225 
226             for (int i = 0; i < expected.getLength(); i++) {
227                 verifyXmlAttribute(expected.item(i), actual.item(i), path);
228             }
229 
230             assertThat(actual.getLength())
231                     .isEqualTo(expected.getLength());
232             assertWithMessage("node have same number of attributes: %s", path)
233                     .that(actual.getLength())
234                     .isEqualTo(expected.getLength());
235         }
236     }
237 
238     /**
239      * Verifies xml attributes of actual node (like name, and node value) by comparing with
240      * expected node attributes.
241      *
242      * @param expected the expected xml node. A {@link Node} interface.
243      * @param actual the actual xml node. A {@link Node} interface.
244      * @param path the path to the current xml nodes that are compared.
245      */
246     private static void verifyXmlAttribute(Node expected, Node actual, String path) {
247         final String expectedName = expected.getNodeName();
248 
249         assertWithMessage("attribute value for '%s' should not be null: %s", expectedName, path)
250                 .that(actual)
251                 .isNotNull();
252 
253         assertWithMessage("attribute name should match: %s", path)
254                 .that(actual.getNodeName())
255                 .isEqualTo(expectedName);
256 
257         // ignore checkstyle version in xml as it changes each release
258         if (!"/#document/checkstyle".equals(path) && !"version".equals(expectedName)) {
259             assertWithMessage("attribute value for '%s' should match: %s", expectedName, path)
260                     .that(actual.getNodeValue())
261                     .isEqualTo(expected.getNodeValue());
262         }
263     }
264 }