All Downloads are FREE. Search and download functionalities are using the official Maven repository.

se.jguru.nazgul.test.xmlbinding.XmlTestUtils Maven / Gradle / Ivy

/*
 * #%L
 * Nazgul Project: nazgul-core-xmlbinding-test
 * %%
 * Copyright (C) 2010 - 2015 jGuru Europe AB
 * %%
 * Licensed under the jGuru Europe AB license (the "License"), based
 * on Apache License, Version 2.0; you may not use this file except
 * in compliance with the License.
 * 
 * You may obtain a copy of the License at
 * 
 *       http://www.jguru.se/licenses/jguruCorporateSourceLicense-2.0.txt
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

package se.jguru.nazgul.test.xmlbinding;

import org.apache.commons.lang3.Validate;
import org.custommonkey.xmlunit.DetailedDiff;
import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.Difference;
import org.custommonkey.xmlunit.NodeDetail;
import org.custommonkey.xmlunit.XMLUnit;
import org.junit.Assert;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.regex.Pattern;

/**
 * Utility class to simplify working with XML data in tests.
 *
 * @author Lennart Jörelid, jGuru Europe AB
 */
@SuppressWarnings("PMD.PreserveStackTrace")
public abstract class XmlTestUtils {

    // Our Log
    private static final Logger log = LoggerFactory.getLogger(XmlTestUtils.class);
    private static final int CONTROL_NODE = 0;
    private static final int TEST_NODE = 1;

    /**
     * Standard pattern for ignoring differences within the synthesized
     * classes section of an EntityTransporter' XML form.
     */
    public static final Pattern ENTITY_TRANSPORTER_METADATA_DIFF
            = Pattern.compile("/entityTransporter\\[\\d+\\]/entityClasses\\[\\d+\\](/entityClass\\[\\d+\\](/.*)?)?");

    /**
     * Compares XML documents provided by the two Readers.
     *
     * @param expected The expected document data.
     * @param actual   The actual document data.
     * @return A DetailedDiff object, describing all differences in documents supplied.
     * @throws SAXException If a SAXException was raised during parsing of the two Documents.
     * @throws IOException  If an I/O-related exception was raised while acquiring the data from the Readers.
     */
    public static Diff compareXmlIgnoringWhitespace(final String expected, final String actual) throws SAXException,
            IOException {

        // Check sanity
        Validate.notNull(expected, "Cannot handle null expected argument.");
        Validate.notNull(actual, "Cannot handle null actual argument.");

        // Ignore whitespace - and also normalize the Documents.
        XMLUnit.setNormalize(true);
        XMLUnit.setIgnoreWhitespace(true);

        // Compare and return
        return XMLUnit.compareXML(expected, actual);
    }


