Skip to content

Commit

Permalink
port and actually improve fix for xmlunit/#40
Browse files Browse the repository at this point in the history
  • Loading branch information
bodewig committed Dec 12, 2015
1 parent eae0367 commit 52cec69
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 60 deletions.
4 changes: 4 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
* fixed the XPath context used by the `ByXPath` element selector so
that "." now refers to the current element.
Issue [xmlunit/#39](https://github.com/xmlunit/xmlunit/issues/39)
* `ElementSelectors.ConditionalBuilder` now stops at the first
predicate returning `true`, even if the associated `ElementSelector`
returns false.
Issue [xmlunit/#40](https://github.com/xmlunit/xmlunit/issues/40)

## XMLUnit.NET 2.0.0-alpha-02 - /Released 2015-11-21/

Expand Down
92 changes: 92 additions & 0 deletions src/main/net-core/Diff/DefaultConditionalSelectorBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
This file is licensed to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
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.
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;

namespace Org.XmlUnit.Diff {

internal class DefaultConditionalSelectorBuilder
: ElementSelectors.IConditionalSelectorBuilder,
ElementSelectors.IConditionalSelectorBuilderThen {

private ElementSelector defaultSelector;
private readonly IList<KeyValuePair<Predicate<XmlElement>, ElementSelector>> conditionalSelectors =
new List<KeyValuePair<Predicate<XmlElement>, ElementSelector>>();
private Predicate<XmlElement> pendingCondition;

public ElementSelectors.IConditionalSelectorBuilder ThenUse(ElementSelector es) {
if (pendingCondition == null) {
throw new InvalidOperationException("missing condition");
}
conditionalSelectors.Add(new KeyValuePair<Predicate<XmlElement>, ElementSelector>(pendingCondition, es));
pendingCondition = null;
return this;
}
public ElementSelectors.IConditionalSelectorBuilderThen When(Predicate<XmlElement> predicate) {
if (pendingCondition != null) {
throw new InvalidOperationException("unbalanced conditions");
}
pendingCondition = predicate;
return this;
}
public ElementSelectors.IConditionalSelectorBuilder ElseUse(ElementSelector es) {
if (defaultSelector != null) {
throw new InvalidOperationException("can't have more than one default selector");
}
defaultSelector = es;
return this;
}
public ElementSelectors.IConditionalSelectorBuilderThen WhenElementIsNamed(string expectedName) {
return When(ElementSelectors.ElementNamePredicate(expectedName));
}
public ElementSelectors.IConditionalSelectorBuilderThen WhenElementIsNamed(XmlQualifiedName expectedName) {
return When(ElementSelectors.ElementNamePredicate(expectedName));
}
public ElementSelector Build() {
if (pendingCondition != null) {
throw new InvalidOperationException("unbalanced conditions");
}
return new DefaultCondionalSelector(conditionalSelectors, defaultSelector).CanBeCompared;
}

internal class DefaultCondionalSelector {
private readonly IEnumerable<KeyValuePair<Predicate<XmlElement>, ElementSelector>> conditionalSelectors;
private readonly ElementSelector defaultSelector;

internal DefaultCondionalSelector(IEnumerable<KeyValuePair<Predicate<XmlElement>,
ElementSelector>> conditionalSelectors,
ElementSelector defaultSelector) {
this.conditionalSelectors = conditionalSelectors.ToArray();
this.defaultSelector = defaultSelector;
}

public bool CanBeCompared(XmlElement control, XmlElement test) {
foreach (KeyValuePair<Predicate<XmlElement>, ElementSelector> p in
conditionalSelectors) {
if (p.Key(control)) {
return p.Value(control, test);
}
}
if (defaultSelector != null) {
return defaultSelector(control, test);
}
return false;
}
}
}
}

63 changes: 9 additions & 54 deletions src/main/net-core/Diff/ElementSelectors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -349,9 +349,10 @@ public interface IConditionalSelectorBuilderThen {
/// </summary>
/// <remarks>
/// <para>
/// All pairs created by the when*/thenUse pairs
/// are evaluated in order until one returns true, finally the
/// default, if any, is consulted.
/// All When*s are consulted in order and if one returns true
/// then the associated ElementSelector is used. If all of
/// them return false, the default set up with ElseUse if any
/// is used.
/// </para>
/// </remarks>
public interface IConditionalSelectorBuilder {
Expand All @@ -368,9 +369,10 @@ public interface IConditionalSelectorBuilder {
/// </summary>
IConditionalSelectorBuilderThen WhenElementIsNamed(XmlQualifiedName expectedName);
/// <summary>
/// Assigns a default ElementSelector.
/// Assigns a default ElementSelector that is used if all
/// Whens have returned false.
/// </summary>
IConditionalSelectorBuilder DefaultTo(ElementSelector es);
IConditionalSelectorBuilder ElseUse(ElementSelector es);
/// <summary>
/// Builds a conditional ElementSelector.
/// </summary>
Expand All @@ -391,53 +393,6 @@ public static IConditionalSelectorBuilder ConditionalBuilder() {
return new DefaultConditionalSelectorBuilder();
}

private class DefaultConditionalSelectorBuilder
: IConditionalSelectorBuilder, IConditionalSelectorBuilderThen {
private ElementSelector defaultSelector;
private readonly IList<ElementSelector> conditionalSelectors = new List<ElementSelector>();
private Predicate<XmlElement> pendingCondition;

public IConditionalSelectorBuilder ThenUse(ElementSelector es) {
if (pendingCondition == null) {
throw new InvalidOperationException("missing condition");
}
conditionalSelectors.Add(ConditionalSelector(pendingCondition, es));
pendingCondition = null;
return this;
}
public IConditionalSelectorBuilderThen When(Predicate<XmlElement> predicate) {
if (pendingCondition != null) {
throw new InvalidOperationException("unbalanced conditions");
}
pendingCondition = predicate;
return this;
}
public IConditionalSelectorBuilder DefaultTo(ElementSelector es) {
if (defaultSelector != null) {
throw new InvalidOperationException("can't have more than one default selector");
}
defaultSelector = es;
return this;
}
public IConditionalSelectorBuilderThen WhenElementIsNamed(string expectedName) {
return When(ElementNamePredicate(expectedName));
}
public IConditionalSelectorBuilderThen WhenElementIsNamed(XmlQualifiedName expectedName) {
return When(ElementNamePredicate(expectedName));
}
public ElementSelector Build() {
if (pendingCondition != null) {
throw new InvalidOperationException("unbalanced conditions");
}
List<ElementSelector> es = new List<ElementSelector>();
es.AddRange(conditionalSelectors);
if (defaultSelector != null) {
es.Add(defaultSelector);
}
return Or(es.ToArray());
}
}

private static bool
MapsEqualForKeys(IDictionary<XmlQualifiedName, string> control,
IDictionary<XmlQualifiedName, string> test,
Expand All @@ -449,11 +404,11 @@ private static bool
});
}

private static Predicate<XmlElement> ElementNamePredicate(string expectedName) {
internal static Predicate<XmlElement> ElementNamePredicate(string expectedName) {
return e => e != null && e.LocalName == expectedName;
}

private static Predicate<XmlElement> ElementNamePredicate(XmlQualifiedName expectedName) {
internal static Predicate<XmlElement> ElementNamePredicate(XmlQualifiedName expectedName) {
return e => expectedName == e.GetQName();
}

Expand Down
1 change: 1 addition & 0 deletions src/main/net-core/XMLUnit.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
<Compile Include="Diff\ComparisonType.cs" />
<Compile Include="Diff\ComparisonTypes.cs" />
<Compile Include="Diff\DefaultComparisonFormatter.cs" />
<Compile Include="Diff\DefaultConditionalSelectorBuilder.cs" />
<Compile Include="Diff\DefaultNodeMatcher.cs" />
<Compile Include="Diff\Diff.cs" />
<Compile Include="Diff\Difference.cs" />
Expand Down
16 changes: 10 additions & 6 deletions src/tests/net-core/Diff/ElementSelectorsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -403,8 +403,12 @@ public void ConditionalBuilder() {

Assert.IsFalse(builder.Build()(control, test));

builder.DefaultTo(ElementSelectors.Default);
Assert.IsTrue(builder.Build()(control, test));
builder.ElseUse(ElementSelectors.Default);
// not or-ed
Assert.IsFalse(builder.Build()(control, test));

XmlElement control2 = doc.CreateElement("baz");
Assert.IsTrue(builder.Build()(control2, test));
}

[Test]
Expand Down Expand Up @@ -549,10 +553,10 @@ public void ConditionalSelectorBuilderWontAllowMultipleWhensWithoutInterleavingT
[Test]
public void ConditionalSelectorBuilderWontAllowMultipleDefaults() {
Assert.Throws<InvalidOperationException>(() => {
ElementSelectors.IConditionalSelectorBuilder b =
ElementSelectors.ConditionalBuilder();
b.DefaultTo(ElementSelectors.ByName);
b.DefaultTo(ElementSelectors.ByName);});
ElementSelectors.IConditionalSelectorBuilder b = ElementSelectors.ConditionalBuilder();
b.ElseUse(ElementSelectors.ByName);
b.ElseUse(ElementSelectors.ByName);
});
}
}
}

0 comments on commit 52cec69

Please sign in to comment.