From: SourceForge.net <no...@so...> - 2008-01-23 22:50:42
|
Feature Requests item #1878549, was opened at 2008-01-23 17:50 Message generated for change (Tracker Item Submitted) made by Item Submitter You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=377771&aid=1878549&group_id=23187 Please note that this message will contain a full copy of the comment thread, including the initial issue submission, for this request, not just the latest update. Category: None Group: None Status: Open Resolution: None Priority: 5 Private: No Submitted By: Frank Callahan (fcallahan) Assigned to: Nobody/Anonymous (nobody) Summary: recursive ElementQualifier implementation Initial Comment: An implementation of the ElementQualifier interface is needed that can match comparable control and test nodes at arbitrary levels of nesting. While working on an automated regression tool, I found that MultiLevelElementNameAndTextQualifier could not correctly match comparable nodes in deeply nested and complex SOAP response messages. The data contents of these nodes were identical, but they occurred in different orders. Also, MultiLevelElementNameAndTextQualifier requires that the number of levels to explore in each candidate comparable pair be specified in advance of running a comparison. QA testers using the tool would have to enter a number as their best quess of how deep to look, a requirement that would make the tool less useful to them. To solve this problem, I wrote a recursive implementation of ElementQualifier that is able to find all the comparable nodes in a control and test SOAP response message. Probably there are other XMLUnit users who have had to write something similar, so it might be a good idea to include a recursive ElementQualifier in the XMLUnit examples package. The Open Source Review Board at my company, Fidelity Investments, has approved making this originally proprietary code available as a potential contribution to XMLUnit, so please consider adding it to the examples package, making any changes that would make it more consistent with your code standards. It does work fine "as is" though. Anyhow, the code for the RecursiveElementNameAndTextQualifier is pasted in below, so anyone who wants to is welcome to try it. Also, it's in an attached file. /** Based on XMLUnit example MultiLevelElementNameAndTextQualifier. Frank Callahan */ import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.custommonkey.xmlunit.ElementNameQualifier; import org.custommonkey.xmlunit.ElementQualifier; /** * Compares all Element and Text nodes in two pieces of XML. Allows elements of * complex, deeply nested types that are returned in different orders but have * the same content to be recognized as comparable. */ public class RecursiveElementNameAndTextQualifier implements ElementQualifier { private static final ElementNameQualifier NAME_QUALIFIER = new ElementNameQualifier(); /** * Uses element names and the text nested an arbitrary level of child * elements deeper into the element to compare elements. Checks all nodes, * not just first child element. * * <p> * Does not ignore empty text nodes. */ public RecursiveElementNameAndTextQualifier() { } /** * Returns result of recursive comparison of all the nodes of a control and * test element. * */ public boolean qualifyForComparison(Element currentControl, Element currentTest) { return compareNodes(currentControl, currentTest); } private boolean compareNodes(Node currentControl, Node currentTest) { try { // if they are elements, compare names of the two nodes if (!NAME_QUALIFIER.qualifyForComparison((Element) currentControl, (Element) currentTest)) { return false; } // Compare the control and test elements' children NodeList controlNodes = null; NodeList testNodes = null; // Check that both nodes have children and, if so, get lists of them if (currentControl.hasChildNodes() && currentTest.hasChildNodes()) { controlNodes = currentControl.getChildNodes(); testNodes = currentTest.getChildNodes(); } else if (currentControl.hasChildNodes() || currentTest.hasChildNodes()) { return false; // if both nodes are empty, they are comparable } else { return true; } // check that both node lists have the same length if (controlNodes.getLength() != testNodes.getLength()) { return false; } // Do checks of test and control nodes' children int nNodes = controlNodes.getLength(); for (int i = 0; i < nNodes; i++) { Node testNode = testNodes.item(i); Node controlNode = controlNodes.item(i); // check if both node are same type if (controlNode.getNodeType() != testNode.getNodeType()) { return false; } // compare text nodes if (controlNode.getNodeType() == Node.TEXT_NODE) { // compare concatenated, trimmed text nodes if (!catText(controlNode).equals(catText(testNode))) { return false; } // recursive check of current child control and test nodes' // children } else if (!compareNodes((Element) controlNode, (Element) testNode)) { return false; } } // All descendants of current control and test nodes are comparable return true; } catch (Exception e) { return false; } } /** * Concatenates contiguous Text nodes and removes all leading and trailing whitespace. * @param textNode * @return */ private String catText(Node textNode) { assert (textNode.getNodeType() == Node.TEXT_NODE); StringBuffer text = new StringBuffer(); Node next = textNode; do { if (next.getNodeValue() != null) { text.append(next.getNodeValue().trim()); next = next.getNextSibling(); } } while (next != null && next.getNodeType() == Node.TEXT_NODE); return text.toString(); } } ---------------------------------------------------------------------- You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=377771&aid=1878549&group_id=23187 |
From: SourceForge.net <no...@so...> - 2008-02-28 14:32:33
|
Feature Requests item #1878549, was opened at 2008-01-23 23:50 Message generated for change (Settings changed) made by bodewig You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=377771&aid=1878549&group_id=23187 Please note that this message will contain a full copy of the comment thread, including the initial issue submission, for this request, not just the latest update. >Category: XMLUnit for Java Group: None Status: Open Resolution: None Priority: 5 Private: No Submitted By: Frank Callahan (fcallahan) Assigned to: Nobody/Anonymous (nobody) Summary: recursive ElementQualifier implementation Initial Comment: An implementation of the ElementQualifier interface is needed that can match comparable control and test nodes at arbitrary levels of nesting. While working on an automated regression tool, I found that MultiLevelElementNameAndTextQualifier could not correctly match comparable nodes in deeply nested and complex SOAP response messages. The data contents of these nodes were identical, but they occurred in different orders. Also, MultiLevelElementNameAndTextQualifier requires that the number of levels to explore in each candidate comparable pair be specified in advance of running a comparison. QA testers using the tool would have to enter a number as their best quess of how deep to look, a requirement that would make the tool less useful to them. To solve this problem, I wrote a recursive implementation of ElementQualifier that is able to find all the comparable nodes in a control and test SOAP response message. Probably there are other XMLUnit users who have had to write something similar, so it might be a good idea to include a recursive ElementQualifier in the XMLUnit examples package. The Open Source Review Board at my company, Fidelity Investments, has approved making this originally proprietary code available as a potential contribution to XMLUnit, so please consider adding it to the examples package, making any changes that would make it more consistent with your code standards. It does work fine "as is" though. Anyhow, the code for the RecursiveElementNameAndTextQualifier is pasted in below, so anyone who wants to is welcome to try it. Also, it's in an attached file. /** Based on XMLUnit example MultiLevelElementNameAndTextQualifier. Frank Callahan */ import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.custommonkey.xmlunit.ElementNameQualifier; import org.custommonkey.xmlunit.ElementQualifier; /** * Compares all Element and Text nodes in two pieces of XML. Allows elements of * complex, deeply nested types that are returned in different orders but have * the same content to be recognized as comparable. */ public class RecursiveElementNameAndTextQualifier implements ElementQualifier { private static final ElementNameQualifier NAME_QUALIFIER = new ElementNameQualifier(); /** * Uses element names and the text nested an arbitrary level of child * elements deeper into the element to compare elements. Checks all nodes, * not just first child element. * * <p> * Does not ignore empty text nodes. */ public RecursiveElementNameAndTextQualifier() { } /** * Returns result of recursive comparison of all the nodes of a control and * test element. * */ public boolean qualifyForComparison(Element currentControl, Element currentTest) { return compareNodes(currentControl, currentTest); } private boolean compareNodes(Node currentControl, Node currentTest) { try { // if they are elements, compare names of the two nodes if (!NAME_QUALIFIER.qualifyForComparison((Element) currentControl, (Element) currentTest)) { return false; } // Compare the control and test elements' children NodeList controlNodes = null; NodeList testNodes = null; // Check that both nodes have children and, if so, get lists of them if (currentControl.hasChildNodes() && currentTest.hasChildNodes()) { controlNodes = currentControl.getChildNodes(); testNodes = currentTest.getChildNodes(); } else if (currentControl.hasChildNodes() || currentTest.hasChildNodes()) { return false; // if both nodes are empty, they are comparable } else { return true; } // check that both node lists have the same length if (controlNodes.getLength() != testNodes.getLength()) { return false; } // Do checks of test and control nodes' children int nNodes = controlNodes.getLength(); for (int i = 0; i < nNodes; i++) { Node testNode = testNodes.item(i); Node controlNode = controlNodes.item(i); // check if both node are same type if (controlNode.getNodeType() != testNode.getNodeType()) { return false; } // compare text nodes if (controlNode.getNodeType() == Node.TEXT_NODE) { // compare concatenated, trimmed text nodes if (!catText(controlNode).equals(catText(testNode))) { return false; } // recursive check of current child control and test nodes' // children } else if (!compareNodes((Element) controlNode, (Element) testNode)) { return false; } } // All descendants of current control and test nodes are comparable return true; } catch (Exception e) { return false; } } /** * Concatenates contiguous Text nodes and removes all leading and trailing whitespace. * @param textNode * @return */ private String catText(Node textNode) { assert (textNode.getNodeType() == Node.TEXT_NODE); StringBuffer text = new StringBuffer(); Node next = textNode; do { if (next.getNodeValue() != null) { text.append(next.getNodeValue().trim()); next = next.getNextSibling(); } } while (next != null && next.getNodeType() == Node.TEXT_NODE); return text.toString(); } } ---------------------------------------------------------------------- You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=377771&aid=1878549&group_id=23187 |
From: SourceForge.net <no...@so...> - 2008-03-14 15:50:29
|
Feature Requests item #1878549, was opened at 2008-01-23 23:50 Message generated for change (Comment added) made by bodewig You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=377771&aid=1878549&group_id=23187 Please note that this message will contain a full copy of the comment thread, including the initial issue submission, for this request, not just the latest update. Category: XMLUnit for Java Group: None >Status: Closed >Resolution: Fixed Priority: 5 Private: No Submitted By: Frank Callahan (fcallahan) Assigned to: Nobody/Anonymous (nobody) Summary: recursive ElementQualifier implementation Initial Comment: An implementation of the ElementQualifier interface is needed that can match comparable control and test nodes at arbitrary levels of nesting. While working on an automated regression tool, I found that MultiLevelElementNameAndTextQualifier could not correctly match comparable nodes in deeply nested and complex SOAP response messages. The data contents of these nodes were identical, but they occurred in different orders. Also, MultiLevelElementNameAndTextQualifier requires that the number of levels to explore in each candidate comparable pair be specified in advance of running a comparison. QA testers using the tool would have to enter a number as their best quess of how deep to look, a requirement that would make the tool less useful to them. To solve this problem, I wrote a recursive implementation of ElementQualifier that is able to find all the comparable nodes in a control and test SOAP response message. Probably there are other XMLUnit users who have had to write something similar, so it might be a good idea to include a recursive ElementQualifier in the XMLUnit examples package. The Open Source Review Board at my company, Fidelity Investments, has approved making this originally proprietary code available as a potential contribution to XMLUnit, so please consider adding it to the examples package, making any changes that would make it more consistent with your code standards. It does work fine "as is" though. Anyhow, the code for the RecursiveElementNameAndTextQualifier is pasted in below, so anyone who wants to is welcome to try it. Also, it's in an attached file. /** Based on XMLUnit example MultiLevelElementNameAndTextQualifier. Frank Callahan */ import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.custommonkey.xmlunit.ElementNameQualifier; import org.custommonkey.xmlunit.ElementQualifier; /** * Compares all Element and Text nodes in two pieces of XML. Allows elements of * complex, deeply nested types that are returned in different orders but have * the same content to be recognized as comparable. */ public class RecursiveElementNameAndTextQualifier implements ElementQualifier { private static final ElementNameQualifier NAME_QUALIFIER = new ElementNameQualifier(); /** * Uses element names and the text nested an arbitrary level of child * elements deeper into the element to compare elements. Checks all nodes, * not just first child element. * * <p> * Does not ignore empty text nodes. */ public RecursiveElementNameAndTextQualifier() { } /** * Returns result of recursive comparison of all the nodes of a control and * test element. * */ public boolean qualifyForComparison(Element currentControl, Element currentTest) { return compareNodes(currentControl, currentTest); } private boolean compareNodes(Node currentControl, Node currentTest) { try { // if they are elements, compare names of the two nodes if (!NAME_QUALIFIER.qualifyForComparison((Element) currentControl, (Element) currentTest)) { return false; } // Compare the control and test elements' children NodeList controlNodes = null; NodeList testNodes = null; // Check that both nodes have children and, if so, get lists of them if (currentControl.hasChildNodes() && currentTest.hasChildNodes()) { controlNodes = currentControl.getChildNodes(); testNodes = currentTest.getChildNodes(); } else if (currentControl.hasChildNodes() || currentTest.hasChildNodes()) { return false; // if both nodes are empty, they are comparable } else { return true; } // check that both node lists have the same length if (controlNodes.getLength() != testNodes.getLength()) { return false; } // Do checks of test and control nodes' children int nNodes = controlNodes.getLength(); for (int i = 0; i < nNodes; i++) { Node testNode = testNodes.item(i); Node controlNode = controlNodes.item(i); // check if both node are same type if (controlNode.getNodeType() != testNode.getNodeType()) { return false; } // compare text nodes if (controlNode.getNodeType() == Node.TEXT_NODE) { // compare concatenated, trimmed text nodes if (!catText(controlNode).equals(catText(testNode))) { return false; } // recursive check of current child control and test nodes' // children } else if (!compareNodes((Element) controlNode, (Element) testNode)) { return false; } } // All descendants of current control and test nodes are comparable return true; } catch (Exception e) { return false; } } /** * Concatenates contiguous Text nodes and removes all leading and trailing whitespace. * @param textNode * @return */ private String catText(Node textNode) { assert (textNode.getNodeType() == Node.TEXT_NODE); StringBuffer text = new StringBuffer(); Node next = textNode; do { if (next.getNodeValue() != null) { text.append(next.getNodeValue().trim()); next = next.getNextSibling(); } } while (next != null && next.getNodeType() == Node.TEXT_NODE); return text.toString(); } } ---------------------------------------------------------------------- >Comment By: Stefan Bodewig (bodewig) Date: 2008-03-14 16:50 Message: Logged In: YES user_id=113148 Originator: NO I committed a slightly modified version that properly deals with adjacent text nodes (testMultipleTextValues failed with your implementation). http://xmlunit.svn.sourceforge.net/viewvc/xmlunit?view=rev&revision=250 Thanks ---------------------------------------------------------------------- You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=377771&aid=1878549&group_id=23187 |