    /**
     * 

Validates that the expected and actual XML-formatted strings are identical, * ignoring any differences considered "trivial". Differences are considered trivial if their XPath locations * (in the document) match the supplied Pattern. A pattern such as * {@code Pattern.compile("/a\\[\\d+\\]/b\\[\\d+\\]")} would identify all differences occurring * within the XPath {@code "/a/b"} as trivial.

*

This is a convenience method; a jUnit assertion error is thrown if the two XML documents * had non-trivial differences implying that this method can be used directly within a unit test method. * No additional/surrounding {@code Assert.isTrue(...) } is required.

* * @param expected The expected XML. * @param actual The actual, received XML. * @param trivialPatternXpathIdentifier a non-null pattern defining XPath locations within the document(s) * where exceptions are considered trivial. * @see #ENTITY_TRANSPORTER_METADATA_DIFF */ public static void validateIdenticalContent(final String expected, final String actual, final Pattern trivialPatternXpathIdentifier) { // Check sanity Validate.notNull(expected, "Cannot handle null expected argument."); Validate.notNull(actual, "Cannot handle null actual argument."); Validate.notNull(trivialPatternXpathIdentifier, "Cannot handle null trivialPatternXpathIdentifier argument."); final Diff diff; try { diff = XmlTestUtils.compareXmlIgnoringWhitespace(expected, actual); } catch (Exception e) { throw new IllegalArgumentException("Could not compare XMLs.", e); } if (!diff.identical()) { // Validate that the differences are non-trivial. final SortedMap> diffMap = getXPathLocationToDifferenceMap(diff); for (String current : diffMap.keySet()) { if (!trivialPatternXpathIdentifier.matcher(current).matches()) { Assert.fail("Diff [" + current + "] was non-trivial. (" + diffMap.get(current) + ")"); } } } } /** * Compares the two supplied XML-formatted streams * * @param expected The expected document data. * @param actual The actual document data. * @return An XPathLocation to Difference Map for the given Diff. */ public static SortedMap> getXPathLocationToDifferenceMap(final String expected, final String actual) { try { return getXPathLocationToDifferenceMap(compareXmlIgnoringWhitespace(expected, actual)); } catch (Exception e) { throw new IllegalArgumentException("Could not acquire XPath2Difference map", e); } } /** * Maps the Difference instances within the supplied Diff to their respective XPath locations. * Should the XPath location (within a single Difference instance) be different between the * expected and actual {@code NodeDetail}s, the Difference is mapped to both XPaths. * * @param diff The Diff from which all Differences should be extracted and mapped to their respective * XPath locations. Should the XPath location (within a single Difference instance) be different between the * expected and actual {@code NodeDetail}s, the Difference is mapped to both XPaths. * @return An XPathLocation to Difference Map for the given Diff. */ @SuppressWarnings("unchecked") public static SortedMap> getXPathLocationToDifferenceMap(final Diff diff) { // Check sanity Validate.notNull(diff, "Cannot handle null diff argument."); final SortedMap> toReturn = new TreeMap>(); final List allDifferences = (List) new DetailedDiff(diff).getAllDifferences(); for (Difference current : allDifferences) { // Map the difference to its XPathLocation. final NodeDetail[] nodeDetails = getNodeDetails(current); final String expectedPartXPath = nodeDetails[CONTROL_NODE] == null ? null : nodeDetails[CONTROL_NODE].getXpathLocation(); final String actualPartXPath = nodeDetails[TEST_NODE] == null ? null : nodeDetails[TEST_NODE].getXpathLocation(); if (expectedPartXPath != null && expectedPartXPath.equals(actualPartXPath)) { addDifference(toReturn, expectedPartXPath, current); } else { // Is this really sane? log.warn("Different XPath locations for Difference [" + current + "]. Mapping to two locations."); addDifference(toReturn, expectedPartXPath, current); addDifference(toReturn, actualPartXPath, current); } } // All done. return toReturn; } /** * Utility method to read all (string formatted) data from the given classpath-relative * file and return the data as a string. * * @param path The classpath-relative file path. * @return The content of the supplied file. */ public static String readFully(final String path) { final StringBuilder toReturn = new StringBuilder(50); try { // Will produce a NPE if the path was not directed to a file. final InputStream resource = XmlTestUtils.class.getClassLoader().getResourceAsStream(path); final BufferedReader tmp = new BufferedReader(new InputStreamReader(resource)); for (String line = tmp.readLine(); line != null; line = tmp.readLine()) { toReturn.append(line).append('\n'); } } catch (final Exception e) { throw new IllegalArgumentException("Resource [" + path + "] not readable."); } // All done. return toReturn.toString(); } // // Private helpers // private static void addDifference(final SortedMap> resultMap, final String xPath, final Difference toAdd) { List diffList = resultMap.get(xPath); if (diffList == null) { diffList = new ArrayList(); resultMap.put(xPath, diffList); } // Add the difference diffList.add(toAdd); } private static NodeDetail[] getNodeDetails(final Difference difference) { NodeDetail[] toReturn = new NodeDetail[2]; toReturn[CONTROL_NODE] = difference.getControlNodeDetail(); toReturn[TEST_NODE] = difference.getTestNodeDetail(); return toReturn; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy