[MOBY-guts] biomoby commit

Eddie Kawas kawas at dev.open-bio.org
Thu Jan 29 19:12:01 UTC 2009


kawas
Thu Jan 29 14:12:01 EST 2009
Update of /home/repository/moby/moby-live/Java/src/main/org/biomoby/shared
In directory dev.open-bio.org:/tmp/cvs-serv12510/src/main/org/biomoby/shared

Modified Files:
	MobyUnitTest.java 
Log Message:
added methods to MobyUnitTest that allow you to perform compare the service output to regular expressions, xpath and to semantically compare the expected DOM with the one obtained by calling the service.
moby-live/Java/src/main/org/biomoby/shared MobyUnitTest.java,1.1,1.2
===================================================================
RCS file: /home/repository/moby/moby-live/Java/src/main/org/biomoby/shared/MobyUnitTest.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- /home/repository/moby/moby-live/Java/src/main/org/biomoby/shared/MobyUnitTest.java	2008/10/21 16:34:29	1.1
+++ /home/repository/moby/moby-live/Java/src/main/org/biomoby/shared/MobyUnitTest.java	2009/01/29 19:12:01	1.2
@@ -1,11 +1,37 @@
 package org.biomoby.shared;
 
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.custommonkey.xmlunit.DetailedDiff;
+import org.custommonkey.xmlunit.Diff;
+import org.custommonkey.xmlunit.Difference;
+import org.custommonkey.xmlunit.ElementNameAndTextQualifier;
+import org.custommonkey.xmlunit.ElementNameQualifier;
+import org.custommonkey.xmlunit.NamespaceContext;
+import org.custommonkey.xmlunit.SimpleNamespaceContext;
+import org.custommonkey.xmlunit.XMLUnit;
+import org.custommonkey.xmlunit.XpathEngine;
+import org.custommonkey.xmlunit.exceptions.XpathException;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+
 /**
- * A class representing a moby unit test
+ * A class representing a moby unit test. In addition, this class has methods to
+ * perform the various tests required for unit testing moby services.
  * 
  * @author Wendy Alexander
  * @author Eddie Kawas
  * 
+ * @version $Id$
  */
 public class MobyUnitTest {
 
@@ -29,6 +55,15 @@
 	setValidOutputXML("");
 	setValidREGEX("");
 	setValidXPath("");
+	init();
+    }
+
+    private void init() {
+	// for comparing dom trees
+	XMLUnit.setIgnoreAttributeOrder(true);
+	XMLUnit.setIgnoreComments(true);
+	XMLUnit.setIgnoreWhitespace(true);
+	XMLUnit.setIgnoreDiffBetweenTextAndCDATA(true);
     }
 
     /**
@@ -115,11 +150,335 @@
     public String toString() {
 	StringBuffer buf = new StringBuffer();
 	buf.append("Unit Test:\n");
-	buf.append("   Input       " + getExampleInput() + "\n");
-	buf.append("   Output      " + getValidOutputXML() + "\n");
-	buf.append("   XPath       " + getValidXPath() + "\n");
-	buf.append("   REGEX       " + getValidREGEX() + "\n");
-	return buf.toString();
+	buf.append("   Input       \n" + Utils.format (getExampleInput(), 2) + "\n");
+	buf.append("   Output      \n" + Utils.format(getValidOutputXML(),2) + "\n");
+	buf.append("   XPath       \n" + Utils.format(getValidXPath(),2) + "\n");
+	buf.append("   REGEX       \n" + Utils.format(getValidREGEX(),2) + "\n");
+	//return buf.toString();
+	return Utils.format (buf.toString(), 1);
+    }
+
+    /**
+     * XML is assumed to be wellformed and valid. Comparing 'XML' that is not
+     * wellformed may be identical but this method will return false!
+     * 
+     * @param testXML
+     *                the XML that you would like to compare to XML obtained
+     *                from <code>getValidOutputXML()</code>
+     * @return true if the documents are semantically similar, false otherwise
+     */
+    public boolean compareOutputXML(String testXML) throws MobyException {
+	if (!getValidOutputXML().trim().equals(""))
+	    try {
+		Diff diff;
+		diff = new Diff(getValidOutputXML(), testXML);
+		// diff.overrideElementQualifier(new
+		// RecursiveElementNameAndTextQualifier());
+		diff.overrideElementQualifier(new MobyQualifier());
+		return diff.similar();
+	    } catch (SAXException e) {
+		throw new MobyException(e.getMessage());
+	    } catch (IOException e) {
+		throw new MobyException(e.getMessage());
+	    }
+	return false;
+    }
+
+    /**
+     * 
+     * @param testXML
+     *                the XML that you would like to test the XPATH, from
+     *                <code>getValidXPath()</code>, expression against
+     * @return true if the XPath expression matches at least one node in the
+     *         testXML
+     */
+    public boolean compareXmlWithXpath(String testXML) throws MobyException {
+	if (!getValidXPath().trim().equals(""))
+	    try {
+		Document d = XMLUnit.buildControlDocument(testXML);
+		HashMap<String, String> m = new HashMap<String, String>();
+		if (d.getDocumentElement() != null) {
+		    if (d.getDocumentElement().getPrefix() != null)
+			m.put(d.getDocumentElement().getPrefix(), d
+				.getDocumentElement().getNamespaceURI());
+		}
+		NamespaceContext ctx = new SimpleNamespaceContext(m);
+		XpathEngine engine = XMLUnit.newXpathEngine();
+		engine.setNamespaceContext(ctx);
+		return engine.getMatchingNodes(getValidXPath(), d).getLength() > 0;
+	    } catch (IOException ioe) {
+		throw new MobyException("IOException:\n", ioe);
+	    } catch (SAXException se) {
+		throw new MobyException("Invalid XML:\n", se);
+	    } catch (XpathException xe) {
+		throw new MobyException("Invalid XPATH:\n" + xe);
+	    }
+	return false;
+    }
+
+    /**
+     * 
+     * @param testXML
+     *                the XML that you would like to test the REGEX, from
+     *                <code>getValidREGEX()</code>, expression against
+     * @return true if the regular expression matches the testXML, false
+     *         otherwise.
+     */
+    public boolean compareXmlWithREGEX(String testXML, boolean multiline) {
+	if (!getValidREGEX().trim().equals("")) {
+	    Pattern p = multiline ? Pattern.compile(getValidREGEX(),
+		    Pattern.MULTILINE) : Pattern.compile(getValidREGEX());
+	    Matcher m = p.matcher(testXML);
+	    return m.find();
+	}
+	return false;
+    }
+
+    /**
+     * 
+     * @param testXML
+     *                the XML that you would like to 'diff' to XML obtained from
+     *                <code>getValidOutputXML()</code>
+     * @return a String of text outlining all of the differences
+     */
+    @SuppressWarnings("unchecked")
+    public String getXMLDifferences(String testXML) {
+	if (!getValidOutputXML().trim().equals(""))
+	    try {
+		StringBuilder sb = new StringBuilder();
+		DetailedDiff myDiff = new DetailedDiff(new Diff(
+			getValidOutputXML(), testXML));
+		// myDiff.overrideElementQualifier(new
+		// RecursiveElementNameAndTextQualifier());
+		myDiff.overrideElementQualifier(new MobyQualifier());
+		List<Difference> differences = myDiff.getAllDifferences();
+		for (Difference d : differences)
+		    sb.append(d.toString() + "\n");
+		return sb.toString();
+	    } catch (SAXException e) {
+		return e.getMessage();
+	    } catch (IOException e) {
+		return e.getMessage();
+	    }
+	return "No XML to validate against!";
     }
 
