Skip to content

Commit 5141915

Browse files
committed
add byNameAndAllAttributes that uses an attrbute filter
inspired by #259
1 parent a7ca5be commit 5141915

File tree

4 files changed

+91
-4
lines changed

4 files changed

+91
-4
lines changed

xmlunit-core/src/main/java/org/xmlunit/diff/ElementSelectors.java

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.xmlunit.util.Predicate;
3030
import org.xmlunit.xpath.JAXPXPathEngine;
3131
import org.xmlunit.xpath.XPathEngine;
32+
import org.w3c.dom.Attr;
3233
import org.w3c.dom.Element;
3334
import org.w3c.dom.Node;
3435

@@ -84,23 +85,46 @@ && bothNullOrEqual(Nodes.getMergedNestedText(controlElement),
8485
/**
8586
* Elements with the same local name (and namespace URI - if any)
8687
* and attribute values for all attributes can be compared.
88+
*
89+
* <p>This {@code ElementSelector} doesn't know anything about a potentially configured attribute filter so may also
90+
* compare attributes that are excluded from comparison by the filter. Use {@link
91+
* #byNameAndAllAttributes(Predicate)} passing in your attribute filter if this causes problems.</p>
8792
*/
8893
public static final ElementSelector byNameAndAllAttributes =
89-
new ElementSelector() {
94+
byNameAndAllAttributes(new Predicate<Attr>() {
95+
@Override
96+
public boolean test(Attr a) {
97+
return true;
98+
}
99+
});
100+
101+
/**
102+
* Elements with the same local name (and namespace URI - if any)
103+
* and attribute values for all attributes can be compared.
104+
*
105+
* @param attributeFilter filter to use when comparing attributes. Only attributes where the filter returns {@code
106+
* true} are considered.
107+
*
108+
* @since XMLUnit 2.9.2
109+
*/
110+
public static final ElementSelector byNameAndAllAttributes(final Predicate<Attr> attributeFilter) {
111+
return new ElementSelector() {
90112
@Override
91113
public boolean canBeCompared(Element controlElement,
92114
Element testElement) {
93115
if (!byName.canBeCompared(controlElement, testElement)) {
94116
return false;
95117
}
96-
Map<QName, String> cAttrs = Nodes.getAttributes(controlElement);
97-
Map<QName, String> tAttrs = Nodes.getAttributes(testElement);
118+
Map<QName, String> cAttrs = Nodes.getAttributes(controlElement, attributeFilter);
119+
Map<QName, String> tAttrs = Nodes.getAttributes(testElement, attributeFilter);
98120
if (cAttrs.size() != tAttrs.size()) {
99121
return false;
100122
}
101123
return mapsEqualForKeys(cAttrs, tAttrs, cAttrs.keySet());
102124
}
103125
};
126+
}
127+
104128
/**
105129
* String Constants.
106130
*/

xmlunit-core/src/main/java/org/xmlunit/util/Nodes.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,32 @@ public static String getMergedNestedText(Node n) {
7676
* @return attributes
7777
*/
7878
public static Map<QName, String> getAttributes(Node n) {
79+
return getAttributes(n, new Predicate<Attr>() {
80+
@Override
81+
public boolean test(Attr a) {
82+
return true;
83+
}
84+
});
85+
}
86+
87+
/**
88+
* Obtains an element's attributes as Map.
89+
* @param n the node
90+
* @param attributeFilter is used to suppress unwanted attributes. Only attributes where the filter's test returns
91+
* {@code true} are returned
92+
* @return attributes
93+
* @since XMLUnit 2.9.2
94+
*/
95+
public static Map<QName, String> getAttributes(Node n, Predicate<Attr> attributeFilter) {
7996
Map<QName, String> map = new LinkedHashMap<QName, String>();
8097
NamedNodeMap m = n.getAttributes();
8198
if (m != null) {
8299
final int len = m.getLength();
83100
for (int i = 0; i < len; i++) {
84101
Attr a = (Attr) m.item(i);
85-
map.put(getQName(a), a.getValue());
102+
if (attributeFilter.test(a)) {
103+
map.put(getQName(a), a.getValue());
104+
}
86105
}
87106
}
88107
return map;

xmlunit-core/src/test/java/org/xmlunit/diff/ElementSelectorsTest.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.mockito.Mockito;
2626
import org.mockito.invocation.InvocationOnMock;
2727
import org.mockito.stubbing.Answer;
28+
import org.w3c.dom.Attr;
2829
import org.w3c.dom.Document;
2930
import org.w3c.dom.Element;
3031
import org.w3c.dom.Node;
@@ -128,6 +129,35 @@ static void byNameAndText_SingleLevel(ElementSelector s, Document doc) {
128129
.canBeCompared(control, differentNS));
129130
}
130131

132+
@Test public void byNameAndAllAttributesWithFilter() {
133+
Element control = doc.createElement(FOO);
134+
control.setAttribute(BAR, BAR);
135+
Element equal = doc.createElement(FOO);
136+
equal.setAttribute(BAR, BAR);
137+
equal.setAttribute("x", "y");
138+
Element noAttributes = doc.createElement(FOO);
139+
Element differentValue = doc.createElement(FOO);
140+
differentValue.setAttribute(BAR, FOO);
141+
Element differentName = doc.createElement(FOO);
142+
differentName.setAttribute(FOO, FOO);
143+
Element differentNS = doc.createElement(FOO);
144+
differentNS.setAttributeNS(SOME_URI, BAR, BAR);
145+
Predicate<Attr> filter = new Predicate<Attr>() {
146+
@Override
147+
public boolean test(Attr a) {
148+
return BAR.equals(a.getName());
149+
}
150+
};
151+
ElementSelector es = ElementSelectors.byNameAndAllAttributes(filter);
152+
153+
assertTrue(es.canBeCompared(control, equal));
154+
assertFalse(es.canBeCompared(control, noAttributes));
155+
assertFalse(es.canBeCompared(noAttributes, control));
156+
assertFalse(es.canBeCompared(control, differentValue));
157+
assertFalse(es.canBeCompared(control, differentName));
158+
assertFalse(es.canBeCompared(control, differentNS));
159+
}
160+
131161
@Test public void byNameAndAttributes_NamePart() {
132162
pureElementNameComparisons(ElementSelectors
133163
.byNameAndAttributes(new String[] {}));

xmlunit-core/src/test/java/org/xmlunit/util/NodesTest.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,20 @@ public class NodesTest {
143143
assertEquals(BAR, m.get(new QName(SOME_URI, FOO, BAR)));
144144
}
145145

146+
@Test public void attributeMapWithFilter() {
147+
Element e = doc.createElement(FOO);
148+
e.setAttribute(FOO, BAR);
149+
e.setAttribute("x", "y");
150+
Map<QName, String> m = Nodes.getAttributes(e, new Predicate<Attr>() {
151+
@Override
152+
public boolean test(Attr a) {
153+
return FOO.equals(a.getName());
154+
}
155+
});
156+
assertEquals(1, m.size());
157+
assertEquals(BAR, m.get(new QName(FOO)));
158+
}
159+
146160
private Document handleWsSetup() {
147161
return Convert.toDocument(Input.fromString(
148162
"<root>\n"

0 commit comments

Comments
 (0)