From 5fa6f91133471b1de82cba478311ecb318c0dcba Mon Sep 17 00:00:00 2001
From: 22222 <22222@users.noreply.github.com>
Date: Sun, 24 Nov 2019 09:56:05 -0600
Subject: [PATCH] Initial commit
---
.editorconfig | 9 +
.gitattributes | 2 +
.gitignore | 353 +++++++++
CodeAnalysis.ruleset | 31 +
Directory.Build.props | Bin 0 -> 1410 bytes
.../CodeAnalysis.ruleset | 16 +
JsonDeepEqual.SampleConsole/Program.cs | 135 ++++
.../Two.JsonDeepEqual.SampleConsole.csproj | 25 +
JsonDeepEqual.Tests/CodeAnalysis.ruleset | 17 +
.../JsonDeepEqualAssertTest.cs | 720 ++++++++++++++++++
JsonDeepEqual.Tests/JsonDeepEqualDiffTest.cs | 638 ++++++++++++++++
JsonDeepEqual.Tests/JsonDiffNodeTest.cs | 246 ++++++
JsonDeepEqual.Tests/JsonDiffTest.cs | 51 ++
JsonDeepEqual.Tests/TestEntities/Company.cs | 88 +++
.../TestEntities/TestClasses.cs | 200 +++++
.../Two.JsonDeepEqual.Tests.csproj | 30 +
JsonDeepEqual.sln | 56 ++
.../Exceptions/JsonEqualException.cs | 123 +++
.../Exceptions/JsonNotEqualException.cs | 32 +
JsonDeepEqual/JsonAssert.cs | 209 +++++
JsonDeepEqual/JsonDeepEqualAssert.cs | 137 ++++
JsonDeepEqual/JsonDeepEqualDiff.cs | 42 +
JsonDeepEqual/JsonDeepEqualDiffOptions.cs | 318 ++++++++
JsonDeepEqual/JsonDiff.cs | 285 +++++++
JsonDeepEqual/JsonDiffNode.cs | 222 ++++++
JsonDeepEqual/JsonDiffOptions.cs | 149 ++++
JsonDeepEqual/Two.JsonDeepEqual.csproj | 28 +
JsonDeepEqual/Utilities/GlobConvert.cs | 74 ++
LICENSE | 21 +
README.md | 191 +++++
UNLICENSE | 24 +
appveyor.yml | 13 +
stylecop.json | 14 +
33 files changed, 4499 insertions(+)
create mode 100644 .editorconfig
create mode 100644 .gitattributes
create mode 100644 .gitignore
create mode 100644 CodeAnalysis.ruleset
create mode 100644 Directory.Build.props
create mode 100644 JsonDeepEqual.SampleConsole/CodeAnalysis.ruleset
create mode 100644 JsonDeepEqual.SampleConsole/Program.cs
create mode 100644 JsonDeepEqual.SampleConsole/Two.JsonDeepEqual.SampleConsole.csproj
create mode 100644 JsonDeepEqual.Tests/CodeAnalysis.ruleset
create mode 100644 JsonDeepEqual.Tests/JsonDeepEqualAssertTest.cs
create mode 100644 JsonDeepEqual.Tests/JsonDeepEqualDiffTest.cs
create mode 100644 JsonDeepEqual.Tests/JsonDiffNodeTest.cs
create mode 100644 JsonDeepEqual.Tests/JsonDiffTest.cs
create mode 100644 JsonDeepEqual.Tests/TestEntities/Company.cs
create mode 100644 JsonDeepEqual.Tests/TestEntities/TestClasses.cs
create mode 100644 JsonDeepEqual.Tests/Two.JsonDeepEqual.Tests.csproj
create mode 100644 JsonDeepEqual.sln
create mode 100644 JsonDeepEqual/Exceptions/JsonEqualException.cs
create mode 100644 JsonDeepEqual/Exceptions/JsonNotEqualException.cs
create mode 100644 JsonDeepEqual/JsonAssert.cs
create mode 100644 JsonDeepEqual/JsonDeepEqualAssert.cs
create mode 100644 JsonDeepEqual/JsonDeepEqualDiff.cs
create mode 100644 JsonDeepEqual/JsonDeepEqualDiffOptions.cs
create mode 100644 JsonDeepEqual/JsonDiff.cs
create mode 100644 JsonDeepEqual/JsonDiffNode.cs
create mode 100644 JsonDeepEqual/JsonDiffOptions.cs
create mode 100644 JsonDeepEqual/Two.JsonDeepEqual.csproj
create mode 100644 JsonDeepEqual/Utilities/GlobConvert.cs
create mode 100644 LICENSE
create mode 100644 README.md
create mode 100644 UNLICENSE
create mode 100644 appveyor.yml
create mode 100644 stylecop.json
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..5af9588
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,9 @@
+root = false
+
+[*]
+indent_style = space
+indent_size = 4
+end_of_line = crlf
+
+[*.{csproj,props,json,ruleset}]
+indent_size = 2
\ No newline at end of file
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..ac5bf39
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,2 @@
+* -text
+*.cs diff=csharp
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..353ae84
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,353 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
\ No newline at end of file
diff --git a/CodeAnalysis.ruleset b/CodeAnalysis.ruleset
new file mode 100644
index 0000000..2180d5d
--- /dev/null
+++ b/CodeAnalysis.ruleset
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 0000000000000000000000000000000000000000..04af7a484c2ab3c851888e0ed11f17952a1a58f7
GIT binary patch
literal 1410
zcmbW1Pfx-?5XI+g;&&j#1i1iL6CpuSLZZe9(KG)<6bfnK&@Zq4X4a*&W{Hw!({|_0
zo40SK{rbF=M~P)6?=qFKY@{Wpav~)zA6XNLiKNmsQ+;|te{5ZeWCm}Wb;FL%Ow4oMx&P$lD?XSr3#yHN
zI-))FZ+ep72+yEVH^t#u&9Gr)$=r(Q!hAO&O({#o`lL=q@=9!m));MmimD)*Tos$q
z5z!gXWxVlKvq^W+0ew`hE%A#zPE!7?yaoBDdHD{ZbrH5#`zb;+SgA@I6jD
zrDFIW(8jlY=XCD<6en=?aBs}s7St5C%12JW{vA?y^qW}WVvT=SV!bIk?p(j4W08E|
ebvRS~$Z-DDF2ByqA39mqWn~HZlsTUN(|!X#LhD8V
literal 0
HcmV?d00001
diff --git a/JsonDeepEqual.SampleConsole/CodeAnalysis.ruleset b/JsonDeepEqual.SampleConsole/CodeAnalysis.ruleset
new file mode 100644
index 0000000..b7decab
--- /dev/null
+++ b/JsonDeepEqual.SampleConsole/CodeAnalysis.ruleset
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/JsonDeepEqual.SampleConsole/Program.cs b/JsonDeepEqual.SampleConsole/Program.cs
new file mode 100644
index 0000000..bfd0670
--- /dev/null
+++ b/JsonDeepEqual.SampleConsole/Program.cs
@@ -0,0 +1,135 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Serialization;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Two.JsonDeepEqual;
+using Two.JsonDeepEqual.Exceptions;
+
+#pragma warning disable CA1801 // Remove unused parameter
+#pragma warning disable CA1307 // Specify StringComparison
+
+namespace SampleConsole
+{
+ public static class Program
+ {
+ public static void Main(string[] args)
+ {
+ Sample_Basic();
+ Sample_WithOptions();
+ Sample_Json();
+ Sample_WithAllOptions();
+ Diff_Basic();
+ Diff_Json();
+ }
+
+ public static void Sample_Basic()
+ {
+ var expected = new
+ {
+ Message = "Hello!",
+ Child = new { Id = 1, Values = new[] { 1, 2, 3 } },
+ };
+ var actual = new
+ {
+ Message = "Hello, World!",
+ Child = new { Id = 2, Values = new[] { 1, 4, 3 } },
+ };
+ try
+ {
+ JsonDeepEqualAssert.Equal(expected, actual);
+ JsonDeepEqualAssert.AreEqual(expected, actual);
+ }
+ catch (JsonEqualException ex)
+ {
+ Console.WriteLine(ex.Message);
+ Console.WriteLine();
+ }
+
+ JsonDeepEqualAssert.NotEqual(expected, actual);
+ JsonDeepEqualAssert.AreNotEqual(expected, actual);
+ }
+
+ public static void Sample_WithOptions()
+ {
+ var expected = new
+ {
+ Id = 1,
+ Message = "Hello!",
+ Child = new { Id = 10, Values = new[] { 1, 2, 3 } },
+ Created = new DateTime(2002, 2, 2, 12, 22, 23),
+ };
+ var actual = new
+ {
+ Id = 2,
+ Message = "Hello, World!",
+ Child = new { Id = 11, Values = new[] { 1, 4, 3 } },
+ Created = new DateTime(2002, 2, 2, 12, 22, 22, 999),
+ };
+ JsonDeepEqualAssert.Equal(expected, actual, new JsonDeepEqualDiffOptions
+ {
+ ExcludePropertyNames = new[] { "Id", "Mess*" },
+ ExcludePropertyPaths = new[] { "**/Values/*" },
+ IgnoreArrayElementOrder = true,
+ DateFormatString = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFK",
+ DateTimeConverter = (DateTime dt) => new System.Data.SqlTypes.SqlDateTime(dt).Value,
+ });
+ }
+
+ public static void Sample_Json()
+ {
+ var expectedJson = @"{ ""a"":1 }";
+ var actualJson = @"{ ""a"":2 }";
+ try
+ {
+ JsonAssert.Equal(expectedJson, actualJson);
+ }
+ catch (JsonEqualException ex)
+ {
+ Console.WriteLine(ex.Message);
+ Console.WriteLine();
+ }
+ }
+
+ public static void Sample_WithAllOptions()
+ {
+ var expected = 1;
+ var actual = 1;
+ JsonDeepEqualAssert.Equal(expected, actual, new JsonDeepEqualDiffOptions
+ {
+ ExcludePropertyNames = new[] { "Id", "*DateTime" },
+ PropertyFilter = (IEnumerable properties) => properties.Where(p => p.PropertyName != "Id" && p.PropertyType != typeof(DateTime)),
+ NullValueHandling = NullValueHandling.Include,
+ DefaultValueHandling = DefaultValueHandling.Include,
+ ReferenceLoopHandling = ReferenceLoopHandling.Error,
+ DateFormatString = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFK",
+ DateTimeConverter = (DateTime dt) => new System.Data.SqlTypes.SqlDateTime(dt).Value,
+
+ ExcludePropertyPaths = new[] { "**System/*" },
+ PropertyPathFilter = (IEnumerable paths) => paths.Where(p => !p.Contains("System")),
+ IgnoreArrayElementOrder = true,
+ IgnoreCase = true,
+ IgnoreLineEndingDifferences = true,
+ IgnoreWhiteSpaceDifferences = true,
+ });
+ }
+
+ public static void Diff_Basic()
+ {
+ var expected = new { Message = "Hello!" };
+ var actual = new { Message = "Hello, World!" };
+
+ IEnumerable differences = JsonDeepEqualDiff.EnumerateDifferences(expected, actual);
+ Console.WriteLine(string.Join(Environment.NewLine, differences.Take(10)));
+ }
+
+ public static void Diff_Json()
+ {
+ var expectedJson = @"{ ""a"":1 }";
+ var actualJson = @"{ ""a"":2 }";
+
+ IEnumerable differences = JsonDiff.EnumerateDifferences(expectedJson, actualJson);
+ Console.WriteLine(string.Join(Environment.NewLine, differences.Take(10)));
+ }
+ }
+}
diff --git a/JsonDeepEqual.SampleConsole/Two.JsonDeepEqual.SampleConsole.csproj b/JsonDeepEqual.SampleConsole/Two.JsonDeepEqual.SampleConsole.csproj
new file mode 100644
index 0000000..151707a
--- /dev/null
+++ b/JsonDeepEqual.SampleConsole/Two.JsonDeepEqual.SampleConsole.csproj
@@ -0,0 +1,25 @@
+
+
+
+ Exe
+ netcoreapp3.0
+ CodeAnalysis.ruleset
+ 1701;1702;1591
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
diff --git a/JsonDeepEqual.Tests/CodeAnalysis.ruleset b/JsonDeepEqual.Tests/CodeAnalysis.ruleset
new file mode 100644
index 0000000..f234a6d
--- /dev/null
+++ b/JsonDeepEqual.Tests/CodeAnalysis.ruleset
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/JsonDeepEqual.Tests/JsonDeepEqualAssertTest.cs b/JsonDeepEqual.Tests/JsonDeepEqualAssertTest.cs
new file mode 100644
index 0000000..fc4a9b1
--- /dev/null
+++ b/JsonDeepEqual.Tests/JsonDeepEqualAssertTest.cs
@@ -0,0 +1,720 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using Two.JsonDeepEqual.Exceptions;
+using Xunit;
+
+namespace Two.JsonDeepEqual
+{
+ public class JsonDeepEqualAssertTest
+ {
+ [Theory]
+ [InlineData(2, 2, true)]
+ [InlineData(2, 1, false)]
+ [InlineData(2, 2f, false)]
+ [InlineData(0f, 0f, true)]
+ [InlineData(0f, float.Epsilon * 2, true)]
+ [InlineData(0d, 0d, true)]
+ [InlineData(0d, double.Epsilon * 2, true)]
+ [InlineData("Hello world!", "Hello world!", true)]
+ [InlineData("Hello World!", "Hello world!", false)]
+ [InlineData("hello", "world", false)]
+ [InlineData(2, null, false)]
+ [InlineData("test", null, false)]
+ [InlineData(null, null, true)]
+ public void Equal_SimpleValues(object a, object b, bool expected)
+ {
+ if (expected)
+ {
+ AssertDeepEqual(a, b);
+ AssertDeepEqual(b, a);
+ }
+ else
+ {
+ AssertNotDeepEqual(a, b);
+ AssertNotDeepEqual(b, a);
+ }
+ }
+
+ [Fact]
+ public void Equal_ByteArrayWithSelf()
+ {
+ var a = new byte[] { 1, 171, 128, 3 };
+ AssertDeepEqual(a, a);
+ }
+
+ [Fact]
+ public void Equal_ByteArrays()
+ {
+ var a = new byte[] { 1, 171, 128, 3 };
+ var b = new byte[] { 1, 171, 128, 3 };
+ AssertDeepEqual(a, b);
+ }
+
+ [Fact]
+ public void NotEqual_ByteArrays()
+ {
+ var a = new byte[] { 1, 171, 128, 3 };
+ var b = new byte[] { 1, 171, 128, 2 };
+ AssertNotDeepEqual(a, b);
+ }
+
+ [Fact]
+ public void Equal_FloatsAlmostEqual()
+ {
+ var a = 0f;
+ var b = float.Epsilon / 2;
+ AssertDeepEqual(a, b);
+ }
+
+ [Fact]
+ public void Equal_DoublesAlmostEqual()
+ {
+ var a = 0d;
+ var b = double.Epsilon / 2;
+ AssertDeepEqual(a, b);
+ }
+
+ [Fact]
+ public void Equal_SameEmptyObjects()
+ {
+ var obj = new object();
+ JsonDeepEqualAssert.Equal(obj, obj);
+ }
+
+ [Fact]
+ public void Equal_EmptyObjects()
+ {
+ JsonDeepEqualAssert.Equal(new object(), new object());
+ }
+
+ [Fact]
+ public void Equal_SameString()
+ {
+ JsonDeepEqualAssert.Equal("2", "2");
+ }
+
+ [Fact]
+ public void NotEqual_DifferentStrings()
+ {
+ JsonDeepEqualAssert.NotEqual("1", "2");
+ }
+
+ #region Messages
+
+ [Fact]
+ public void Equal_DifferentStrings_ShouldThrowExceptionWithExpectedMessage()
+ {
+ var expected = "Test hello";
+ var actual = "Test world";
+ var actualException = Assert.Throws(() => JsonDeepEqualAssert.Equal(expected, actual));
+
+ var expectedMessage = @"JsonDeepEqualAssert.Equal() Failure: 1 difference
+ ↓ (pos 6)
+ Expected: ""Test hello""
+ Actual: ""Test world""
+ ↑ (pos 6)";
+ Assert.Equal(expectedMessage, actualException.Message, ignoreLineEndingDifferences: true);
+ }
+
+ [Fact]
+ public void Equal_DifferentAnonymousObjects_ShouldThrowExceptionWithExpectedMessage()
+ {
+ var expected = new { id = 1, message = "hello" };
+ var actual = new { id = 2, message = "world" };
+ var actualException = Assert.Throws(() => JsonDeepEqualAssert.Equal(expected, actual));
+
+ var expectedMessage = @"JsonDeepEqualAssert.Equal() Failure: 2 differences
+/id:
+ Expected: 1
+ Actual: 2
+/message:
+ ↓ (pos 1)
+ Expected: ""hello""
+ Actual: ""world""
+ ↑ (pos 1)";
+ Assert.Equal(expectedMessage, actualException.Message, ignoreLineEndingDifferences: true);
+ }
+
+ [Fact]
+ public void Equal_DifferentCustomObjects_ShouldThrowExceptionWithExpectedMessage()
+ {
+ var expected = new Company { Id = 1, Name = "hello" };
+ var actual = new Company { Id = 2, Name = "world" };
+ var actualException = Assert.Throws(() => JsonDeepEqualAssert.Equal(expected, actual));
+
+ var expectedMessage = @"JsonDeepEqualAssert.Equal() Failure: 2 differences
+/Id:
+ Expected: 1
+ Actual: 2
+/Name:
+ ↓ (pos 1)
+ Expected: ""hello""
+ Actual: ""world""
+ ↑ (pos 1)";
+ Assert.Equal(expectedMessage, actualException.Message, ignoreLineEndingDifferences: true);
+ }
+
+ [Fact]
+ public void Equal_DifferentIntLists_ShouldThrowExceptionWithExpectedMessage()
+ {
+ var expected = new[] { 0, 1, 2 };
+ var actual = new[] { 1, 2 };
+ var actualException = Assert.Throws(() => JsonDeepEqualAssert.Equal(expected, actual));
+
+ var expectedMessage = @"JsonDeepEqualAssert.Equal() Failure: 3 differences
+/0:
+ Expected: 0
+ Actual: 1
+/1:
+ Expected: 1
+ Actual: 2
+/2:
+ Expected: 2
+ Actual: null";
+ Assert.Equal(expectedMessage, actualException.Message, ignoreLineEndingDifferences: true);
+ }
+
+ [Fact]
+ public void Equal_DifferentObjects_LongValues_ShouldThrowExceptionWithExpectedMessage()
+ {
+ var obj1 = new Person
+ {
+ Id = 1,
+ FullName = string.Join(string.Empty, Enumerable.Range(0, 512)),
+ };
+ var obj2 = new Person
+ {
+ Id = 2,
+ FullName = string.Join(string.Empty, Enumerable.Range(0, 256)) + string.Join(string.Empty, Enumerable.Range(1, 256)),
+ };
+ var actualException = Assert.Throws(() => JsonDeepEqualAssert.Equal(obj1, obj2));
+
+ var expectedMessage = @"JsonDeepEqualAssert.Equal() Failure: 2 differences
+/Id:
+ Expected: 1
+ Actual: 2
+/FullName:
+ ↓ (pos 659)
+ Expected: …4925025125225325425525625725825926026126226326426526626726826…
+ Actual: …4925025125225325425512345678910111213141516171819202122232425…
+ ↑ (pos 659)";
+ Assert.Equal(expectedMessage, actualException.Message, ignoreLineEndingDifferences: true);
+ }
+
+ [Fact]
+ public void Equal_LotsOfDifferences_ShouldThrowExceptionWithExpectedMessage()
+ {
+ var obj1 = new ListTestClass
+ {
+ List = Enumerable.Range(0, 512).ToList(),
+ };
+ var obj2 = new ListTestClass
+ {
+ List = Enumerable.Range(1, 512).ToList(),
+ };
+ var actualException = Assert.Throws(() => JsonDeepEqualAssert.Equal(obj1, obj2));
+
+ var expectedStartMessage = @"JsonDeepEqualAssert.Equal() Failure: 20+ differences
+/List/0:
+ Expected: 0
+ Actual: 1
+/List/1:
+ Expected: 1
+ Actual: 2";
+ Assert.StartsWith(
+ expectedStartMessage.Replace("\r", string.Empty, StringComparison.Ordinal),
+ actualException.Message.Replace("\r", string.Empty, StringComparison.Ordinal),
+ StringComparison.Ordinal
+ );
+ }
+
+ [Fact]
+ public void NotEqual_SameString_ShouldThrowExceptionWithExpectedMessage()
+ {
+ var expected = "hello";
+ var actual = "hello";
+ var actualException = Assert.Throws(() => JsonDeepEqualAssert.NotEqual(expected, actual));
+
+ var expectedMessage = "JsonDeepEqualAssert.NotEqual() Failure";
+ Assert.Equal(expectedMessage, actualException.Message, ignoreLineEndingDifferences: true);
+ }
+
+ #endregion
+
+ [Fact]
+ public void Equal_EqualAddresses()
+ {
+ var a = CreateSampleAddress();
+ var b = CreateSampleAddress();
+ AssertDeepEqual(a, b);
+ }
+
+ [Fact]
+ public void Equal_DifferentAddresses_ShouldThrowException()
+ {
+ var a = new Address
+ {
+ Id = 1,
+ Lines = new[] { "123 Fake ST", "Arlington, VA 22222" },
+ };
+ var b = new Address
+ {
+ Id = 1,
+ Lines = new[] { "321 Fake ST", "Arlington, VA 22222" },
+ };
+ AssertNotDeepEqual(a, b);
+ AssertDeepEqual(a, b, new JsonDeepEqualDiffOptions
+ {
+ ExcludePropertyNames = new[] { nameof(Address.Lines) },
+ });
+
+ var actualException = Assert.Throws(() => JsonDeepEqualAssert.Equal(a, b));
+ Assert.Single(actualException.Differences);
+ Assert.Equal("/Lines/0", actualException.Differences.ElementAt(0).Path);
+ Assert.Equal("\"123 Fake ST\"", actualException.Differences.ElementAt(0).ExpectedValueDisplay);
+ Assert.Equal("\"321 Fake ST\"", actualException.Differences.ElementAt(0).ActualValueDisplay);
+ }
+
+ [Fact]
+ public void Equal_EqualCompanies()
+ {
+ var a = CreateSampleCompany();
+ var b = CreateSampleCompany();
+ AssertDeepEqual(a, b);
+ }
+
+ [Fact]
+ public void Equal_DifferentCompanies_ShouldThrowException()
+ {
+ var a = CreateSampleCompany();
+ var b = CreateSampleCompany();
+ b.Employees.First().FullName = "Robert Plant";
+
+ Assert.NotEqual(a, b);
+ AssertDeepEqual(a, b, new JsonDeepEqualDiffOptions
+ {
+ ExcludePropertyPaths = new[] { "/Employees/**" },
+ });
+ AssertDeepEqual(a, b, new JsonDeepEqualDiffOptions
+ {
+ ExcludePropertyPaths = new[] { "/Employees/*/FullName" },
+ });
+ AssertDeepEqual(a, b, new JsonDeepEqualDiffOptions
+ {
+ ExcludePropertyPaths = new[] { "**Employees/*/FullName" },
+ });
+ AssertDeepEqual(a, b, new JsonDeepEqualDiffOptions
+ {
+ ExcludePropertyNames = new[] { "FullName" },
+ });
+ AssertDeepEqual(a, b, new JsonDeepEqualDiffOptions
+ {
+ ExcludePropertyNames = new[] { "*Name*" },
+ });
+ AssertDeepEqual(a, b, new JsonDeepEqualDiffOptions
+ {
+ ExcludePropertyPaths = new[] { "Employees/0/*Name*" },
+ });
+
+ var actualException = Assert.Throws(() => JsonDeepEqualAssert.Equal(a, b));
+ Assert.Single(actualException.Differences);
+ Assert.Equal("/Employees/0/FullName", actualException.Differences.ElementAt(0).Path);
+ Assert.Equal("\"Robert Paulson\"", actualException.Differences.ElementAt(0).ExpectedValueDisplay);
+ Assert.Equal("\"Robert Plant\"", actualException.Differences.ElementAt(0).ActualValueDisplay);
+ }
+
+ [Fact]
+ public void Equal_DifferentCompaniesWithPrivateGetters()
+ {
+ var a = new CompanyPrivateGetters
+ {
+ Id = 1,
+ Name = "The Company",
+ };
+ var b = new CompanyPrivateGetters
+ {
+ Id = 1,
+ Name = "A Company",
+ };
+ AssertDeepEqual(a, b);
+ }
+
+ [Fact]
+ public void Equal_EqualPeople_Cycle()
+ {
+ var a = CreateSamplePerson_OwnFatherAndMotherSomehow();
+ var b = CreateSamplePerson_OwnFatherAndMotherSomehow();
+ AssertDeepEqual(a, b);
+ }
+
+ [Fact]
+ public void NotEqual_DifferentPeople_PartialCycle()
+ {
+ var a = CreateSamplePerson_OwnFatherAndMotherSomehow();
+ var b = CreateSamplePerson_OwnFatherAndMotherSomehow();
+ b.Mother = a;
+ AssertNotDeepEqual(a, b);
+ }
+
+ [Fact]
+ public void Equal_EqualPeople_OwnGrandpa()
+ {
+ var a = CreateSamplePerson_OwnGrandpa();
+ var b = CreateSamplePerson_OwnGrandpa();
+ AssertDeepEqual(a, b);
+ }
+
+ [Fact]
+ public void NotEqual_DifferentPeople_OwnGrandpa()
+ {
+ var a = CreateSamplePerson_OwnGrandpa();
+ var b = CreateSamplePerson_OwnGrandpa();
+ b.Father!.FullName = "Bob";
+
+ AssertNotDeepEqual(a, b);
+ }
+
+ [Fact]
+ public void Equal_EmptyListTestObjectsWithSameType()
+ {
+ var obj1 = new ListTestClass();
+ var obj2 = new ListTestClass();
+ AssertDeepEqual(obj1, obj2);
+ }
+
+ [Fact]
+ public void Equal_ListTestObjectSamples()
+ {
+ var obj1 = new ListTestClass()
+ {
+ Enumerable = new[] { "test" },
+ ReadOnlyCollection = new[] { "test2", "test22" },
+ Collection = new[] { "test3" },
+ Array = new[] { "test4" },
+ IList = new[] { "test5" },
+ List = new List { "test6" },
+ };
+ var obj2 = new ListTestClass()
+ {
+ Enumerable = new[] { "test" },
+ ReadOnlyCollection = new[] { "test2", "test22" },
+ Collection = new[] { "test3" },
+ Array = new[] { "test4" },
+ IList = new[] { "test5" },
+ List = new List { "test6" },
+ };
+ AssertDeepEqual(obj1, obj2);
+ }
+
+ [Fact]
+ public void Equal_ListTestObjectsSamples_DifferentListTypes()
+ {
+ var obj1 = new ListTestClass()
+ {
+ Enumerable = new[] { "test" },
+ ReadOnlyCollection = new[] { "test2", "test22" },
+ Collection = new[] { "test3" },
+ Array = new[] { "test4" },
+ IList = new[] { "test5" },
+ List = new List { "test6" },
+ };
+ var obj2 = new ListTestClass()
+ {
+ Enumerable = new List { "test" },
+ ReadOnlyCollection = new List { "test2", "test22" },
+ Collection = new List { "test3" },
+ Array = new[] { "test4" },
+ IList = new List { "test5" },
+ List = new List { "test6" },
+ };
+ AssertDeepEqual(obj1, obj2);
+ }
+
+ [Fact]
+ public void NotEqual_DifferentListTestObjectsSamples()
+ {
+ var obj1 = new ListTestClass()
+ {
+ Enumerable = new[] { "test" },
+ ReadOnlyCollection = new[] { "test2", "test22" },
+ Collection = new[] { "test3" },
+ Array = new[] { "test4" },
+ IList = new[] { "test5" },
+ List = new List { "test6" },
+ };
+ var obj2 = new ListTestClass()
+ {
+ Enumerable = new[] { "TEST" },
+ ReadOnlyCollection = new[] { "TEST2", "TEST22" },
+ Collection = new[] { "TEST3" },
+ Array = new[] { "TEST4" },
+ IList = new[] { "TEST5" },
+ List = new List { "TEST6" },
+ };
+ AssertNotDeepEqual(obj1, obj2);
+ }
+
+ [Fact]
+ public void Equal_EmptyListTestObjectsWithDifferentTypes()
+ {
+ var obj1 = new ListTestClass();
+ var obj2 = new ListTestClass();
+ AssertDeepEqual(obj1, obj2);
+ }
+
+ [Fact]
+ public void Equal_EmptyListTestObjectsWithDifferentCompatibleTypes()
+ {
+ var obj1 = new ListTestClass();
+ var obj2 = new ListTestClass();
+ AssertDeepEqual(obj1, obj2);
+ }
+
+ [Fact]
+ public void Equal_EmptyDictionaryTestObjectsWithSameType()
+ {
+ var obj1 = new DictionaryTestClass();
+ var obj2 = new DictionaryTestClass();
+ AssertDeepEqual(obj1, obj2);
+ }
+
+ [Fact]
+ public void Equal_EmptyDictionariesWithSameType()
+ {
+ var obj1 = new DictionaryTestClass
+ {
+ Dictionary = new Dictionary(),
+ DictionaryOfEnumerables = new Dictionary>(),
+ DictionaryOfCollections = new Dictionary>(),
+ };
+ var obj2 = new DictionaryTestClass()
+ {
+ Dictionary = new Dictionary(),
+ DictionaryOfEnumerables = new Dictionary>(),
+ DictionaryOfCollections = new Dictionary>(),
+ };
+ AssertDeepEqual(obj1, obj2);
+ }
+
+ [Fact]
+ public void Equal_DictionariesWithSameValuesButDifferentListTypes_()
+ {
+ var person = new Person { FullName = "Robert Paulson" };
+ var obj1 = new DictionaryTestClass
+ {
+ Dictionary = new Dictionary() { [1] = person },
+ DictionaryOfEnumerables = new Dictionary>() { [1] = new Person[] { person } },
+ DictionaryOfCollections = new Dictionary>() { [1] = new Person[] { person } },
+ };
+ var obj2 = new DictionaryTestClass()
+ {
+ Dictionary = new Dictionary() { [1] = person },
+ DictionaryOfEnumerables = new Dictionary>() { [1] = new List { person } },
+ DictionaryOfCollections = new Dictionary>() { [1] = new List { person } },
+ };
+ AssertDeepEqual(obj1, obj2);
+ }
+
+ [Fact]
+ public void Equal_DictionaryTestObjectsWithEqualValuesButDifferentListTypes()
+ {
+ var person1 = new Person { FullName = "Robert Paulson" };
+ var person2 = new Person { FullName = "Robert Paulson" };
+
+ var obj1 = new DictionaryTestClass
+ {
+ Dictionary = new Dictionary() { [1] = person1 },
+ DictionaryOfEnumerables = new Dictionary>() { [1] = new Person[] { person1 } },
+ DictionaryOfCollections = new Dictionary>() { [1] = new Person[] { person1 } },
+ };
+ var obj2 = new DictionaryTestClass()
+ {
+ Dictionary = new Dictionary() { [1] = person2 },
+ DictionaryOfEnumerables = new Dictionary>() { [1] = new List { person2 } },
+ DictionaryOfCollections = new Dictionary>() { [1] = new List { person2 } },
+ };
+
+ AssertDeepEqual(obj1, obj2);
+ }
+
+ [Fact]
+ public void NotEqual_ReflectionPropertiesObjectSample()
+ {
+ var obj1 = new ReflectionValuesTestClass()
+ {
+ Type = typeof(string),
+ PropertyInfo = typeof(string).GetRuntimeProperty(nameof(string.Length)),
+ };
+ var obj2 = new ReflectionValuesTestClass()
+ {
+ Type = typeof(ReflectionValuesTestClass),
+ PropertyInfo = typeof(ReflectionValuesTestClass).GetRuntimeProperty(nameof(ReflectionValuesTestClass.PropertyInfo)),
+ };
+ AssertNotDeepEqual(obj1, obj2);
+ }
+
+ [Fact]
+ public void Equal_EmptyArrays()
+ {
+#pragma warning disable CA1825 // Avoid zero-length array allocations.
+ var obj1 = new string[0];
+ var obj2 = new string[0];
+#pragma warning restore CA1825 // Avoid zero-length array allocations.
+ AssertDeepEqual(obj1, obj2);
+ }
+
+ [Fact]
+ public void Equal_PrimitiveValuesObjectSample()
+ {
+ var obj1 = PrimitiveValuesTestClass.CreateSample();
+ var obj2 = PrimitiveValuesTestClass.CreateSample();
+ AssertDeepEqual(obj1, obj2);
+ }
+
+ [Fact]
+ public void Equal_Arrays_IgnoreOptions()
+ {
+ var expected = new[] { 1, 2, 3 };
+ var actual = new[] { 3, 2, 1 };
+ AssertNotDeepEqual(expected, actual);
+ AssertDeepEqual(expected, actual, new JsonDeepEqualDiffOptions { IgnoreArrayElementOrder = true });
+ }
+
+ [Fact]
+ public void Equal_Strings_IgnoreOptions()
+ {
+ var expected = "Hell o\nWorld";
+ var actual = "hell o\r\nworld";
+ AssertNotDeepEqual(expected, actual);
+ AssertDeepEqual(expected, actual, new JsonDeepEqualDiffOptions { IgnoreCase = true, IgnoreWhiteSpaceDifferences = true, IgnoreLineEndingDifferences = true });
+ }
+
+ #region Helpers
+
+ private void AssertDeepEqual(object? expected, object? actual)
+ {
+ JsonDeepEqualAssert.Equal(expected, actual);
+ JsonDeepEqualAssert.AreEqual(expected, actual);
+ Assert.Throws(() => JsonDeepEqualAssert.NotEqual(expected, actual));
+ Assert.Throws(() => JsonDeepEqualAssert.AreNotEqual(expected, actual));
+ AssertDeepEqual(expected, actual, null);
+ }
+
+ private static void AssertDeepEqual(object? expected, object? actual, JsonDeepEqualDiffOptions? options)
+ {
+ JsonDeepEqualAssert.Equal(expected, actual, options);
+ JsonDeepEqualAssert.AreEqual(expected, actual, options);
+ Assert.Throws(() => JsonDeepEqualAssert.NotEqual(expected, actual, options));
+ Assert.Throws(() => JsonDeepEqualAssert.AreNotEqual(expected, actual, options));
+ }
+
+ private void AssertNotDeepEqual(object? expected, object? actual)
+ {
+ JsonDeepEqualAssert.NotEqual(expected, actual);
+ JsonDeepEqualAssert.AreNotEqual(expected, actual);
+ Assert.Throws(() => JsonDeepEqualAssert.Equal(expected, actual));
+ Assert.Throws(() => JsonDeepEqualAssert.AreEqual(expected, actual));
+ AssertNotDeepEqual(expected, actual, null);
+ }
+
+ private static void AssertNotDeepEqual(object? expected, object? actual, JsonDeepEqualDiffOptions? options)
+ {
+ JsonDeepEqualAssert.NotEqual(expected, actual, options);
+ JsonDeepEqualAssert.AreNotEqual(expected, actual, options);
+ Assert.Throws(() => JsonDeepEqualAssert.Equal(expected, actual, options));
+ Assert.Throws(() => JsonDeepEqualAssert.AreEqual(expected, actual, options));
+ }
+
+ #endregion
+
+ #region Samples
+
+ private Address CreateSampleAddress()
+ {
+ var address = new Address
+ {
+ Id = 1,
+ Lines = new[] { "123 Fake ST", "Arlington, VA 22222" },
+ };
+ return address;
+ }
+
+ private Company CreateSampleCompany()
+ {
+ var company = new Company
+ {
+ Id = 1,
+ Name = "The Company",
+ Employees = new[]
+ {
+ new Employee
+ {
+ Id = 2,
+ FullName = "Robert Paulson",
+ Addresses = new[]
+ {
+ new Address { Id = 3, AddressType = AddressType.Home, Lines = new[] { "123 Fake ST", "Arlington, VA 22222" } },
+ new Address { Id = 4, AddressType = AddressType.Work, Lines = new[] { "2 Company BLVD", "Arlington, VA 22222" } },
+ },
+ Phones = new[]
+ {
+ new Phone { Id = 5, PhoneType = PhoneType.Cell, Number = "555-555-5555", },
+ },
+ },
+ new Employee
+ {
+ Id = 6,
+ FullName = "Jenny Heath",
+ Phones = new[]
+ {
+ new Phone { Id = 7, PhoneType = PhoneType.Home, Number = "555-867-5309", },
+ },
+ },
+ },
+ };
+ return company;
+ }
+
+ private Person CreateSamplePerson_OwnFatherAndMotherSomehow()
+ {
+ var person = new Person { FullName = "a" };
+ person.Father = person;
+ person.Mother = person;
+ person.Children = new[] { person };
+ return person;
+ }
+
+ private Person CreateSamplePerson_OwnGrandpa()
+ {
+ var protagonistFather = new Person { FullName = "Protagonist's Father" };
+ var protagonistMother = new Person { FullName = "Protagonist's Mother" };
+ var protagonist = new Person { FullName = "Protagonist", Father = protagonistFather, Mother = protagonistMother };
+
+ var widow = new Person { FullName = "Widow" };
+ var deadMan = new Person { FullName = "Dead Man " };
+ var widowDaughter = new Person { FullName = "Widow's Daughter", Father = deadMan, Mother = widow };
+
+ var protagonistBaby = new Person { FullName = "Protagonist's Baby", Father = protagonist, Mother = widow };
+ var protagonistStepBrotherAndStepGrandchild = new Person { FullName = "Grandchild", Father = protagonistFather, Mother = widowDaughter };
+
+ protagonist.Spouses = new[] { widow };
+ widow.Spouses = new[] { deadMan, protagonist };
+ deadMan.Spouses = new[] { widow };
+ protagonistMother.Spouses = new[] { protagonistFather };
+ protagonistFather.Spouses = new[] { protagonistMother, widowDaughter };
+ widowDaughter.Spouses = new[] { protagonistFather };
+
+ protagonist.Children = new[] { protagonistBaby };
+ protagonistFather.Children = new[] { protagonist, protagonistStepBrotherAndStepGrandchild };
+ protagonistMother.Children = new[] { protagonist };
+ widow.Children = new[] { widowDaughter, protagonistBaby };
+ widowDaughter.Children = new[] { protagonistStepBrotherAndStepGrandchild };
+
+ return protagonist;
+ }
+
+ #endregion
+ }
+}
diff --git a/JsonDeepEqual.Tests/JsonDeepEqualDiffTest.cs b/JsonDeepEqual.Tests/JsonDeepEqualDiffTest.cs
new file mode 100644
index 0000000..f93e31e
--- /dev/null
+++ b/JsonDeepEqual.Tests/JsonDeepEqualDiffTest.cs
@@ -0,0 +1,638 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using System;
+using System.Linq;
+using Xunit;
+
+namespace Two.JsonDeepEqual
+{
+ public class JsonDeepEqualDiffTest
+ {
+ [Theory]
+ [InlineData(2, 2, 0)]
+ [InlineData(1, 2, 1)]
+ [InlineData(2.0f, 2.0f, 0)]
+ [InlineData(1.0f, 2.0f, 1)]
+ [InlineData("hello", "hello", 0)]
+ [InlineData("hello", "world", 1)]
+ public void EnumerateDifferences_SimpleValue(object a, object b, int expectedDifferenceCount)
+ {
+ var differences = JsonDeepEqualDiff.EnumerateDifferences(a, b).ToList();
+ Assert.Equal(expectedDifferenceCount, differences.Count);
+ }
+
+ [Fact]
+ public void EnumerateDifferences_PrimitiveArrays_ShouldBeEqual()
+ {
+ var a = new int[] { 1, 2, 3 };
+ var b = new int[] { 1, 2, 3 };
+ var differences = JsonDeepEqualDiff.EnumerateDifferences(a, b).ToList();
+ Assert.Empty(differences);
+ }
+
+ [Fact]
+ public void EnumerateDifferences_BinaryArrays_ShouldBeEqual()
+ {
+ var a = new byte[] { 1, 2, 3 };
+ var b = new byte[] { 1, 2, 3 };
+ var differences = JsonDeepEqualDiff.EnumerateDifferences(a, b).ToList();
+ Assert.Empty(differences);
+ }
+
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public void EnumerateDifferences_PrimitiveArrays_SameLengtWithNoOverlap_ShouldHaveDifferences(bool ignoreOrder)
+ {
+ var a = new int[] { 1, 2, 3 };
+ var b = new int[] { 4, 5, 6 };
+ var differences = JsonDeepEqualDiff.EnumerateDifferences(a, b, new JsonDeepEqualDiffOptions { IgnoreArrayElementOrder = ignoreOrder }).ToList();
+ if (!ignoreOrder)
+ {
+ Assert.Equal(3, differences.Count);
+
+ Assert.Equal("/0", differences[0].Path);
+ Assert.Equal(1, differences[0].ExpectedValue.ToObject