Skip to content

Commit de08f4d

Browse files
committed
give each ElementSelector a chance to see all nodes first
1 parent 7549fb2 commit de08f4d

File tree

4 files changed

+149
-26
lines changed

4 files changed

+149
-26
lines changed

RELEASE_NOTES.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99
implementations of your own.
1010
[#33](https://github.com/xmlunit/xmlunit.net/pull/33).
1111

12+
* `DefaultNodeMatcher` with multiple `ElementSelector`s could fail to
13+
find the best matches as the order of `ElementSelector`s should
14+
select them.
15+
Issue similar to [xmlunit/#197](https://github.com/xmlunit/xmlunit/issues/197)
16+
1217
## XMLUnit.NET 2.8.0 - /Released 2020-05-12/
1318

1419
This version contains a backwards incompatible change to the

src/main/net-core/Diff/DefaultNodeMatcher.cs

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -99,16 +99,25 @@ public IEnumerable<KeyValuePair<XmlNode, XmlNode>>
9999
unmatchedTestIndexes.Add(i);
100100
}
101101
int controlSize = controlList.Count;
102-
MatchInfo lastMatch = new MatchInfo(null, -1);
102+
ICollection<int> unmatchedControlIndexes = new HashSet<int>();
103103
for (int i = 0; i < controlSize; i++) {
104-
XmlNode control = controlList[i];
105-
MatchInfo testMatch = FindMatchingNode(control, testList,
106-
lastMatch.Index,
107-
unmatchedTestIndexes);
108-
if (testMatch != null) {
109-
unmatchedTestIndexes.Remove(testMatch.Index);
110-
matches.AddLast(new KeyValuePair<XmlNode,
111-
XmlNode>(control, testMatch.Node));
104+
unmatchedControlIndexes.Add(i);
105+
}
106+
foreach (ElementSelector e in elementSelectors) {
107+
MatchInfo lastMatch = new MatchInfo(null, -1);
108+
for (int i = 0; i < controlSize; i++) {
109+
if (!unmatchedControlIndexes.Contains(i)) {
110+
continue;
111+
}
112+
XmlNode control = controlList[i];
113+
MatchInfo testMatch = FindMatchingNode(control, testList,
114+
lastMatch.Index, unmatchedTestIndexes, e);
115+
if (testMatch != null) {
116+
unmatchedControlIndexes.Remove(i);
117+
unmatchedTestIndexes.Remove(testMatch.Index);
118+
matches.AddLast(new KeyValuePair<XmlNode,
119+
XmlNode>(control, testMatch.Node));
120+
}
112121
}
113122
}
114123
return matches;
@@ -117,26 +126,14 @@ public IEnumerable<KeyValuePair<XmlNode, XmlNode>>
117126
private MatchInfo FindMatchingNode(XmlNode searchFor,
118127
IList<XmlNode> searchIn,
119128
int indexOfLastMatch,
120-
ICollection<int> availableIndexes) {
129+
ICollection<int> availableIndexes,
130+
ElementSelector e) {
121131
MatchInfo m = SearchIn(searchFor, searchIn,
122132
availableIndexes,
123-
indexOfLastMatch + 1, searchIn.Count);
133+
indexOfLastMatch + 1, searchIn.Count, e);
124134
return m ?? SearchIn(searchFor, searchIn,
125135
availableIndexes,
126-
0, indexOfLastMatch);
127-
}
128-
129-
private MatchInfo SearchIn(XmlNode searchFor,
130-
IList<XmlNode> searchIn,
131-
ICollection<int> availableIndexes,
132-
int fromInclusive, int toExclusive) {
133-
foreach (ElementSelector e in elementSelectors) {
134-
MatchInfo m = SearchIn(searchFor, searchIn, availableIndexes, fromInclusive, toExclusive, e);
135-
if (m != null) {
136-
return m;
137-
}
138-
}
139-
return null;
136+
0, indexOfLastMatch, e);
140137
}
141138

