Skip to content

Commit 39261ea

Browse files
authored
Dev (#7)
* Initial commit * Updated info on common files * Added tools * Add changelog * Ignore tools * Build process set up * Updated devel to dev * Fixed path issues and C# version/target * Try 2019 image * Removed CI github action * cleanup of csproj * Namespace renamed * additional cleanup of csproj * renamed builder for more flexibility and usecases * fixes after namespace changes * more fixes after merge * Fixed WithDefaultOptions logic where text nodes was compared by a naive node comparer, not text comparer * Remove appveyor logger during testing * Added docs generation * Tweaked DiffBuilders options handling, updated documentation * Small tweaks to api * Preview 1 release * Reverted back to older version of tunnelvisionlabs.referenceassemblyannotator * Enabled build during Create Package cake step * Enabled build during Unit Test cake step * Tweaks to cake config * Added docs to AttributeComparisonSource * Added HtmlDifferenceEngine.Compare(INode controlNode, INode testNode) to make it easier to compare single nodes to each other. * Updated changelog * Fixed path index calculation * Refactored engine to clear single use. * Updates to change log * Updates to docs and tweaks to naming.
1 parent d795aab commit 39261ea

36 files changed

+816
-743
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
# 0.13.0
2+
3+
Released on Wednesday, December 25, 2019.
4+
5+
- Updated path's index' calculation in `ComparisonSource` to only include nodes that implement the IParentNode.
6+
- Small change to HtmlDifferenceEngine. It now takes the control and test sources during construction. This makes it clear it is single use. For reusable differ, use `HtmlDiffer` going forward.
7+
- Added interface `IDiffContext` and made `DiffContext` internal.
8+
19
# 0.13.0-preview-3
210

311
Released on Sunday, November 24, 2019.

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,5 @@ Read more about the available options on the [Options](/docs/Options.md) page.
4040
- [Difference engine internals](/docs/DiffingEngineInternals.md)
4141

4242
## Acknowledgments
43-
Big thanks to [Florian Rappl](https://github.com/FlorianRappl) from the AngleSharp team for providing ideas, input and sample code for working with AngleSharp.
44-
45-
Another shout-out goes to [XMLUnit](https://www.xmlunit.org). It is a great XML diffing library, and it has been a great inspiration for creating this library.
43+
- [Florian Rappl](https://github.com/FlorianRappl) from the AngleSharp team for providing ideas, input and sample code for working with AngleSharp.
44+
- [XMLUnit](https://www.xmlunit.org) has been a great inspiration for creating this library.

docs/DiffingEngineInternals.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ The heart of the library is the `HtmlDifferenceEngine` class, which goes through
33

44
The `HtmlDifferenceEngine` class depends on three _strategies_, the `IFilterStrategy`, `IMatcherStrategy`, and `ICompareStrategy` types. These are used in the highlighted activities in the diagram. With those, we can control what nodes and attributes take part in the comparison (filter strategy), how control and test nodes and attributes are matched up for comparison (matching strategy), and finally, how nodes and attributes are determined to be same or different (compare strategy).
55

6-
It starts with a call to the `Compare(INodeList controlNodes, INodeList testNodes)` and recursively calls itself when nodes have child nodes.
6+
It starts with a call to the `Compare()` and recursively calls itself when nodes have child nodes.
77

88
![img](HtmlDifferenceEngineFlow.svg)
99

docs/HtmlDifferenceEngineFlow.svg

Lines changed: 461 additions & 469 deletions
Loading

src/AngleSharp.Diffing.Tests/AngleSharp.DiffingTests.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<TargetFramework>netcoreapp3.0</TargetFramework>
@@ -10,7 +10,7 @@
1010
<ItemGroup>
1111
<PackageReference Include="AngleSharp" Version="$(AngleSharpVersion)" />
1212
<PackageReference Include="AngleSharp.Css" Version="$(AngleSharpVersion)" />
13-
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="$(FxCopAnalyzersVersion)">
13+
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8">
1414
<PrivateAssets>all</PrivateAssets>
1515
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1616
</PackageReference>

src/AngleSharp.Diffing.Tests/Core/ComparisonSourceTest.cs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using Shouldly;
1+
using System;
2+
using Shouldly;
23
using Xunit;
34

45
namespace AngleSharp.Diffing.Core
@@ -65,5 +66,44 @@ public void Test002()
6566

6667
source.GetHashCode().ShouldNotBe(otherSource.GetHashCode());
6768
}
69+
70+
[Theory(DisplayName = "The index in the source's path is based on its position in it's parents" +
71+
"child node list, i.e. excluding other node types that does not contain children")]
72+
[InlineData("<p>", 0, "p(0)")]
73+
[InlineData("text<p>", 1, "p(0)")]
74+
[InlineData("<!--x--><p>", 1, "p(0)")]
75+
[InlineData("<i></i>text<p>", 2, "p(1)")]
76+
[InlineData("<i></i><!--x--><p>", 2, "p(1)")]
77+
public void Test005(string sourceMarkup, int nodeIndex, string expectedPath)
78+
{
79+
var node = ToNodeList(sourceMarkup)[nodeIndex];
80+
81+
var sut = new ComparisonSource(node, ComparisonSourceType.Control);
82+
83+
sut.Path.ShouldBe(expectedPath);
84+
}
85+
86+
[Fact(DisplayName = "The parent path is calculated correctly when not provided")]
87+
public void Test006()
88+
{
89+
var nodes = ToNodeList("<p>txt<br/><i>text</i></p>");
90+
var textNode = nodes[0].ChildNodes[2].FirstChild;
91+
92+
var sut = new ComparisonSource(textNode, ComparisonSourceType.Control);
93+
94+
sut.Path.ShouldBe("p(0) > i(1) > #text(0)");
95+
}
96+
97+
[Fact(DisplayName = "Source uses parent path if provided to construct own path")]
98+
public void Test007()
99+
{
100+
var node = ToNode("<p>");
101+
var parentPath = "SOME > PAHT";
102+
103+
var sut = new ComparisonSource(node, 0, parentPath, ComparisonSourceType.Control);
104+
105+
var expectedPath = ComparisonSource.CombinePath(parentPath, ComparisonSource.GetNodePathSegment(node));
106+
sut.Path.ShouldBe(expectedPath);
107+
}
68108
}
69109
}

src/AngleSharp.Diffing.Tests/Core/DiffingEngineTestBase.cs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,16 @@ public DiffingEngineTestBase(DiffingTestFixture fixture) : base(fixture)
99
{
1010
}
1111

12-
protected static HtmlDifferenceEngine CreateHtmlDiffEngine(
13-
Func<DiffContext, SourceCollection, SourceCollection, IEnumerable<Comparison>>? nodeMatcher = null,
14-
Func<DiffContext, SourceMap, SourceMap, IEnumerable<AttributeComparison>>? attrMatcher = null,
12+
protected static HtmlDiffer CreateHtmlDiffEngine(
13+
Func<IDiffContext, SourceCollection, SourceCollection, IEnumerable<Comparison>>? nodeMatcher = null,
14+
Func<IDiffContext, SourceMap, SourceMap, IEnumerable<AttributeComparison>>? attrMatcher = null,
1515
Func<ComparisonSource, FilterDecision>? nodeFilter = null,
1616
Func<AttributeComparisonSource, FilterDecision>? attrFilter = null,
1717
Func<Comparison, CompareResult>? nodeComparer = null,
1818
Func<AttributeComparison, CompareResult>? attrComparer = null
1919
)
2020
{
21-
return new HtmlDifferenceEngine(
21+
return new HtmlDiffer(
2222
new MockDiffingStrategy(
2323
nodeFilter, attrFilter,
2424
nodeMatcher, attrMatcher,
@@ -31,16 +31,16 @@ class MockDiffingStrategy : IDiffingStrategy
3131
{
3232
private readonly Func<ComparisonSource, FilterDecision>? _nodeFilter;
3333
private readonly Func<AttributeComparisonSource, FilterDecision>? _attrFilter;
34-
private readonly Func<DiffContext, SourceCollection, SourceCollection, IEnumerable<Comparison>>? _nodeMatcher;
35-
private readonly Func<DiffContext, SourceMap, SourceMap, IEnumerable<AttributeComparison>>? _attrMatcher;
34+
private readonly Func<IDiffContext, SourceCollection, SourceCollection, IEnumerable<Comparison>>? _nodeMatcher;
35+
private readonly Func<IDiffContext, SourceMap, SourceMap, IEnumerable<AttributeComparison>>? _attrMatcher;
3636
private readonly Func<Comparison, CompareResult>? _nodeCompare;
3737
private readonly Func<AttributeComparison, CompareResult>? _attrCompare;
3838

3939
public MockDiffingStrategy(
4040
Func<ComparisonSource, FilterDecision>? nodeFilter = null,
4141
Func<AttributeComparisonSource, FilterDecision>? attrFilter = null,
42-
Func<DiffContext, SourceCollection, SourceCollection, IEnumerable<Comparison>>? nodeMatcher = null,
43-
Func<DiffContext, SourceMap, SourceMap, IEnumerable<AttributeComparison>>? attrMatcher = null,
42+
Func<IDiffContext, SourceCollection, SourceCollection, IEnumerable<Comparison>>? nodeMatcher = null,
43+
Func<IDiffContext, SourceMap, SourceMap, IEnumerable<AttributeComparison>>? attrMatcher = null,
4444
Func<Comparison, CompareResult>? nodeCompare = null,
4545
Func<AttributeComparison, CompareResult>? attrCompare = null)
4646
{
@@ -59,12 +59,12 @@ public FilterDecision Filter(in ComparisonSource comparisonSource)
5959
=> _nodeFilter!(comparisonSource);
6060

6161
public IEnumerable<Comparison> Match(
62-
DiffContext context,
62+
IDiffContext context,
6363
SourceCollection controlNodes,
6464
SourceCollection testNodes) => _nodeMatcher!(context, controlNodes, testNodes);
6565

6666
public IEnumerable<AttributeComparison> Match(
67-
DiffContext context,
67+
IDiffContext context,
6868
SourceMap controlAttributes,
6969
SourceMap testAttributes) => _attrMatcher!(context, controlAttributes, testAttributes);
7070

src/AngleSharp.Diffing.Tests/Core/HtmlDifferenceEngineTest.cs

Lines changed: 7 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -296,51 +296,7 @@ public void OnlyOnePartHasChildNodes(string control, string test, Type expectedD
296296
results.Count.ShouldBe(2);
297297
results[0].ShouldBeOfType<NodeDiff>();
298298
results[1].ShouldBeOfType(expectedDiffType);
299-
}
300-
301-
[Fact(DisplayName = "Path in Diffs is set correctly when nested nodes are compared")]
302-
public void PathIsSetCorrectly()
303-
{
304-
var ctrlNodes = ToNodeList(@"<main><h1><!--foo--><p>hello world</p></h1></main>");
305-
var testNodes = ToNodeList(@"<!--foo--><main><h1><p>hello world</p></h1></main>");
306-
307-
var sut = CreateHtmlDiffEngine(
308-
nodeMatcher: OneToOneNodeListMatcher,
309-
nodeFilter: RemoveCommentNodeFilter,
310-
nodeComparer: DiffResultNodeComparer);
311-
312-
var results = sut.Compare(ctrlNodes, testNodes).ToList();
313-
314-
results.Count.ShouldBe(4);
315-
results[0].ShouldBeOfType<NodeDiff>().Control.Path.ShouldBe("main(0)");
316-
results[0].ShouldBeOfType<NodeDiff>().Test.Path.ShouldBe("main(1)");
317-
results[1].ShouldBeOfType<NodeDiff>().Control.Path.ShouldBe("main(0) > h1(0)");
318-
results[1].ShouldBeOfType<NodeDiff>().Test.Path.ShouldBe("main(1) > h1(0)");
319-
results[2].ShouldBeOfType<NodeDiff>().Control.Path.ShouldBe("main(0) > h1(0) > p(1)");
320-
results[2].ShouldBeOfType<NodeDiff>().Test.Path.ShouldBe("main(1) > h1(0) > p(0)");
321-
results[3].ShouldBeOfType<NodeDiff>().Control.Path.ShouldBe("main(0) > h1(0) > p(1) > #text(0)");
322-
results[3].ShouldBeOfType<NodeDiff>().Test.Path.ShouldBe("main(1) > h1(0) > p(0) > #text(0)");
323-
}
324-
325-
[Fact(DisplayName = "Attribute path in comparison sources are based on nodes tree structure")]
326-
public void AttributeSourcePathisBasedOnParentElements()
327-
{
328-
var nodes = ToNodeList(@"<p id=""foo""></p>");
329-
330-
var sut = CreateHtmlDiffEngine(
331-
nodeMatcher: OneToOneNodeListMatcher,
332-
nodeFilter: NoneNodeFilter,
333-
nodeComparer: SameResultNodeComparer,
334-
attrMatcher: AttributeNameMatcher,
335-
attrFilter: NoneAttrFilter,
336-
attrComparer: DiffResultAttrComparer);
337-
338-
var results = sut.Compare(nodes, nodes).ToList();
339-
340-
results.Count.ShouldBe(1);
341-
results[0].ShouldBeOfType<AttrDiff>().Control.Path.ShouldBe("p(0)[id]");
342-
results[0].ShouldBeOfType<AttrDiff>().Test.Path.ShouldBe("p(0)[id]");
343-
}
299+
}
344300

345301
[Fact(DisplayName = "Comparison sources have their type set correctly")]
346302
public void ComparisonSourcesHaveCorrectType()
@@ -404,17 +360,17 @@ public void Test2()
404360
#endregion
405361

406362
#region NodeMatchers
407-
private static IEnumerable<Comparison> NoneNodeMatcher(DiffContext ctx, SourceCollection controlNodes, SourceCollection testNodes)
363+
private static IEnumerable<Comparison> NoneNodeMatcher(IDiffContext ctx, SourceCollection controlNodes, SourceCollection testNodes)
408364
=> Array.Empty<Comparison>();
409365

410-
private static Func<DiffContext, SourceCollection, SourceCollection, IEnumerable<Comparison>> SpecificIndexNodeMatcher(int index)
366+
private static Func<IDiffContext, SourceCollection, SourceCollection, IEnumerable<Comparison>> SpecificIndexNodeMatcher(int index)
411367
=> (ctx, controlNodes, testNodes) =>
412368
{
413369
return new List<Comparison> { new Comparison(controlNodes[index], testNodes[index]) };
414370
};
415371

416372
private static IEnumerable<Comparison> OneToOneNodeListMatcher(
417-
DiffContext context,
373+
IDiffContext context,
418374
SourceCollection controlNodes,
419375
SourceCollection testNodes) => OneToOneNodeMatcher.Match(context, controlNodes, testNodes);
420376

@@ -427,19 +383,19 @@ private static IEnumerable<Comparison> OneToOneNodeListMatcher(
427383

428384
#region AttributeMatchers
429385
private static IReadOnlyList<AttributeComparison> NoneAttributeMatcher(
430-
DiffContext context,
386+
IDiffContext context,
431387
SourceMap controlAttributes,
432388
SourceMap testAttributes) => Array.Empty<AttributeComparison>();
433389

434-
private static Func<DiffContext, SourceMap, SourceMap, IEnumerable<AttributeComparison>> SpecificAttributeMatcher(string matchAttrName)
390+
private static Func<IDiffContext, SourceMap, SourceMap, IEnumerable<AttributeComparison>> SpecificAttributeMatcher(string matchAttrName)
435391
{
436392
return (ctx, ctrlAttrs, testAttrs) => new List<AttributeComparison>
437393
{
438394
new AttributeComparison(ctrlAttrs[matchAttrName], testAttrs[matchAttrName] )
439395
};
440396
}
441397

442-
private static IEnumerable<AttributeComparison> AttributeNameMatcher(DiffContext context, SourceMap controlAttrs, SourceMap testAttrs)
398+
private static IEnumerable<AttributeComparison> AttributeNameMatcher(IDiffContext context, SourceMap controlAttrs, SourceMap testAttrs)
443399
{
444400
foreach (var ctrlAttrSrc in controlAttrs)
445401
{

src/AngleSharp.Diffing.Tests/Core/SourceCollectionTest.cs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22
using System.Linq;
33
using Shouldly;
44
using Xunit;
@@ -49,22 +49,17 @@ public void Test2()
4949
sut.Count.ShouldBe(2);
5050
}
5151

52-
[Fact(DisplayName = "Collection should make sources accessible on the index they specify")]
52+
[Fact(DisplayName = "Collection throws if sources is not ordered source-index when enumerated")]
5353
public void Test3()
5454
{
5555
var sources = ToNodeList("<span></span><p></p><main></main><h1></h1><ul></ul><ol></ol><em></em>")
5656
.ToComparisonSourceList(ComparisonSourceType.Control)
5757
.OrderBy(x => x.Node.NodeName);
5858

59-
var sut = new SourceCollection(ComparisonSourceType.Control, sources);
59+
var cut = new SourceCollection(ComparisonSourceType.Control, sources);
6060

61-
sut[0].Index.ShouldBe(0);
62-
sut[1].Index.ShouldBe(1);
63-
sut[2].Index.ShouldBe(2);
64-
sut[3].Index.ShouldBe(3);
65-
sut[4].Index.ShouldBe(4);
66-
sut[5].Index.ShouldBe(5);
67-
sut[6].Index.ShouldBe(6);
61+
Should.Throw<InvalidOperationException>(() => cut.ToList());
62+
Should.Throw<InvalidOperationException>(() => cut.GetAllSources().ToList());
6863
}
6964

7065
[Fact(DisplayName = "When an source is removed from the list, it does not add to the count nor is it returned when iterating")]

src/AngleSharp.Diffing.Tests/DiffingTestBase.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ public abstract class DiffingTestBase : IClassFixture<DiffingTestFixture>
1111
{
1212
private readonly DiffingTestFixture _testFixture;
1313

14+
protected IDiffContext DummyContext { get; } = new DiffContext(default(IElement), default(IElement));
15+
1416
protected INodeList EmptyNodeList => ToNodeList("");
1517

1618
public DiffingTestBase(DiffingTestFixture fixture)

0 commit comments

Comments
 (0)