+    private static final String[] ALL_ATTRIBUTES = { "*" };
+
+    /**
+     * This inner class implements a 'Qualifier' used to compare XML documents
+     * using the XMLUnit engine. It used both RecursiveElementNameAndTextQualifier and
+     * MultiLevelElementNameAndTextQualifier as a template.
+     * 
+     * @author Eddie Kawas
+     * 
+     */
+    protected class MobyQualifier extends ElementNameQualifier {
+	/*
+	******************************************************************
+	Copyright (c) 2008, Jeff Martin, Tim Bacon
+	All rights reserved.
+
+	Redistribution and use in source and binary forms, with or without
+	modification, are permitted provided that the following conditions
+	are met:
+
+	        * Redistributions of source code must retain the above copyright
+	          notice, this list of conditions and the following disclaimer.
+	        * Redistributions in binary form must reproduce the above
+	          copyright notice, this list of conditions and the following
+	          disclaimer in the documentation and/or other materials provided
+	          with the distribution.
+	        * Neither the name of the xmlunit.sourceforge.net nor the names
+	          of its contributors may be used to endorse or promote products
+	          derived from this software without specific prior written
+	          permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+	LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+	FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+	COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+	INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+	BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+	CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+	LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+	ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+	POSSIBILITY OF SUCH DAMAGE.
+
+	******************************************************************
+	*/
+	private final String[] qualifyingAttrNames;
+
+	private final ElementNameQualifier NAME_QUALIFIER = new ElementNameQualifier();
+
+	private final ElementNameAndTextQualifier NAME_AND_TEXT_QUALIFIER = new ElementNameAndTextQualifier();
+
+	/**
+	 * No-args constructor: use all attributes from all elements to
+	 * determine whether elements qualify for comparability
+	 */
+	public MobyQualifier() {
+	    this(ALL_ATTRIBUTES);
+	}
+
+	/**
+	 * Simple constructor for a single qualifying attribute name
+	 * 
+	 * @param attrName
+	 *                the value to use to qualify whether two elements can
+	 *                be compared further for differences
+	 */
+	public MobyQualifier(String attrName) {
+	    this(new String[] { attrName });
+	}
+
+	/**
+	 * Extended constructor for multiple qualifying attribute names
+	 * 
+	 * @param attrNames
+	 *                the array of values to use to qualify whether two
+	 *                elements can be compared further for differences
+	 */
+	public MobyQualifier(String[] attrNames) {
+	    this.qualifyingAttrNames = new String[attrNames.length];
+	    System.arraycopy(attrNames, 0, qualifyingAttrNames, 0,
+		    attrNames.length);
+	}
+
+	/**
+	 * Determine whether two elements qualify for further Difference
+	 * comparison.
+	 * 
+	 * @param differenceEngine
+	 *                the DifferenceEngine instance wanting to determine if
+	 *                the elements are comparable
+	 * @param control
+	 * @param test
+	 * @return true if the two elements qualify for further comparison based
+	 *         on both the superclass qualification (namespace URI and non-
+	 *         namespaced tag name), and the presence of qualifying
+	 *         attributes with the same values; false otherwise
+	 */
+	public boolean qualifyForComparison_old(Element control, Element test) {
+	    if (super.qualifyForComparison(control, test)) {
+		return areAttributesComparable(control, test);
+	    }
+	    return false;
+	}
+
+	public boolean qualifyForComparison(Element control, Element test) {
+	    boolean stillSimilar = true;
+	    Element currentControl = control;
+	    Element currentTest = test;
+
+	    // we dont try to compare the Collection node because they have unordered children
+	    if (!control.getLocalName().equals("Collection")) {
+		// not a collection, match name and attributes
+		stillSimilar = NAME_QUALIFIER.qualifyForComparison(
+			currentControl, currentTest);
+
+		if (stillSimilar) {
+		    if (currentControl.hasChildNodes()
+			    && currentTest.hasChildNodes()) {
+			Node n1 = getFirstEligibleChild(currentControl);
+			Node n2 = getFirstEligibleChild(currentTest);
+			if (n1.getNodeType() == Node.ELEMENT_NODE
+				&& n2.getNodeType() == Node.ELEMENT_NODE) {
+			    currentControl = (Element) n1;
+			    currentTest = (Element) n2;
+			} else {
+			    stillSimilar = false;
+			}
+		    } else {
+			stillSimilar = false;
+		    }
+		}
+
+		// finally compare the level containing the text child node
+		if (stillSimilar) {
+		    stillSimilar = NAME_AND_TEXT_QUALIFIER
+			    .qualifyForComparison(currentControl, currentTest);
+		}
+	    }
+	    return stillSimilar
+
+	    && areAttributesComparable(currentControl, currentTest);
+
+	}
+
+	private Node getFirstEligibleChild(Node parent) {
+	    Node n1 = parent.getFirstChild();
+	    while (n1.getNodeType() == Node.TEXT_NODE
+		    && n1.getNodeValue().trim().length() == 0) {
+		Node n2 = n1.getNextSibling();
+		if (n2 == null)
+		    break;
+		n1 = n2;
+	    }
+	    return n1;
+	}
+
+	/**
+	 * Determine whether the qualifying attributes are present in both
+	 * elements and if so whether their values are the same
+	 * 
+	 * @param control
+	 * @param test
+	 * @return true if all qualifying attributes are present with the same
+	 *         values, false otherwise
+	 */
+	protected boolean areAttributesComparable(Element control, Element test) {
+	    String controlValue, testValue;
+	    Attr[] qualifyingAttributes;
+	    NamedNodeMap namedNodeMap = control.getAttributes();
+	    if (matchesAllAttributes(qualifyingAttrNames)) {
+		qualifyingAttributes = new Attr[namedNodeMap.getLength()];
+		for (int n = 0; n < qualifyingAttributes.length; ++n) {
+		    qualifyingAttributes[n] = (Attr) namedNodeMap.item(n);
+		}
+	    } else {
+		qualifyingAttributes = new Attr[qualifyingAttrNames.length];
+		for (int n = 0; n < qualifyingAttrNames.length; ++n) {
+		    qualifyingAttributes[n] = (Attr) namedNodeMap
+			    .getNamedItem(qualifyingAttrNames[n]);
+		}
+	    }
+
+	    String nsURI, name;
+	    for (int i = 0; i < qualifyingAttributes.length; ++i) {
+		if (qualifyingAttributes[i] != null) {
+		    nsURI = qualifyingAttributes[i].getNamespaceURI();
+		    controlValue = qualifyingAttributes[i].getNodeValue();
+		    name = qualifyingAttributes[i].getName();
+		} else {
+		    // cannot be "*" case
+		    nsURI = controlValue = "";
+		    name = qualifyingAttrNames[i];
+		}
+		if (nsURI == null || nsURI.length() == 0) {
+		    testValue = test.getAttribute(name);
+		} else {
+		    testValue = test.getAttributeNS(nsURI,
+			    qualifyingAttributes[i].getLocalName());
+		}
+		if (controlValue == null) {
+		    if (testValue != null) {
+			return false;
+		    }
+		} else {
+		    if (!controlValue.equals(testValue)) {
+			return false;
+		    }
+		}
+	    }
+	    return true;
+	}
+
+	private boolean matchesAllAttributes(String[] attributes) {
+	    return Arrays.equals(attributes, ALL_ATTRIBUTES);
+	}
+
+    }
 }




More information about the MOBY-guts mailing list