142139
private MatchInfo SearchIn(XmlNode searchFor,
@@ -195,4 +192,4 @@ public static bool DefaultNodeTypeMatcher(XmlNodeType controlType,
195192
&& testType == XmlNodeType.CDATA);
196193
}
197194
}
198-
}
195+
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/*
2+
This file is licensed to You under the Apache License, Version 2.0
3+
(the "License"); you may not use this file except in compliance with
4+
the License. You may obtain a copy of the License at
5+
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
8+
Unless required by applicable law or agreed to in writing, software
9+
distributed under the License is distributed on an "AS IS" BASIS,
10+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
See the License for the specific language governing permissions and
12+
limitations under the License.
13+
*/
14+
using System;
15+
using System.Collections.Generic;
16+
using System.Linq;
17+
using System.Xml;
18+
using NUnit.Framework;
19+
20+
namespace Org.XmlUnit.Diff {
21+
22+
[TestFixture]
23+
public class DefaultNodeMatcherTest {
24+
25+
private XmlDocument doc;
26+
27+
[SetUp]
28+
public void CreateDoc() {
29+
doc = new XmlDocument();
30+
}
31+
32+
[Test]
33+
public void ElementSelectorsAreQueriedInSequence() {
34+
XmlElement control1 = doc.CreateElement("a");
35+
control1.AppendChild(doc.CreateTextNode("foo"));
36+
XmlElement control2 = doc.CreateElement("a");
37+
control2.AppendChild(doc.CreateTextNode("bar"));
38+
39+
XmlElement test1 = doc.CreateElement("a");
40+
test1.AppendChild(doc.CreateTextNode("baz"));
41+
XmlElement test2 = doc.CreateElement("a");
42+
test2.AppendChild(doc.CreateTextNode("foo"));
43+
44+
DefaultNodeMatcher m =
45+
new DefaultNodeMatcher(ElementSelectors.ByNameAndText,
46+
ElementSelectors.ByName);
47+
List<KeyValuePair<XmlNode, XmlNode>> result =
48+
m.Match(new XmlNode[] { control1, control2},
49+
new XmlNode[] { test1, test2 }).ToList();
50+
Assert.AreEqual(result.Count, 2);
51+
52+
// ByNameAndText
53+
Assert.AreSame(result[0].Key, control1);
54+
Assert.AreSame(result[0].Value, test2);
55+
56+
// ByName
57+
Assert.AreSame(result[1].Key, control2);
58+
Assert.AreSame(result[1].Value, test1);
59+
}
60+
61+
[Test]
62+
// https://github.com/xmlunit/xmlunit/issues/197
63+
public void ElementSelectorsAreQueriedInSequenceWithConditionalSelector() {
64+
XmlElement control1 = doc.CreateElement("a");
65+
control1.AppendChild(doc.CreateTextNode("foo"));
66+
XmlElement control2 = doc.CreateElement("a");
67+
control2.AppendChild(doc.CreateTextNode("bar"));
68+
69+
XmlElement test1 = doc.CreateElement("a");
70+
test1.AppendChild(doc.CreateTextNode("baz"));
71+
XmlElement test2 = doc.CreateElement("a");
72+
test2.AppendChild(doc.CreateTextNode("foo"));
73+
74+
DefaultNodeMatcher m =
75+
new DefaultNodeMatcher(ElementSelectors.SelectorForElementNamed("a", ElementSelectors.ByNameAndText),
76+
ElementSelectors.ByName);
77+
List<KeyValuePair<XmlNode, XmlNode>> result =
78+
m.Match(new XmlNode[] { control1, control2},
79+
new XmlNode[] { test1, test2 }).ToList();
80+
Assert.AreEqual(result.Count, 2);
81+
82+
// ByNameAndText
83+
Assert.AreSame(result[0].Key, control1);
84+
Assert.AreSame(result[0].Value, test2);
85+
86+
// ByName
87+
Assert.AreSame(result[1].Key, control2);
88+
Assert.AreSame(result[1].Value, test1);
89+
}
90+
91+
[Test]
92+
public void ElementSelectorsAreQueriedInSequenceWithControlNodesSwapped() {
93+
XmlElement control1 = doc.CreateElement("a");
94+
control1.AppendChild(doc.CreateTextNode("bar"));
95+
XmlElement control2 = doc.CreateElement("a");
96+
control2.AppendChild(doc.CreateTextNode("foo"));
97+
98+
XmlElement test1 = doc.CreateElement("a");
99+
test1.AppendChild(doc.CreateTextNode("foo"));
100+
XmlElement test2 = doc.CreateElement("a");
101+
test2.AppendChild(doc.CreateTextNode("baz"));
102+
103+
DefaultNodeMatcher m =
104+
new DefaultNodeMatcher(ElementSelectors.ByNameAndText,
105+
ElementSelectors.ByName);
106+
List<KeyValuePair<XmlNode, XmlNode>> result =
107+
m.Match(new XmlNode[] { control1, control2},
108+
new XmlNode[] { test1, test2 }).ToList();
109+
Assert.AreEqual(result.Count, 2);
110+
111+
// ByNameAndText
112+
Assert.AreSame(result[0].Key, control2);
113+
Assert.AreSame(result[0].Value, test1);
114+
115+
// ByName
116+
Assert.AreSame(result[1].Key, control1);
117+
Assert.AreSame(result[1].Value, test2);
118+
}
119+
}
120+
}

src/tests/net-core/NetFramework/XMLUnit.Core.Tests.NetFramework.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
<Compile Include="..\Diff\ByNameAndTextRecSelectorTest.cs" />
7575
<Compile Include="..\Diff\ChildNodeXPathContextProviderTest.cs" />
7676
<Compile Include="..\Diff\ComparisonControllersTest.cs" />
77+
<Compile Include="..\Diff\DefaultNodeMatcherTest.cs" />
7778
<Compile Include="..\Diff\DefaultComparisonFormatterTest.cs" />
7879
<Compile Include="..\Diff\DifferenceEvaluatorsTest.cs" />
7980
<Compile Include="..\Diff\DifferenceTest.cs" />

0 commit comments

Comments
 (0)