[
  {
    "path": ".gitignore",
    "content": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n\n# User-specific files\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# User-specific files (MonoDevelop/Xamarin Studio)\n*.userprefs\n\n# Build results\n[Dd]ebug/\n[Dd]ebugPublic/\n[Rr]elease/\n[Rr]eleases/\nx64/\nx86/\nbld/\n[Bb]in/\n[Oo]bj/\n[Ll]og/\n\n# Visual Studio 2015 cache/options directory\n.vs/\n# Uncomment if you have tasks that create the project's static files in wwwroot\n#wwwroot/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n# NUNIT\n*.VisualState.xml\nTestResult.xml\n\n# Build Results of an ATL Project\n[Dd]ebugPS/\n[Rr]eleasePS/\ndlldata.c\n\n# DNX\nproject.lock.json\nartifacts/\n\n*_i.c\n*_p.c\n*_i.h\n*.ilk\n*.meta\n*.obj\n*.pch\n*.pdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.svclog\n*.scc\n\n# Chutzpah Test files\n_Chutzpah*\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opendb\n*.opensdf\n*.sdf\n*.cachefile\n*.VC.db\n*.VC.VC.opendb\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n*.sap\n\n# TFS 2012 Local Workspace\n$tf/\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n*.DotSettings.user\n\n# JustCode is a .NET coding add-in\n.JustCode\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# NCrunch\n_NCrunch_*\n.*crunch*.local.xml\nnCrunchTemp_*\n\n# MightyMoose\n*.mm.*\nAutoTest.Net/\n\n# Web workbench (sass)\n.sass-cache/\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.[Pp]ublish.xml\n*.azurePubxml\n# TODO: Comment the next line if you want to checkin your web deploy settings\n# but database connection strings (with potential passwords) will be unencrypted\n*.pubxml\n*.publishproj\n\n# Microsoft Azure Web App publish settings. Comment the next line if you want to\n# checkin your Azure Web App publish settings, but sensitive information contained\n# in these scripts will be unencrypted\nPublishScripts/\n\n# NuGet Packages\n*.nupkg\n# The packages folder can be ignored because of Package Restore\n**/packages/*\n# except build/, which is used as an MSBuild target.\n!**/packages/build/\n# Uncomment if necessary however generally it will be regenerated when needed\n#!**/packages/repositories.config\n# NuGet v3's project.json files produces more ignoreable files\n*.nuget.props\n*.nuget.targets\n\n# Microsoft Azure Build Output\ncsx/\n*.build.csdef\n\n# Microsoft Azure Emulator\necf/\nrcf/\n\n# Windows Store app package directories and files\nAppPackages/\nBundleArtifacts/\nPackage.StoreAssociation.xml\n_pkginfo.txt\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!*.[Cc]ache/\n\n# Others\nClientBin/\n~$*\n*~\n*.dbmdl\n*.dbproj.schemaview\n*.pfx\n*.publishsettings\nnode_modules/\norleans.codegen.cs\n\n# Since there are multiple workflows, uncomment next line to ignore bower_components\n# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)\n#bower_components/\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file\n# to a newer Visual Studio version. Backup files are not needed,\n# because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\n\n# SQL Server files\n*.mdf\n*.ldf\n\n# Business Intelligence projects\n*.rdl.data\n*.bim.layout\n*.bim_*.settings\n\n# Microsoft Fakes\nFakesAssemblies/\n\n# GhostDoc plugin setting file\n*.GhostDoc.xml\n\n# Node.js Tools for Visual Studio\n.ntvs_analysis.dat\n\n# Visual Studio 6 build log\n*.plg\n\n# Visual Studio 6 workspace options file\n*.opt\n\n# Visual Studio LightSwitch build output\n**/*.HTMLClient/GeneratedArtifacts\n**/*.DesktopClient/GeneratedArtifacts\n**/*.DesktopClient/ModelManifest.xml\n**/*.Server/GeneratedArtifacts\n**/*.Server/ModelManifest.xml\n_Pvt_Extensions\n\n# Paket dependency manager\n.paket/paket.exe\npaket-files/\n\n# FAKE - F# Make\n.fake/\n\n# JetBrains Rider\n.idea/\n*.sln.iml\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2019 Valerii Tereshchenko\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/AbstractComparer.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace ObjectsComparer\n{\n    /// <summary>\n    /// Implementation of <see cref=\"T:ObjectsComparer.IComparer\" /> which provides implementation of Compare methods.\n    /// </summary>\n    public abstract class AbstractComparer: BaseComparer, IComparer\n    {\n        protected AbstractComparer(ComparisonSettings settings, BaseComparer parentComparer, IComparersFactory factory) \n            : base(settings, parentComparer, factory)\n        {\n        }\n\n        public abstract IEnumerable<Difference> CalculateDifferences(Type type, object obj1, object obj2);\n\n        /// <summary>\n        /// Calculates list of differences between objects.\n        /// </summary>\n        /// <typeparam name=\"T\">Type.</typeparam>\n        /// <param name=\"obj1\">Object 1.</param>\n        /// <param name=\"obj2\">Object 2.</param>\n        /// <returns>List of differences between objects.</returns>\n        public IEnumerable<Difference> CalculateDifferences<T>(T obj1, T obj2)\n        {\n            return CalculateDifferences(typeof(T), obj1, obj2);\n        }\n\n        /// <summary>\n        /// Compares objects.\n        /// </summary>\n        /// <param name=\"type\">Type.</param>\n        /// <param name=\"obj1\">Object 1.</param>\n        /// <param name=\"obj2\">Object 2.</param>\n        /// <param name=\"differences\">List of differences.</param>\n        /// <returns>True if objects are equal, otherwise false.</returns>\n        public bool Compare(Type type, object obj1, object obj2, out IEnumerable<Difference> differences)\n        {\n            differences = CalculateDifferences(type, obj1, obj2);\n\n            return !differences.Any();\n        }\n\n        /// <summary>\n        /// Compares objects.\n        /// </summary>\n        /// <param name=\"type\">Type.</param>\n        /// <param name=\"obj1\">Object 1.</param>\n        /// <param name=\"obj2\">Object 2.</param>\n        /// <returns>True if objects are equal, otherwise false.</returns>\n        public bool Compare(Type type, object obj1, object obj2)\n        {\n            return !CalculateDifferences(type, obj1, obj2).Any();\n        }\n\n        /// <summary>\n        /// Compares objects.\n        /// </summary>\n        /// <typeparam name=\"T\">Type.</typeparam>\n        /// <param name=\"obj1\">Object 1.</param>\n        /// <param name=\"obj2\">Object 2.</param>\n        /// <param name=\"differences\">List of differences.</param>\n        /// <returns>True if objects are equal, otherwise false.</returns>\n        public bool Compare<T>(T obj1, T obj2, out IEnumerable<Difference> differences)\n        {\n            return Compare(typeof(T), obj1, obj2, out differences);\n        }\n\n        /// <summary>\n        /// Compares objects.\n        /// </summary>\n        /// <typeparam name=\"T\">Type.</typeparam>\n        /// <param name=\"obj1\">Object 1.</param>\n        /// <param name=\"obj2\">Object 2.</param>\n        /// <returns>True if objects are equal, otherwise false.</returns>\n        public bool Compare<T>(T obj1, T obj2)\n        {\n            return Compare(typeof(T), obj1, obj2);\n        }\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/AbstractComparer~1.cs",
    "content": "using System.Collections.Generic;\nusing System.Linq;\n\nnamespace ObjectsComparer\n{\n    /// <summary>\n    /// Implementation of <see cref=\"IComparer{T}\"/> which provides implementation of Compare methods.\n    /// </summary>\n    public abstract class AbstractComparer<T>: BaseComparer, IComparer<T>\n    {\n        protected AbstractComparer(ComparisonSettings settings, BaseComparer parentComparer, IComparersFactory factory)\n            :base(settings, parentComparer, factory)\n        {\n        }\n\n        /// <summary>\n        /// Compares objects.\n        /// </summary>\n        /// <param name=\"obj1\">Object 1.</param>\n        /// <param name=\"obj2\">Object 2.</param>\n        /// <param name=\"differences\">List of differences.</param>\n        /// <returns>True if objects are equal, otherwise false.</returns>\n        public bool Compare(T obj1, T obj2, out IEnumerable<Difference> differences)\n        {\n            differences = CalculateDifferences(obj1, obj2);\n\n            return !differences.Any();\n        }\n\n        /// <summary>\n        /// Compares objects.\n        /// </summary>\n        /// <param name=\"obj1\">Object 1.</param>\n        /// <param name=\"obj2\">Object 2.</param>\n        /// <returns>True if objects are equal, otherwise false.</returns>\n        public bool Compare(T obj1, T obj2)\n        {\n            return !CalculateDifferences(obj1, obj2).Any();\n        }\n\n        /// <summary>\n        /// Calculates list of differences between objects.\n        /// </summary>\n        /// <param name=\"obj1\">Object 1.</param>\n        /// <param name=\"obj2\">Object 2.</param>\n        /// <returns>List of differences between objects.</returns>\n        public abstract IEnumerable<Difference> CalculateDifferences(T obj1, T obj2);\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/Attributes/IgnoreInComparisonAttribute.cs",
    "content": "﻿// ***********************************************************************\n// Assembly         : Objects Comparer\n// Author           : Ankur Kumar Gupta\n// Created          : 24-Feb-2022\n// ***********************************************************************\n\nnamespace ObjectsComparer.Attributes\n{\n  using System;\n\n  /// <summary>\n  /// Class is used to specify whether the element on which it is applied will have comparison effect\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]\n  public sealed class IgnoreInComparisonAttribute : Attribute\n  {\n\n  }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/BaseComparer.cs",
    "content": "using System;\nusing System.Linq.Expressions;\nusing System.Reflection;\nusing ObjectsComparer.Utils;\n\nnamespace ObjectsComparer\n{\n    /// <summary>\n    /// Provides base implementation to configure comparer.\n    /// </summary>\n    public abstract class BaseComparer: IBaseComparer\n    {\n        /// <summary>\n        /// Comparison Settings.\n        /// </summary>\n        public ComparisonSettings Settings { get; }\n\n        /// <summary>\n        /// Default <see cref=\"IValueComparer\"/>\n        /// </summary>\n        public IValueComparer DefaultValueComparer { get; private set; }\n\n        protected IComparersFactory Factory { get; }\n\n        internal ComparerOverridesCollection OverridesCollection { get;  } = new ComparerOverridesCollection();\n        \n        protected BaseComparer(ComparisonSettings settings, BaseComparer parentComparer, IComparersFactory factory)\n        {\n            Factory = factory ?? new ComparersFactory();\n            Settings = settings ?? new ComparisonSettings();\n            DefaultValueComparer = new DefaultValueComparer();\n            // ReSharper disable once InvertIf\n            if (parentComparer != null)\n            {\n                DefaultValueComparer = parentComparer.DefaultValueComparer;\n                OverridesCollection.Merge(parentComparer.OverridesCollection);\n            }\n        }\n\n        /// <summary>\n        /// Adds Comparer Override by Type.\n        /// </summary>\n        /// <param name=\"type\">Type.</param>\n        /// <param name=\"valueComparer\">Value Comparer.</param>\n        /// <param name=\"filter\">Value Comparer will be used only if filter(memberInfo) == true. Null by default.</param>\n        public void AddComparerOverride(Type type, IValueComparer valueComparer, Func<MemberInfo, bool> filter = null)\n        {\n            OverridesCollection.AddComparer(type, valueComparer, filter);\n        }\n\n        /// <summary>\n        /// Adds Comparer Override by member filter.\n        /// </summary>\n        /// <param name=\"valueComparer\">Value Comparer.</param>\n        /// <param name=\"filter\">Value Comparer will be used only if filter(memberInfo) == true.</param>\n        public void AddComparerOverride(IValueComparer valueComparer, Func<MemberInfo, bool> filter)\n        {\n            OverridesCollection.AddComparer(valueComparer, filter);\n        }\n\n        /// <summary>\n        /// Adds Comparer Override by Type.\n        /// </summary>\n        /// <typeparam name=\"TType\">Type.</typeparam>\n        /// <param name=\"valueComparer\">Value Comparer.</param>\n        /// <param name=\"filter\">Value Comparer will be used only if filter(memberInfo) == true. Null by default.</param>\n        public void AddComparerOverride<TType>(IValueComparer valueComparer, Func<MemberInfo, bool> filter = null)\n        {\n            AddComparerOverride(typeof(TType), valueComparer, filter);\n        }\n\n        /// <summary>\n        /// Adds Comparer Override by Member.\n        /// </summary>\n        /// <typeparam name=\"TProp\">Type of the member.</typeparam>\n        /// <param name=\"memberLambda\">Lambda to get member.</param>\n        /// <param name=\"valueComparer\">Value Comparer.</param>\n        public void AddComparerOverride<TProp>(Expression<Func<TProp>> memberLambda, IValueComparer valueComparer)\n        {\n            OverridesCollection.AddComparer(PropertyHelper.GetMemberInfo(memberLambda), valueComparer);\n        }\n\n        /// <summary>\n        /// Adds Comparer Override by Member.\n        /// </summary>\n        /// <typeparam name=\"TProp\">Type of the member.</typeparam>\n        /// <param name=\"memberLambda\">Lambda to get member.</param>\n        /// <param name=\"compareFunction\">Function to compare objects.</param>\n        /// <param name=\"toStringFunction\">Function to convert objects to string.</param>\n        public void AddComparerOverride<TProp>(\n            Expression<Func<TProp>> memberLambda, \n            Func<TProp, TProp, ComparisonSettings, bool> compareFunction, \n            Func<TProp, string> toStringFunction)\n        {\n            OverridesCollection.AddComparer(\n                PropertyHelper.GetMemberInfo(memberLambda),\n                new DynamicValueComparer<TProp>(\n                    compareFunction,\n                    toStringFunction));\n        }\n\n        /// <summary>\n        /// Adds Comparer Override by Member.\n        /// </summary>\n        /// <typeparam name=\"TProp\">Type of the member.</typeparam>\n        /// <param name=\"memberLambda\">Lambda to get member.</param>\n        /// <param name=\"compareFunction\">Function to compare objects.</param>\n        public void AddComparerOverride<TProp>(\n            Expression<Func<TProp>> memberLambda,\n            Func<TProp, TProp, ComparisonSettings, bool> compareFunction)\n        {\n            OverridesCollection.AddComparer(\n                PropertyHelper.GetMemberInfo(memberLambda),\n                new DynamicValueComparer<TProp>(\n                    compareFunction,\n                    obj => obj?.ToString()));\n        }\n\n        /// <summary>\n        /// Adds Comparer Override by Member.\n        /// </summary>\n        /// <param name=\"memberInfo\">Member Info.</param>\n        /// <param name=\"valueComparer\">Value Comparer.</param>\n        public void AddComparerOverride(MemberInfo memberInfo, IValueComparer valueComparer)\n        {\n            OverridesCollection.AddComparer(memberInfo, valueComparer);\n        }\n\n        /// <summary>\n        /// Adds Comparer Override by Member name.\n        /// </summary>\n        /// <param name=\"memberName\">Member Name.</param>\n        /// <param name=\"valueComparer\">Value Comparer.</param>\n        /// <param name=\"filter\">Value Comparer will be used only if filter(memberInfo) == true. Null by default.</param>\n        public void AddComparerOverride(string memberName, IValueComparer valueComparer, Func<MemberInfo, bool> filter = null)\n        {\n            OverridesCollection.AddComparer(memberName, valueComparer, filter);\n        }\n\n        /// <summary>\n        /// Sets <see cref=\"IBaseComparer.DefaultValueComparer\"/>.\n        /// </summary>\n        /// <param name=\"valueComparer\">Value Comparer.</param>\n        public void SetDefaultComparer(IValueComparer valueComparer)\n        {\n            DefaultValueComparer = valueComparer ?? throw new ArgumentNullException(nameof(valueComparer));\n        }\n\n        /// <summary>\n        /// Ignors comparison for Type.\n        /// </summary>\n        /// <typeparam name=\"TType\">Type.</typeparam>\n        public void IgnoreMember<TType>()\n        {\n            OverridesCollection.AddComparer(typeof(TType), DoNotCompareValueComparer.Instance);\n        }\n\n        /// <summary>\n        /// Ignors comparison for Member.\n        /// </summary>\n        /// <typeparam name=\"TProp\">Type of the member.</typeparam>\n        public void IgnoreMember<TProp>(Expression<Func<TProp>> memberLambda)\n        {\n            OverridesCollection.AddComparer(\n                PropertyHelper.GetMemberInfo(memberLambda),\n                DoNotCompareValueComparer.Instance);\n        }\n\n        /// <summary>\n        /// Ignors comparison for Member by Member name.\n        /// </summary>\n        /// <param name=\"memberName\">Member Name.</param>\n        public void IgnoreMember(string memberName)\n        {\n            OverridesCollection.AddComparer(memberName, DoNotCompareValueComparer.Instance);\n        }\n\n        /// <summary>\n        /// Ignors comparison by Member filter.\n        /// </summary>\n        /// <param name=\"filter\">Member will be ignored if filter(memberInfo) == true.</param>\n        public void IgnoreMember(Func<MemberInfo, bool> filter)\n        {\n            OverridesCollection.AddComparer(DoNotCompareValueComparer.Instance, filter);\n        }\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/Comparer.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Reflection;\nusing ObjectsComparer.Utils;\n\nnamespace ObjectsComparer\n{\n    /// <summary>\n    /// Compares objects.\n    /// </summary>\n    public class Comparer : AbstractComparer\n    {\n        private static string CalculateDifferencesMethodName\n        {\n            // ReSharper disable once IteratorMethodResultIsIgnored\n            get { return MemberInfoExtensions.GetMethodName<Comparer<object>>(x => x.CalculateDifferences(null, null)); }\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"Comparer\" /> class. \n        /// </summary>\n        /// <param name=\"settings\">Comparison Settings.</param>\n        /// <param name=\"parentComparer\">Parent Comparer. Is used to copy DefaultValueComparer and Overrides. Null by default.</param>\n        /// <param name=\"factory\">Factory to create comparers in case of some members of the objects will need it.</param>\n        public Comparer(ComparisonSettings settings = null, BaseComparer parentComparer = null, IComparersFactory factory = null) : base(settings, parentComparer, factory)\n        {\n        }\n\n        /// <summary>\n        /// Calculates list of differences between objects.\n        /// </summary>\n        /// <param name=\"type\">Type.</param>\n        /// <param name=\"obj1\">Object 1.</param>\n        /// <param name=\"obj2\">Object 2.</param>\n        /// <returns>List of differences between objects.</returns>\n        public override IEnumerable<Difference> CalculateDifferences(Type type, object obj1, object obj2)\n        {\n            var objectsComparerMethod = typeof(IComparersFactory).GetTypeInfo().GetMethods().First(m => m.IsGenericMethod);\n            var objectsComparerGenericMethod = objectsComparerMethod.MakeGenericMethod(type);\n            var comparer = objectsComparerGenericMethod.Invoke(Factory, new object[] { Settings, this });\n            var genericType = typeof(IComparer<>).MakeGenericType(type);\n            var method = genericType.GetTypeInfo().GetMethod(CalculateDifferencesMethodName, new[] { type, type });\n\n            // ReSharper disable once PossibleNullReferenceException\n            return (IEnumerable<Difference>)method.Invoke(comparer, new[] { obj1, obj2 });\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/ComparerOverridesCollection.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Reflection;\nusing ObjectsComparer.Exceptions;\nusing ObjectsComparer.Utils;\n\nnamespace ObjectsComparer\n{\n    internal class ComparerOverridesCollection\n    {\n        private class ValueComparerWithFilter\n        {\n            public IValueComparer ValueComparer { get; }\n\n            public Func<MemberInfo, bool> Filter { get; }\n\n            public ValueComparerWithFilter(IValueComparer valueComparer, Func<MemberInfo, bool> filter)\n            {\n                Filter = filter;\n                ValueComparer = valueComparer;\n            }\n        }\n\n        private readonly List<Tuple<Func<MemberInfo, bool>, IValueComparer>> _overridesByMemberFilter = new List<Tuple<Func<MemberInfo, bool>, IValueComparer>>();\n        private readonly Dictionary<MemberInfo, IValueComparer> _overridesByMember = new Dictionary<MemberInfo, IValueComparer>();\n        private readonly Dictionary<Type, List<ValueComparerWithFilter>> _overridesByType = new Dictionary<Type, List<ValueComparerWithFilter>>();\n        private readonly Dictionary<string, List<ValueComparerWithFilter>> _overridesByName = new Dictionary<string, List<ValueComparerWithFilter>>();\n\n        public void AddComparer(MemberInfo memberInfo, IValueComparer valueComparer)\n        {\n            if (memberInfo == null)\n            {\n                throw new ArgumentNullException(nameof(memberInfo));\n            }\n\n            if (_overridesByMember.ContainsKey(memberInfo))\n            {\n                throw new ValueComparerExistsException(memberInfo);\n            }\n\n            _overridesByMember[memberInfo] = valueComparer ?? throw new ArgumentNullException(nameof(valueComparer));\n        }\n\n        public void AddComparer(IValueComparer valueComparer, Func<MemberInfo, bool> filter)\n        {\n            if (filter == null)\n            {\n                throw new ArgumentNullException(nameof(filter));\n            }\n\n            _overridesByMemberFilter.Add(new Tuple<Func<MemberInfo, bool>, IValueComparer>(filter, valueComparer));\n        }\n\n        public void AddComparer(Type type, IValueComparer valueComparer,\n            Func<MemberInfo, bool> filter = null)\n        {\n            if (type == null)\n            {\n                throw new ArgumentNullException(nameof(type));\n            }\n\n            if (valueComparer == null)\n            {\n                throw new ArgumentNullException(nameof(valueComparer));\n            }\n\n            if (!_overridesByType.ContainsKey(type))\n            {\n                _overridesByType[type] = new List<ValueComparerWithFilter>();\n            }\n\n            if (!_overridesByType[type].Any(comparer => comparer.ValueComparer == valueComparer && comparer.Filter == filter))\n            {\n                _overridesByType[type].Add(new ValueComparerWithFilter(valueComparer, filter));\n            }\n        }\n\n        public void AddComparer(string memberName, IValueComparer valueComparer,\n            Func<MemberInfo, bool> filter = null)\n        {\n            if (string.IsNullOrWhiteSpace(memberName))\n            {\n                throw new ArgumentException($\"{nameof(memberName)} cannot be null or empty\");\n            }\n\n            if (valueComparer == null)\n            {\n                throw new ArgumentNullException(nameof(valueComparer));\n            }\n\n            if (!_overridesByName.ContainsKey(memberName))\n            {\n                _overridesByName[memberName] = new List<ValueComparerWithFilter>();\n            }\n\n            _overridesByName[memberName].Add(new ValueComparerWithFilter(valueComparer, filter));\n        }\n\n        public void Merge(ComparerOverridesCollection collection)\n        {\n            foreach (var overridePair in collection._overridesByMember)\n            {\n                AddComparer(overridePair.Key, overridePair.Value);\n            }\n\n            foreach (var overrideCollection in collection._overridesByType)\n            {\n                foreach (var overridePair in overrideCollection.Value)\n                {\n                    AddComparer(overrideCollection.Key, overridePair.ValueComparer, overridePair.Filter);\n                }\n            }\n\n            foreach (var overrideCollection in collection._overridesByName)\n            {\n                foreach (var overridePair in overrideCollection.Value)\n                {\n                    AddComparer(overrideCollection.Key, overridePair.ValueComparer, overridePair.Filter);\n                }\n            }\n\n            foreach (var memberFilterOverride in collection._overridesByMemberFilter)\n            {\n                AddComparer(memberFilterOverride.Item2, memberFilterOverride.Item1);\n            }\n        }\n\n        public IValueComparer GetComparer(Type type)\n        {\n            if (type == null)\n            {\n                throw new ArgumentNullException(nameof(type));\n            }\n\n            if (!_overridesByType.TryGetValue(type, out var overridesByType))\n            {\n                return null;\n            }\n\n            overridesByType = overridesByType.Where(o => o.Filter == null).ToList();\n            if (overridesByType.Count > 1)\n            {\n                throw new AmbiguousComparerOverrideResolutionException(type);\n            }\n\n            return overridesByType.Count == 1 ? overridesByType[0].ValueComparer : null;\n        }\n\n        public IValueComparer GetComparer(MemberInfo memberInfo)\n        {\n            if (memberInfo == null)\n            {\n                throw new ArgumentNullException(nameof(memberInfo));\n            }\n\n            if (_overridesByMember.TryGetValue(memberInfo, out var overrideByMemberInfo))\n            {\n                return overrideByMemberInfo;\n            }\n\n            var memberFilterOverride = _overridesByMemberFilter.Find(t => t.Item1(memberInfo))?.Item2;\n            if (memberFilterOverride != null)\n            {\n                return memberFilterOverride;\n            }\n\n            if (_overridesByName.TryGetValue(memberInfo.Name, out var overridesByName))\n            {\n                overridesByName = overridesByName.Where(o => o.Filter == null || o.Filter(memberInfo)).ToList();\n\n                if (overridesByName.Count > 1)\n                {\n                    throw new AmbiguousComparerOverrideResolutionException(memberInfo);\n                }\n\n                if (overridesByName.Count == 1)\n                {\n                    return overridesByName[0].ValueComparer;\n                }\n            }\n\n            if (_overridesByType.TryGetValue(memberInfo.GetMemberType(), out var overridesByType))\n            {\n                overridesByType = overridesByType.Where(o => o.Filter == null || o.Filter(memberInfo)).ToList();\n\n                if (overridesByType.Count > 1)\n                {\n                    throw new AmbiguousComparerOverrideResolutionException(memberInfo);\n                }\n\n                if (overridesByType.Count == 1)\n                {\n                    return overridesByType[0].ValueComparer;\n                }\n            }\n\n            return null;\n        }\n\n        public IValueComparer GetComparer(string memberName)\n        {\n            if (string.IsNullOrWhiteSpace(memberName))\n            {\n                throw new ArgumentNullException(nameof(memberName));\n            }\n\n            if (!_overridesByName.TryGetValue(memberName, out var overridesByName))\n            {\n                return null;\n            }\n\n            overridesByName = overridesByName.Where(o => o.Filter == null).ToList();\n            if (overridesByName.Count > 1)\n            {\n                throw new AmbiguousComparerOverrideResolutionException(memberName);\n            }\n\n            return overridesByName.Count == 1 ? overridesByName[0].ValueComparer : null;\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/ComparersFactory.cs",
    "content": "﻿using System;\n\nnamespace ObjectsComparer\n{\n    /// <summary>\n    /// Implements Comparers Factory.\n    /// </summary>\n    public class ComparersFactory : IComparersFactory\n    {\n        /// <summary>\n        /// Creates type specific comparer.\n        /// </summary>\n        /// <typeparam name=\"T\">Type.</typeparam>\n        /// <param name=\"settings\">Comparison Settings. Null by default.</param>\n        /// <param name=\"parentComparer\">Parent comparer. Null by default.</param>\n        /// <returns>Instance of <see cref=\"IComparer{T}\"/>.</returns>\n        public virtual IComparer<T> GetObjectsComparer<T>(ComparisonSettings settings = null, BaseComparer parentComparer = null)\n        {\n            return new Comparer<T>(settings, parentComparer, this);\n        }\n\n        /// <summary>\n        /// Creates type specific comparer.\n        /// </summary>\n        /// <param name=\"type\">Type.</param>\n        /// <param name=\"settings\">Comparison Settings. Null by default.</param>\n        /// <param name=\"parentComparer\">Parent comparer. Null by default.</param>\n        /// <returns>Instance of <see cref=\"IComparer\"/>.</returns>\n        public IComparer GetObjectsComparer(Type type, ComparisonSettings settings = null, BaseComparer parentComparer = null)\n        {\n            return new Comparer(settings, parentComparer, this);\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/Comparer~1.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Reflection;\nusing System.Text;\nusing ObjectsComparer.Utils;\nusing ObjectsComparer.Attributes;\n\nnamespace ObjectsComparer\n{\n    /// <summary>\n    /// Compares objects of type <see cref=\"T\"/>.\n    /// </summary>\n    public class Comparer<T> : AbstractComparer<T>\n    {\n        private readonly List<MemberInfo> _members;\n        private readonly List<IComparerWithCondition> _conditionalComparers;\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"Comparer{T}\" /> class. \n        /// </summary>\n        /// <param name=\"settings\">Comparison Settings.</param>\n        /// <param name=\"parentComparer\">Parent Comparer. Is used to copy DefaultValueComparer and Overrides. Null by default.</param>\n        /// <param name=\"factory\">Factory to create comparers in case of some members of the objects will need it.</param>\n        public Comparer(ComparisonSettings settings = null, BaseComparer parentComparer = null, IComparersFactory factory = null)\n            : base(settings, parentComparer, factory)\n        {\n            var properties = GetProperties(typeof(T), new List<Type>());\n            var fields = typeof(T).GetTypeInfo().GetFields().Where(f =>\n                f.IsPublic \n                && !f.IsStatic \n                && !f.GetCustomAttributes(true).Any(c=> c is IgnoreInComparisonAttribute)).ToList();\n            _members = properties.Union(fields.Cast<MemberInfo>()).ToList();\n            _conditionalComparers = new List<IComparerWithCondition>\n            {\n                new MultidimensionalArraysComparer(Settings, this, Factory),\n                new ExpandoObjectComparer(Settings, this, Factory),\n                new DynamicObjectComparer(Settings, this, Factory),\n                new CompilerGeneratedObjectComparer(Settings, this, Factory),\n                new HashSetsComparer(Settings, this, Factory),\n                new GenericEnumerablesComparer(Settings, this, Factory),\n                new EnumerablesComparer(Settings, this, Factory),\n                new TypesComparer(Settings, this, Factory)\n            };\n\n            // Additional value comparers\n            AddComparerOverride<StringBuilder>(new ToStringComparer<StringBuilder>());\n            AddComparerOverride<Uri>(UriComparer.Instance);\n        }\n\n        /// <summary>\n        /// Calculates list of differences between objects.\n        /// </summary>\n        /// <param name=\"obj1\">Object 1.</param>\n        /// <param name=\"obj2\">Object 2.</param>\n        /// <returns>List of differences between objects.</returns>\n        public override IEnumerable<Difference> CalculateDifferences(T obj1, T obj2)\n        {\n            return CalculateDifferences(obj1, obj2, null);\n        }\n\n        internal IEnumerable<Difference> CalculateDifferences(T obj1, T obj2, MemberInfo memberInfo)\n        {\n            var comparer = memberInfo != null\n                ? OverridesCollection.GetComparer(memberInfo)\n                : OverridesCollection.GetComparer(typeof(T));\n\n            if (typeof(T).IsComparable() ||\n                comparer != null)\n            {\n                comparer = comparer ?? DefaultValueComparer;\n                if (!comparer.Compare(obj1, obj2, Settings))\n                {\n                    yield return\n                        new Difference(string.Empty, comparer.ToString(obj1),\n                            comparer.ToString(obj2));\n                }\n\n                yield break;\n            }\n\n            var conditionalComparer = _conditionalComparers.FirstOrDefault(c => c.IsMatch(typeof(T), obj1, obj2));\n            if (conditionalComparer != null)\n            {\n                foreach (var difference in conditionalComparer.CalculateDifferences(typeof(T), obj1, obj2))\n                {\n                    yield return difference;\n                }\n\n                if (conditionalComparer.IsStopComparison(typeof(T), obj1, obj2))\n                {\n                    yield break;\n                }\n            }\n\n            if (obj1 == null || obj2 == null)\n            {\n                if (!DefaultValueComparer.Compare(obj1, obj2, Settings))\n                {\n                    yield return new Difference(string.Empty, DefaultValueComparer.ToString(obj1), DefaultValueComparer.ToString(obj2));\n                }\n\n                yield break;\n            }\n\n            if (!Settings.RecursiveComparison)\n            {\n                yield break;\n            }\n\n            foreach (var member in _members)\n            {\n                if (member.GetCustomAttributes(true).Any(c => c is IgnoreInComparisonAttribute))\n                {\n                  continue;\n                }\n\n                var value1 = member.GetMemberValue(obj1);\n                var value2 = member.GetMemberValue(obj2);\n                var type = member.GetMemberType();\n\n                if (conditionalComparer != null && conditionalComparer.SkipMember(typeof(T), member))\n                {\n                    continue;\n                }\n\n                var valueComparer = DefaultValueComparer;\n                var hasCustomComparer = false;\n\n                var comparerOverride = OverridesCollection.GetComparer(member);\n                if (comparerOverride != null)\n                {\n                    valueComparer = comparerOverride;\n                    hasCustomComparer = true;\n                }\n\n                if (!hasCustomComparer\n                    && !type.IsComparable())\n                {\n                    var objectDataComparer = Factory.GetObjectsComparer(type, Settings, this);\n\n                    foreach (var failure in objectDataComparer.CalculateDifferences(type, value1, value2))\n                    {\n                        yield return failure.InsertPath(member.Name);\n                    }\n\n                    continue;\n                }\n\n                if (!valueComparer.Compare(value1, value2, Settings))\n                {\n                    yield return new Difference(member.Name, valueComparer.ToString(value1), valueComparer.ToString(value2));\n                }\n            }\n        }\n\n        private List<PropertyInfo> GetProperties(Type type, List<Type> processedTypes)\n        {\n            var properties = type.GetTypeInfo().GetProperties().Where(p =>\n                p.CanRead\n                && p.GetGetMethod(true).IsPublic\n                && p.GetGetMethod(true).GetParameters().Length == 0\n                && !p.GetCustomAttributes(true).Any(c=> c is IgnoreInComparisonAttribute)\n                && !p.GetGetMethod(true).IsStatic).ToList();\n            processedTypes.Add(type);\n\n            if (type.GetTypeInfo().IsInterface)\n            {\n                foreach (var parrentInterface in type.GetTypeInfo().GetInterfaces())\n                {\n                    if (processedTypes.Contains(parrentInterface))\n                    {\n                        continue;\n                    }\n\n                    properties = properties\n                        .Union(GetProperties(parrentInterface, processedTypes))\n                        .Distinct()\n                        .ToList();\n                }\n            }\n\n            return properties;\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/ComparisonSettings.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\n\nnamespace ObjectsComparer\n{\n    /// <summary>\n    /// Configuration for Objects Comparer.\n    /// </summary>\n    public class ComparisonSettings\n    {\n        /// <summary>\n        /// If true, all members which are not primitive types, do not have custom comparison rule and \n        /// do not implement <see cref=\"IComparable\"/> will be compared as separate objects using the same rules as current objects. True by default.\n        /// </summary>\n        public bool RecursiveComparison { get; set; }\n\n        /// <summary>\n        /// If true, empty <see cref=\"System.Collections.IEnumerable\"/>  and null values will be considered as equal values. False by default.\n        /// </summary>\n        public bool EmptyAndNullEnumerablesEqual { get; set; }\n\n        /// <summary>\n        /// If true and member does not exists, objects comparer will consider that this member is equal to default value of opposite member type. \n        /// Applicable for dynamic types comparison only. False by default.\n        /// </summary>\n        public bool UseDefaultIfMemberNotExist { get; set; }\n\n        private readonly Dictionary<Tuple<Type, string>, object> _settings = new Dictionary<Tuple<Type, string>, object>();\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"ComparisonSettings\" /> class. \n        /// </summary>\n        public ComparisonSettings()\n        {\n            RecursiveComparison = true;\n            EmptyAndNullEnumerablesEqual = false;\n            UseDefaultIfMemberNotExist = false;\n        }\n\n        /// <summary>\n        /// Sets value of custom setting. Could be used to pass parameters to custom value comparers.\n        /// </summary>\n        /// <typeparam name=\"T\">Setting Type.</typeparam>\n        /// <param name=\"value\">Setting Value.</param>\n        /// <param name=\"key\">Setting Key.</param>\n        public void SetCustomSetting<T>(T value, string key = null)\n        {\n            var dictionaryKey = new Tuple<Type, string>(typeof(T), key);\n            _settings[dictionaryKey] = value;\n        }\n\n        /// <summary>\n        /// Gets value of custom setting. Could be used in custom value comparers.\n        /// </summary>\n        /// <typeparam name=\"T\">Setting Type.</typeparam>\n        /// <param name=\"key\">Setting Key.</param>\n        /// <returns>Setting Value.</returns>\n        public T GetCustomSetting<T>(string key = null)\n        {\n            var dictionaryKey = new Tuple<Type, string>(typeof(T), key);\n\n            if (_settings.TryGetValue(dictionaryKey, out var value))\n            {\n                return (T) value;\n            }\n\n            throw new KeyNotFoundException();\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/CustomComparers/AbstractDynamicObjectsComprer.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Reflection;\nusing ObjectsComparer.Utils;\n\nnamespace ObjectsComparer\n{\n    internal abstract class AbstractDynamicObjectsComprer<T>: AbstractComparer, IComparerWithCondition\n    {\n        protected AbstractDynamicObjectsComprer(ComparisonSettings settings, BaseComparer parentComparer, IComparersFactory factory) : base(settings, parentComparer, factory)\n        {\n        }\n\n        public override IEnumerable<Difference> CalculateDifferences(Type type, object obj1, object obj2)\n        {\n            var castedObject1 = (T)obj1;\n            var castedObject2 = (T)obj2;\n            var propertyKeys1 = GetProperties(castedObject1);\n            var propertyKeys2 = GetProperties(castedObject2);\n\n            var propertyKeys = propertyKeys1.Union(propertyKeys2);\n\n            foreach (var propertyKey in propertyKeys)\n            {\n                var existsInObject1 = propertyKeys1.Contains(propertyKey);\n                var existsInObject2 = propertyKeys2.Contains(propertyKey);\n                object value1 = null;\n                if (existsInObject1)\n                {\n                    TryGetMemberValue(castedObject1, propertyKey, out value1);\n                }\n\n                object value2 = null;\n                if (existsInObject2)\n                {\n                    TryGetMemberValue(castedObject2, propertyKey, out value2);\n                }\n\n                var propertyType = (value1 ?? value2)?.GetType() ?? typeof(object);\n                var customComparer = OverridesCollection.GetComparer(propertyType) ??\n                                     OverridesCollection.GetComparer(propertyKey);\n                var valueComparer = customComparer ?? DefaultValueComparer;\n\n                if (Settings.UseDefaultIfMemberNotExist)\n                {\n                    if (!existsInObject1)\n                    {\n                        value1 = propertyType.GetDefaultValue();\n                    }\n\n                    if (!existsInObject2)\n                    {\n                        value2 = propertyType.GetDefaultValue();\n                    }\n                }\n\n                if (!Settings.UseDefaultIfMemberNotExist)\n                {\n                    if (!existsInObject1)\n                    {\n                        yield return new Difference(propertyKey, string.Empty, valueComparer.ToString(value2),\n                            DifferenceTypes.MissedMemberInFirstObject);\n                        continue;\n                    }\n\n                    if (!existsInObject2)\n                    {\n                        yield return new Difference(propertyKey, valueComparer.ToString(value1), string.Empty,\n                            DifferenceTypes.MissedMemberInSecondObject);\n                        continue;\n                    }\n                }\n\n                if (value1 != null && value2 != null && value1.GetType() != value2.GetType())\n                {\n                    var valueComparer2 = OverridesCollection.GetComparer(value2.GetType()) ??\n                        OverridesCollection.GetComparer(propertyKey) ?? \n                        DefaultValueComparer;\n                    yield return new Difference(propertyKey, valueComparer.ToString(value1), valueComparer2.ToString(value2),\n                        DifferenceTypes.TypeMismatch);\n                    continue;\n                }\n\n                //null cannot be casted to ValueType\n                if (value1 == null && value2 != null && value2.GetType().GetTypeInfo().IsValueType ||\n                    value2 == null && value1 != null && value1.GetType().GetTypeInfo().IsValueType)\n                {\n                    var valueComparer2 = value2 != null ? \n                        OverridesCollection.GetComparer(value2.GetType()) ?? OverridesCollection.GetComparer(propertyKey) ?? DefaultValueComparer :\n                        DefaultValueComparer;\n                    yield return new Difference(propertyKey, valueComparer.ToString(value1), valueComparer2.ToString(value2),\n                        DifferenceTypes.TypeMismatch);\n                    continue;\n                }\n\n                if (customComparer != null)\n                {\n                    if (!customComparer.Compare(value1, value2, Settings))\n                    {\n                        yield return new Difference(propertyKey, customComparer.ToString(value1), customComparer.ToString(value2));\n                    }\n\n                    continue;\n                }\n\n                var comparer = Factory.GetObjectsComparer(propertyType, Settings, this);\n                foreach (var failure in comparer.CalculateDifferences(propertyType, value1, value2))\n                {\n                    yield return failure.InsertPath(propertyKey);\n                }\n            }\n        }\n\n        public abstract bool IsMatch(Type type, object obj1, object obj2);\n\n        public abstract bool IsStopComparison(Type type, object obj1, object obj2);\n\n        public abstract bool SkipMember(Type type, MemberInfo member);\n\n        protected abstract IList<string> GetProperties(T obj);\n        \n        protected abstract bool TryGetMemberValue(T obj, string propertyName, out object value);\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/CustomComparers/AbstractEnumerablesComparer.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.Reflection;\nusing ObjectsComparer.Utils;\n\nnamespace ObjectsComparer\n{\n    internal abstract class AbstractEnumerablesComparer: AbstractComparer, IComparerWithCondition\n    {\n        protected AbstractEnumerablesComparer(ComparisonSettings settings, BaseComparer parentComparer,\n            IComparersFactory factory)\n            : base(settings, parentComparer, factory)\n        {\n        }\n\n        public bool IsStopComparison(Type type, object obj1, object obj2)\n        {\n            return Settings.EmptyAndNullEnumerablesEqual && (obj1 == null || obj2 == null);\n        }\n\n        public virtual bool SkipMember(Type type, MemberInfo member)\n        {\n            if (type.InheritsFrom(typeof(Array)))\n            {\n                if (member.Name == \"LongLength\")\n                {\n                    return true;\n                }\n            }\n\n            if (type.InheritsFrom(typeof(List<>)))\n            {\n                if (member.Name == PropertyHelper.GetMemberInfo(() => new List<string>().Capacity).Name)\n                {\n                    return true;\n                }\n            }\n\n            return false;\n        }\n\n        public abstract override IEnumerable<Difference> CalculateDifferences(Type type, object obj1, object obj2);\n\n        public abstract bool IsMatch(Type type, object obj1, object obj2);\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/CustomComparers/CompilerGeneratedObjectComparer.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Reflection;\nusing System.Runtime.CompilerServices;\n\nnamespace ObjectsComparer\n{\n    internal class CompilerGeneratedObjectComparer : AbstractDynamicObjectsComprer<object>\n    {\n        public CompilerGeneratedObjectComparer(ComparisonSettings settings, BaseComparer parentComparer, IComparersFactory factory)\n            : base(settings, parentComparer, factory)\n        {\n        }\n\n        public override bool IsMatch(Type type, object obj1, object obj2)\n        {\n            return (obj1 != null || obj2 != null) &&\n                   (obj1 == null || obj1.GetType().GetTypeInfo().GetCustomAttribute(typeof(CompilerGeneratedAttribute)) != null) &&\n                   (obj2 == null || obj2.GetType().GetTypeInfo().GetCustomAttribute(typeof(CompilerGeneratedAttribute)) != null);\n        }\n\n        public override bool IsStopComparison(Type type, object obj1, object obj2)\n        {\n            return true;\n        }\n\n        public override bool SkipMember(Type type, MemberInfo member)\n        {\n            return false;\n        }\n\n        protected override IList<string> GetProperties(object obj)\n        {\n            return obj?.GetType().GetTypeInfo().GetMembers()\n                .Where(memberInfo => memberInfo is PropertyInfo)\n                .Select(memberInfo => memberInfo.Name)\n                .Distinct()\n                .ToList() ?? new List<string>();\n        }\n\n        protected override bool TryGetMemberValue(object obj, string propertyName, out object value)\n        {\n            value = null;\n            if (obj == null)\n            {\n                return false;\n            }\n\n            var propertyInfo = obj.GetType().GetTypeInfo().GetProperty(propertyName);\n            if (propertyInfo == null)\n            {\n                return false;\n            }\n\n            value = propertyInfo.GetValue(obj);\n\n            return true;\n\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/CustomComparers/DynamicObjectComparer.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Dynamic;\nusing System.Linq;\nusing System.Reflection;\nusing ObjectsComparer.Utils;\n\nnamespace ObjectsComparer\n{\n    internal class DynamicObjectComparer : AbstractDynamicObjectsComprer<DynamicObject>\n    {\n        private class FakeGetMemberBinder : GetMemberBinder\n        {\n            public FakeGetMemberBinder(string name, bool ignoreCase) : base(name, ignoreCase)\n            {\n            }\n\n            public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion)\n            {\n                throw new NotSupportedException();\n            }\n        }\n\n        public DynamicObjectComparer(ComparisonSettings settings, BaseComparer parentComparer, IComparersFactory factory)\n            : base(settings, parentComparer, factory)\n        {\n        }\n\n        public override bool IsMatch(Type type, object obj1, object obj2)\n        {\n            return type.InheritsFrom(typeof(DynamicObject)) ||\n                   (obj1 != null && obj2 != null && type == typeof(object) && obj1.GetType().InheritsFrom(typeof(DynamicObject)) && obj2.GetType().InheritsFrom(typeof(DynamicObject)));\n        }\n\n        public override bool IsStopComparison(Type type, object obj1, object obj2)\n        {\n            return obj1 == null || obj2 == null;\n        }\n\n        public override bool SkipMember(Type type, MemberInfo member)\n        {\n            return false;\n        }\n\n        protected override IList<string> GetProperties(DynamicObject obj)\n        {\n            return obj?.GetDynamicMemberNames().ToList() ?? new List<string>();\n        }\n\n        protected override bool TryGetMemberValue(DynamicObject obj, string propertyName, out object value)\n        {\n            if (obj == null)\n            {\n                value = null;\n                return false;\n            }\n\n            var getBinder = new FakeGetMemberBinder(propertyName, false);\n\n            return obj.TryGetMember(getBinder, out value);\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/CustomComparers/EnumerablesComparer.cs",
    "content": "﻿using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Reflection;\nusing ObjectsComparer.Utils;\n\nnamespace ObjectsComparer\n{\n    internal class EnumerablesComparer : AbstractComparer, IComparerWithCondition\n    {\n        public EnumerablesComparer(ComparisonSettings settings, BaseComparer parentComparer, IComparersFactory factory)\n            : base(settings, parentComparer, factory)\n        {\n        }\n\n        public override IEnumerable<Difference> CalculateDifferences(Type type, object obj1, object obj2)\n        {\n            if (!Settings.EmptyAndNullEnumerablesEqual &&\n                (obj1 == null || obj2 == null) && obj1 != obj2)\n            {\n                yield return new Difference(\"[]\", obj1?.ToString() ?? string.Empty, obj2?.ToString() ?? string.Empty);\n                yield break;\n            }\n\n            obj1 = obj1 ?? Enumerable.Empty<object>();\n            obj2 = obj2 ?? Enumerable.Empty<object>();\n\n            if (!type.InheritsFrom(typeof(IEnumerable)))\n            {\n                throw new ArgumentException(nameof(type));\n            }\n\n            if (!obj1.GetType().InheritsFrom(typeof(IEnumerable)))\n            {\n                throw new ArgumentException(nameof(obj1));\n            }\n\n            if (!obj2.GetType().InheritsFrom(typeof(IEnumerable)))\n            {\n                throw new ArgumentException(nameof(obj2));\n            }\n\n            var array1 = ((IEnumerable)obj1).Cast<object>().ToArray();\n            var array2 = ((IEnumerable)obj2).Cast<object>().ToArray();\n\n            if (array1.Length != array2.Length)\n            {\n                yield return new Difference(\"\", array1.Length.ToString(), array2.Length.ToString(), \n                    DifferenceTypes.NumberOfElementsMismatch);\n                yield break;\n            }\n\n            //ToDo Extract type\n            for (var i = 0; i < array2.Length; i++)\n            {\n                if (array1[i] == null && array2[i] == null)\n                {\n                    continue;\n                }\n\n                var valueComparer1 = array1[i] != null ? OverridesCollection.GetComparer(array1[i].GetType()) ?? DefaultValueComparer : DefaultValueComparer;\n                var valueComparer2 = array2[i] != null ? OverridesCollection.GetComparer(array2[i].GetType()) ?? DefaultValueComparer : DefaultValueComparer;\n\n                if (array1[i] == null)\n                {\n                    yield return new Difference($\"[{i}]\", string.Empty, valueComparer2.ToString(array2[i]));\n                    continue;\n                }\n\n                if (array2[i] == null)\n                {\n                    yield return new Difference($\"[{i}]\", valueComparer1.ToString(array1[i]), string.Empty);\n                    continue;\n                }\n\n                if (array1[i].GetType() != array2[i].GetType())\n                {\n                    yield return new Difference($\"[{i}]\", valueComparer1.ToString(array1[i]), valueComparer2.ToString(array2[i]), DifferenceTypes.TypeMismatch);\n                    continue;\n                }\n\n                var comparer = Factory.GetObjectsComparer(array1[i].GetType(), Settings, this);\n                foreach (var failure in comparer.CalculateDifferences(array1[i].GetType(), array1[i], array2[i]))\n                {\n                    yield return failure.InsertPath($\"[{i}]\");\n                }\n            }\n        }\n\n        public bool IsMatch(Type type, object obj1, object obj2)\n        {\n            return type.InheritsFrom(typeof(IEnumerable)) && !type.InheritsFrom(typeof(IEnumerable<>));\n        }\n\n        public bool IsStopComparison(Type type, object obj1, object obj2)\n        {\n            return Settings.EmptyAndNullEnumerablesEqual && obj1 == null || obj2 == null;\n        }\n\n        public bool SkipMember(Type type, MemberInfo member)\n        {\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/CustomComparers/EnumerablesComparer~1.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Reflection;\nusing ObjectsComparer.Utils;\n\nnamespace ObjectsComparer\n{\n    internal class EnumerablesComparer<T> : AbstractComparer\n    {\n        private readonly IComparer<T> _comparer;\n\n        public EnumerablesComparer(ComparisonSettings settings, BaseComparer parentComparer, IComparersFactory factory)\n            :base(settings, parentComparer, factory)\n        {\n            _comparer = Factory.GetObjectsComparer<T>(Settings, this);\n        }\n\n        public override IEnumerable<Difference> CalculateDifferences(Type type, object obj1, object obj2)\n        {\n            if (!type.InheritsFrom(typeof(IEnumerable<>)))\n            {\n                throw new ArgumentException(\"Invalid type\");\n            }\n\n            if (!Settings.EmptyAndNullEnumerablesEqual &&\n                (obj1 == null || obj2 == null) && obj1 != obj2)\n            {\n                yield break;\n            }\n\n            obj1 = obj1 ?? Enumerable.Empty<T>();\n            obj2 = obj2 ?? Enumerable.Empty<T>();\n\n            if (!obj1.GetType().InheritsFrom(typeof(IEnumerable<T>)))\n            {\n                throw new ArgumentException(nameof(obj1));\n            }\n\n            if (!obj2.GetType().InheritsFrom(typeof(IEnumerable<T>)))\n            {\n                throw new ArgumentException(nameof(obj2));\n            }\n\n            var list1 = ((IEnumerable<T>)obj1).ToList();\n            var list2 = ((IEnumerable<T>)obj2).ToList();\n\n            if (list1.Count != list2.Count)\n            {\n                if (!type.GetTypeInfo().IsArray)\n                {\n                    yield return new Difference(\"\", list1.Count.ToString(), list2.Count.ToString(), \n                        DifferenceTypes.NumberOfElementsMismatch);\n                }\n\n                yield break;\n            }\n\n            for (var i = 0; i < list2.Count; i++)\n            {\n                foreach (var failure in _comparer.CalculateDifferences(list1[i], list2[i]))\n                {\n                    yield return failure.InsertPath($\"[{i}]\");\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/CustomComparers/ExpandoObjectComparer.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Dynamic;\nusing System.Linq;\nusing System.Reflection;\nusing ObjectsComparer.Utils;\n\nnamespace ObjectsComparer\n{\n    internal class ExpandoObjectComparer : AbstractDynamicObjectsComprer<ExpandoObject>\n    {\n        public ExpandoObjectComparer(ComparisonSettings settings, BaseComparer parentComparer, IComparersFactory factory)\n            : base(settings, parentComparer, factory)\n        {\n        }\n\n        public override bool IsMatch(Type type, object obj1, object obj2)\n        {\n            return type.InheritsFrom(typeof(ExpandoObject)) || \n                   (obj1 != null && obj2 != null && type == typeof(object) && obj1.GetType().InheritsFrom(typeof(ExpandoObject)) && obj2.GetType().InheritsFrom(typeof(ExpandoObject)));\n        }\n\n        public override bool IsStopComparison(Type type, object obj1, object obj2)\n        {\n            return obj1 == null || obj2 == null;\n        }\n\n        public override bool SkipMember(Type type, MemberInfo member) => false;\n\n        protected override IList<string> GetProperties(ExpandoObject obj)\n        {\n            return ((IDictionary<string, object>) obj)?.Keys.ToList() ?? new List<string>();\n        }\n\n        protected override bool TryGetMemberValue(ExpandoObject obj, string propertyName, out object value)\n        {\n            if (obj != null)\n            {\n                return ((IDictionary<string, object>) obj).TryGetValue(propertyName, out value);\n            }\n\n            value = null;\n\n            return false;\n\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/CustomComparers/GenericEnumerablesComparer.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.Linq;\nusing System.Reflection;\nusing ObjectsComparer.Utils;\n\nnamespace ObjectsComparer\n{\n    internal class GenericEnumerablesComparer : AbstractEnumerablesComparer\n    {\n        public GenericEnumerablesComparer(ComparisonSettings settings, BaseComparer parentComparer,\n            IComparersFactory factory)\n            : base(settings, parentComparer, factory)\n        {\n        }\n\n        public override IEnumerable<Difference> CalculateDifferences(Type type, object obj1, object obj2)\n        {\n            if (obj1 == null && obj2 == null)\n            {\n                yield break;\n            }\n\n            var typeInfo = (obj1 ?? obj2).GetType().GetTypeInfo();\n\n            Type elementType;\n\n            if (typeInfo.IsGenericType && typeInfo.GetGenericTypeDefinition() == typeof(IEnumerable<>))\n            {\n                elementType = typeInfo.GetElementType();\n            }\n            else\n            {\n                elementType = typeInfo.GetInterfaces()\n                    .Where(\n                        i =>\n                            i.GetTypeInfo().IsGenericType &&\n                            i.GetTypeInfo().GetGenericTypeDefinition() == typeof(IEnumerable<>))\n                    .Select(i => i.GetTypeInfo().GetGenericArguments()[0])\n                    .First();\n            }\n\n            var enumerablesComparerType = typeof(EnumerablesComparer<>).MakeGenericType(elementType);\n            var comparer = (IComparer)Activator.CreateInstance(enumerablesComparerType, Settings, this, Factory);\n\n            foreach (var difference in comparer.CalculateDifferences(type, obj1, obj2))\n            {\n                yield return difference;\n            }\n        }\n\n        public override bool IsMatch(Type type, object obj1, object obj2)\n        {\n            return type.InheritsFrom(typeof(IEnumerable<>));\n        }\n\n        public override bool SkipMember(Type type, MemberInfo member)\n        {\n            if (base.SkipMember(type, member))\n            {\n                return true;\n            }\n\n            if (type.InheritsFrom(typeof(ICollection<>)) &&\n                member.Name == PropertyHelper.GetMemberInfo(() => new Collection<string>().Count).Name)\n            {\n                return true;\n            }\n\n            if (!type.InheritsFrom(typeof(IDictionary<,>)))\n            {\n                return false;\n            }\n\n            return member.Name == PropertyHelper.GetMemberInfo(() => new Dictionary<object, object>().Values).Name ||\n                   member.Name == PropertyHelper.GetMemberInfo(() => new Dictionary<object, object>().Keys).Name;\n        }\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/CustomComparers/HashSetsComparer.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Reflection;\nusing ObjectsComparer.Utils;\n\nnamespace ObjectsComparer\n{\n    internal class HashSetsComparer : AbstractEnumerablesComparer\n    {\n        public HashSetsComparer(ComparisonSettings settings, BaseComparer parentComparer, IComparersFactory factory)\n            : base(settings, parentComparer, factory)\n        {\n        }\n\n        public override IEnumerable<Difference> CalculateDifferences(Type type, object obj1, object obj2)\n        {\n            if (obj1 == null && obj2 == null)\n            {\n                yield break;\n            }\n\n            var typeInfo = (obj1 ?? obj2).GetType().GetTypeInfo();\n\n            Type elementType = typeInfo.GetInterfaces()\n                    .Where(\n                        i =>\n                            i.GetTypeInfo().IsGenericType &&\n                            i.GetTypeInfo().GetGenericTypeDefinition() == typeof(ISet<>))\n                    .Select(i => i.GetTypeInfo().GetGenericArguments()[0])\n                    .First();\n\n            var enumerablesComparerType = typeof(HashSetsComparer<>).MakeGenericType(elementType);\n            var comparer = (IComparer)Activator.CreateInstance(enumerablesComparerType, Settings, this, Factory);\n\n            foreach (var difference in comparer.CalculateDifferences(type, obj1, obj2))\n            {\n                yield return difference;\n            }\n        }\n\n        public override bool IsMatch(Type type, object obj1, object obj2)\n        {\n            return type.InheritsFrom(typeof(HashSet<>));\n        }\n\n        public override bool SkipMember(Type type, MemberInfo member)\n        {\n            return base.SkipMember(type, member) ||\n                   member.Name == PropertyHelper.GetMemberInfo(() => new HashSet<string>().Comparer).Name ||\n                   member.Name == PropertyHelper.GetMemberInfo(() => new HashSet<string>().Count).Name;\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/CustomComparers/HashSetsComparer~1.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing ObjectsComparer.Utils;\n\nnamespace ObjectsComparer\n{\n    internal class HashSetsComparer<T> : AbstractComparer\n    {\n        public HashSetsComparer(ComparisonSettings settings, BaseComparer parentComparer, IComparersFactory factory)\n            :base(settings, parentComparer, factory)\n        {\n        }\n\n        public override IEnumerable<Difference> CalculateDifferences(Type type, object obj1, object obj2)\n        {\n            if (!type.InheritsFrom(typeof(HashSet<>)))\n            {\n                throw new ArgumentException(\"Invalid type\");\n            }\n\n            if (!Settings.EmptyAndNullEnumerablesEqual &&\n                (obj1 == null || obj2 == null) && obj1 != obj2)\n            {\n                yield break;\n            }\n\n            obj1 = obj1 ?? new HashSet<T>();\n            obj2 = obj2 ?? new HashSet<T>();\n\n            if (!obj1.GetType().InheritsFrom(typeof(HashSet<T>)))\n            {\n                throw new ArgumentException(nameof(obj1));\n            }\n\n            if (!obj2.GetType().InheritsFrom(typeof(HashSet<T>)))\n            {\n                throw new ArgumentException(nameof(obj2));\n            }\n\n            var hashSet1 = ((IEnumerable<T>)obj1).ToList();\n            var hashSet2 = ((IEnumerable<T>)obj2).ToList();\n            var valueComparer = OverridesCollection.GetComparer(typeof(T)) ?? DefaultValueComparer;\n\n            foreach (var element in hashSet1)\n            {\n                if (!hashSet2.Contains(element))\n                {\n                    yield return new Difference(\"\", valueComparer.ToString(element), string.Empty,\n                        DifferenceTypes.MissedElementInSecondObject);\n                }\n            }\n            \n            foreach (var element in hashSet2)\n            {\n                if (!hashSet1.Contains(element))\n                {\n                    yield return new Difference(\"\", string.Empty, valueComparer.ToString(element),\n                        DifferenceTypes.MissedElementInFirstObject);\n                }\n            }\n        }\n\n        public bool IsMatch(Type type, object obj1, object obj2)\n        {\n            return type.InheritsFrom(typeof(HashSet<>));\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/CustomComparers/IComparerWithCondition.cs",
    "content": "﻿using System;\nusing System.Reflection;\n\nnamespace ObjectsComparer\n{\n    internal interface IComparerWithCondition: IComparer\n    {\n        bool IsMatch(Type type, object obj1, object obj2);\n\n        bool IsStopComparison(Type type, object obj1, object obj2);\n\n        bool SkipMember(Type type, MemberInfo member);\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/CustomComparers/MultidimensionalArrayComparer~1.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing ObjectsComparer.Utils;\n\nnamespace ObjectsComparer\n{\n    internal class MultidimensionalArrayComparer<T> : AbstractComparer\n    {\n        private readonly IComparer<T> _comparer;\n\n        public MultidimensionalArrayComparer(ComparisonSettings settings, BaseComparer parentComparer, IComparersFactory factory)\n            : base(settings, parentComparer, factory)\n        {\n            _comparer = Factory.GetObjectsComparer<T>(Settings, this);\n        }\n\n        public override IEnumerable<Difference> CalculateDifferences(Type type, object obj1, object obj2)\n        {\n            if (!type.InheritsFrom(typeof(Array)))\n            {\n                throw new ArgumentException(\"Invalid type\");\n            }\n\n            if (!Settings.EmptyAndNullEnumerablesEqual &&\n                (obj1 == null || obj2 == null) && obj1 != obj2)\n            {\n                yield break;\n            }\n\n            if (obj1 != null && !obj1.GetType().InheritsFrom(typeof(Array)))\n            {\n                throw new ArgumentException(nameof(obj1));\n            }\n\n            if (obj2 != null && !obj2.GetType().InheritsFrom(typeof(Array)))\n            {\n                throw new ArgumentException(nameof(obj2));\n            }\n\n            var array1 = (Array)obj1 ?? Array.CreateInstance(typeof(T), new int[type.GetArrayRank()]);\n            var array2 = (Array)obj2 ?? Array.CreateInstance(typeof(T), new int[type.GetArrayRank()]);\n\n            if (array1.Rank != array2.Rank)\n            {\n                yield return new Difference(\"Rank\", array1.Rank.ToString(), array2.Rank.ToString());\n                yield break;\n            }\n\n            var dimensionsFailure = false;\n            for (var i = 0; i < array1.Rank; i++)\n            {\n                var length1 = array1.GetLength(i);\n                var length2 = array2.GetLength(i);\n\n                // ReSharper disable once InvertIf\n                if (length1 != length2)\n                {\n                    dimensionsFailure = true;\n                    yield return new Difference($\"Dimension{i}\", length1.ToString(), length2.ToString());\n                }\n            }\n\n            if (dimensionsFailure)\n            {\n                yield break;\n            }\n\n            for (var i = 0; i < array1.Length; i++)\n            {\n                var indecies = IndexToCoordinates(array1, i);\n\n                foreach (var failure in _comparer.CalculateDifferences((T)array1.GetValue(indecies), (T)array2.GetValue(indecies)))\n                {\n                    yield return failure.InsertPath($\"[{string.Join(\",\", indecies)}]\");\n                }\n            }\n        }\n\n        private static int[] IndexToCoordinates(Array arr, int i)\n        {\n            var dims = Enumerable.Range(0, arr.Rank)\n                .Select(arr.GetLength)\n                .ToArray();\n\n            return dims.Select((d, n) => i / dims.Take(n).Aggregate(1, (i1, i2) => i1 * i2) % d).ToArray();\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/CustomComparers/MultidimensionalArraysComparer.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Reflection;\nusing ObjectsComparer.Utils;\n\nnamespace ObjectsComparer\n{\n    internal class MultidimensionalArraysComparer : AbstractEnumerablesComparer\n    {\n        public MultidimensionalArraysComparer(ComparisonSettings settings, BaseComparer parentComparer,\n            IComparersFactory factory)\n            : base(settings, parentComparer, factory)\n        {\n        }\n\n        public override IEnumerable<Difference> CalculateDifferences(Type type, object obj1, object obj2)\n        {\n            if (obj1 == null && obj2 == null)\n            {\n                yield break;\n            }\n\n            var typeInfo = (obj1 ?? obj2).GetType().GetTypeInfo();\n            var enumerablesComparerType = typeof(MultidimensionalArrayComparer<>).MakeGenericType(typeInfo.GetElementType());\n            var comparer = (IComparer)Activator.CreateInstance(enumerablesComparerType, Settings, this, Factory);\n\n            foreach (var difference in comparer.CalculateDifferences(type, obj1, obj2))\n            {\n                yield return difference;\n            }\n        }\n\n        public override bool IsMatch(Type type, object obj1, object obj2)\n        {\n            return type.GetTypeInfo().IsArray && type.GetTypeInfo().GetArrayRank() > 1;\n        }\n\n        public override bool SkipMember(Type type, MemberInfo member)\n        {\n            if (base.SkipMember(type, member))\n            {\n                return true;\n            }\n\n            if (!type.IsArray)\n            {\n                return false;\n            }\n\n            Array array = new int[0];\n            return member.Name == PropertyHelper.GetMemberInfo(() => array.Length).Name;\n        }\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/CustomComparers/TypesComparer.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Reflection;\nusing ObjectsComparer.Utils;\n\nnamespace ObjectsComparer\n{\n    internal class TypesComparer : AbstractComparer, IComparerWithCondition\n    {\n        public TypesComparer(ComparisonSettings settings, BaseComparer parentComparer,\n            IComparersFactory factory)\n            : base(settings, parentComparer, factory)\n        {\n        }\n\n        public override IEnumerable<Difference> CalculateDifferences(Type type, object obj1, object obj2)\n        {\n            if (obj1 == null && obj2 == null)\n            {\n                yield break;\n            }\n\n            if (obj1?.GetType().InheritsFrom(typeof(Type)) == false)\n            {\n                throw new ArgumentException(nameof(obj1));\n            }\n\n            if (obj2?.GetType().InheritsFrom(typeof(Type)) == false)\n            {\n                throw new ArgumentException(nameof(obj2));\n            }\n\n            var type1Str = obj1?.ToString();\n            var type2Str = obj2?.ToString();\n\n            if (type1Str != type2Str)\n            {\n                yield return new Difference(string.Empty, type1Str, type2Str);\n            }\n        }\n\n        public bool IsMatch(Type type, object obj1, object obj2)\n        {\n            return type.InheritsFrom(typeof(Type));\n        }\n\n        public bool IsStopComparison(Type type, object obj1, object obj2) => true;\n\n        public bool SkipMember(Type type, MemberInfo member) => true;\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/Difference.cs",
    "content": "﻿namespace ObjectsComparer\n{\n    /// <summary>\n    /// Represents difference in one member between objects.\n    /// </summary>\n    public class Difference\n    {\n        /// <summary>\n        /// Path to the member.\n        /// </summary>\n        public string MemberPath { get; }\n\n        /// <summary>\n        /// Value in the first object, converted to string.\n        /// </summary>\n        public string Value1 { get; }\n\n        /// <summary>\n        /// Value in the second object, converted to string.\n        /// </summary>\n        public string Value2 { get; }\n\n        /// <summary>\n        /// Type of the difference.\n        /// </summary>\n        public DifferenceTypes DifferenceType { get; }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"Difference\" /> class. \n        /// </summary>\n        /// <param name=\"memberPath\">Member Path.</param>\n        /// <param name=\"value1\">Value of the first object, converted to string.</param>\n        /// <param name=\"value2\">Value of the second object, converted to string.</param>\n        /// <param name=\"differenceType\">Type of the difference.</param>\n        public Difference(string memberPath, string value1, string value2,\n            DifferenceTypes differenceType = DifferenceTypes.ValueMismatch)\n        {\n            MemberPath = memberPath;\n            Value1 = value1;\n            Value2 = value2;\n            DifferenceType = differenceType;\n        }\n\n        /// <summary>\n        /// Combines difference with path of the root element.\n        /// </summary>\n        /// <param name=\"path\">Root element path.</param>\n        /// <returns>Difference with combined <see cref=\"MemberPath\"/>.</returns>\n        public Difference InsertPath(string path)\n        {\n            var newPath = string.IsNullOrWhiteSpace(MemberPath) || MemberPath.StartsWith(\"[\")\n                ? path + MemberPath\n                : path + \".\" + MemberPath;\n\n            return new Difference(\n                newPath,\n                Value1,\n                Value2,\n                DifferenceType);\n        }\n\n        /// <summary>Returns a string that represents the current object.</summary>\n        /// <returns>A string that represents the current object.</returns>\n        public override string ToString()\n        {\n            return $\"Difference: DifferenceType={DifferenceType}, MemberPath='{MemberPath}', Value1='{Value1}', Value2='{Value2}'.\";\n        }\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/DifferenceTypes.cs",
    "content": "﻿using System.Collections;\n\nnamespace ObjectsComparer\n{\n    /// <summary>\n    /// Specifies types of the differences between object members.\n    /// </summary>\n    public enum DifferenceTypes\n    {\n        /// <summary>\n        /// Value of the member in first and second objects are not equal.\n        /// </summary>\n        ValueMismatch,\n\n        /// <summary>\n        /// Type of the member in first and second objects  are not equal.\n        /// </summary>\n        TypeMismatch,\n\n        /// <summary>\n        /// Member does not exist in the first object.\n        /// </summary>\n        MissedMemberInFirstObject,\n\n        /// <summary>\n        /// Member does not exist in the second object.\n        /// </summary>\n        MissedMemberInSecondObject,\n\n        /// <summary>\n        /// First object does not contain element.\n        /// </summary>\n        MissedElementInFirstObject,\n\n        /// <summary>\n        /// Second object does not contain element.\n        /// </summary>\n        MissedElementInSecondObject,\n\n        /// <summary>\n        /// <see cref=\"IEnumerable\"/>s have different number of elements.\n        /// </summary>\n        NumberOfElementsMismatch\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/Exceptions/AmbiguousComparerOverrideResolutionException.cs",
    "content": "﻿using System;\nusing System.Reflection;\n\nnamespace ObjectsComparer.Exceptions\n{\n    /// <summary>\n    /// Represents errors that occur when Objects Comparer has more than one comparer override which could be used to compare member.\n    /// </summary>\n    public class AmbiguousComparerOverrideResolutionException: Exception\n    {\n        /// <summary>\n        /// Name of the Member that was a cause of exception\n        /// </summary>\n        public string MemberName { get; }\n\n        /// <summary>\n        /// MemberInfo of the Member that was a cause of exception\n        /// </summary>\n        public MemberInfo MemberInfo { get; }\n\n        /// <summary>\n        /// Type that was a cause of exception\n        /// </summary>\n        public Type Type { get; }\n\n        internal AmbiguousComparerOverrideResolutionException(MemberInfo memberInfo) \n            : base($\"Unable to resolve comparer for member {memberInfo.MemberType}. More than one value comparer meet criteria for this member.\")\n        {\n            MemberInfo = memberInfo;\n            MemberName = memberInfo.Name;\n        }\n\n        internal AmbiguousComparerOverrideResolutionException(string memberName)\n            : base($\"Unable to resolve comparer for member {memberName}. More than one value comparer meet criteria for this member.\")\n        {\n            MemberName = memberName;\n        }\n\n        internal AmbiguousComparerOverrideResolutionException(Type type)\n            : base($\"Unable to resolve comparer for type {type.GetTypeInfo().FullName}. More than one value comparer meet criteria for this type.\")\n        {\n            Type = type;\n        }\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/Exceptions/ValueComparerExistsException.cs",
    "content": "﻿using System;\nusing System.Reflection;\n\nnamespace ObjectsComparer.Exceptions\n{\n    /// <summary>\n    /// Represents errors that occur when comparer for Member has already been added.\n    /// </summary>\n    public class ValueComparerExistsException: Exception\n    {\n        /// <summary>\n        /// MemberInfo of the Member that was a cause of exception\n        /// </summary>\n        public MemberInfo MemberInfo { get; }\n\n        internal ValueComparerExistsException(MemberInfo memberInfo) \n            : base($\"Comparer override for member {memberInfo.MemberType} has already been added.\")\n        {\n            MemberInfo = memberInfo;\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/IBaseComparer.cs",
    "content": "using System;\nusing System.Linq.Expressions;\nusing System.Reflection;\n\nnamespace ObjectsComparer\n{\n    /// <summary>\n    /// Defines properties and methods to configure comparer.\n    /// </summary>\n    public interface IBaseComparer\n    {\n        /// <summary>\n        /// Default <see cref=\"IValueComparer\"/>\n        /// </summary>\n        IValueComparer DefaultValueComparer { get; }\n\n        /// <summary>\n        /// Comparison Settings.\n        /// </summary>\n        ComparisonSettings Settings { get; }\n\n        /// <summary>\n        /// Sets <see cref=\"DefaultValueComparer\"/>.\n        /// </summary>\n        /// <param name=\"valueComparer\">Value Comparer.</param>\n        void SetDefaultComparer(IValueComparer valueComparer);\n\n        /// <summary>\n        /// Adds Comparer Override by Member.\n        /// </summary>\n        /// <typeparam name=\"TProp\">Type of the member.</typeparam>\n        /// <param name=\"memberLambda\">Lambda to get member.</param>\n        /// <param name=\"valueComparer\">Value Comparer.</param>\n        void AddComparerOverride<TProp>(Expression<Func<TProp>> memberLambda, IValueComparer valueComparer);\n\n        /// <summary>\n        /// Adds Comparer Override by Member.\n        /// </summary>\n        /// <param name=\"memberInfo\">Member Info.</param>\n        /// <param name=\"valueComparer\">Value Comparer.</param>\n        void AddComparerOverride(MemberInfo memberInfo, IValueComparer valueComparer);\n\n        /// <summary>\n        /// Adds Comparer Override by Type.\n        /// </summary>\n        /// <param name=\"type\">Type.</param>\n        /// <param name=\"valueComparer\">Value Comparer.</param>\n        /// <param name=\"filter\">Value Comparer will be used only if filter(memberInfo) == true. Null by default.</param>\n        void AddComparerOverride(Type type, IValueComparer valueComparer, Func<MemberInfo, bool> filter = null);\n\n        /// <summary>\n        /// Adds Comparer Override by Type.\n        /// </summary>\n        /// <typeparam name=\"TType\">Type.</typeparam>\n        /// <param name=\"valueComparer\">Value Comparer.</param>\n        /// <param name=\"filter\">Value Comparer will be used only if filter(memberInfo) == true. Null by default.</param>\n        void AddComparerOverride<TType>(IValueComparer valueComparer, Func<MemberInfo, bool> filter = null);\n\n        /// <summary>\n        /// Adds Comparer Override by Member.\n        /// </summary>\n        /// <typeparam name=\"TProp\">Type of the member.</typeparam>\n        /// <param name=\"memberLambda\">Lambda to get member.</param>\n        /// <param name=\"compareFunction\">Function to compare objects.</param>\n        /// <param name=\"toStringFunction\">Function to convert objects to string.</param>\n        void AddComparerOverride<TProp>(\n            Expression<Func<TProp>> memberLambda, \n            Func<TProp, TProp, ComparisonSettings, bool> compareFunction, \n            Func<TProp, string> toStringFunction);\n\n        /// <summary>\n        /// Adds Comparer Override by Member.\n        /// </summary>\n        /// <typeparam name=\"TProp\">Type of the member.</typeparam>\n        /// <param name=\"memberLambda\">Lambda to get member.</param>\n        /// <param name=\"compareFunction\">Function to compare objects.</param>\n        void AddComparerOverride<TProp>(\n            Expression<Func<TProp>> memberLambda,\n            Func<TProp, TProp, ComparisonSettings, bool> compareFunction);\n\n        /// <summary>\n        /// Adds Comparer Override by Member name.\n        /// </summary>\n        /// <param name=\"memberName\">Member Name.</param>\n        /// <param name=\"valueComparer\">Value Comparer.</param>\n        /// <param name=\"filter\">Value Comparer will be used only if filter(memberInfo) == true. Null by default.</param>\n        void AddComparerOverride(string memberName, IValueComparer valueComparer, Func<MemberInfo, bool> filter = null);\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/IComparer.cs",
    "content": "using System;\nusing System.Collections.Generic;\n\nnamespace ObjectsComparer\n{\n    /// <summary>\n    /// Defines methods to compare complex objects.\n    /// </summary>\n    public interface IComparer : IBaseComparer\n    {\n        /// <summary>\n        /// Compares objects.\n        /// </summary>\n        /// <param name=\"type\">Type.</param>\n        /// <param name=\"obj1\">Object 1.</param>\n        /// <param name=\"obj2\">Object 2.</param>\n        /// <param name=\"differences\">List of differences.</param>\n        /// <returns>True if objects are equal, otherwise false.</returns>\n        bool Compare(Type type, object obj1, object obj2, out IEnumerable<Difference> differences);\n\n        /// <summary>\n        /// Compares objects.\n        /// </summary>\n        /// <typeparam name=\"T\">Type.</typeparam>\n        /// <param name=\"obj1\">Object 1.</param>\n        /// <param name=\"obj2\">Object 2.</param>\n        /// <param name=\"differences\">List of differences.</param>\n        /// <returns>True if objects are equal, otherwise false.</returns>\n        bool Compare<T>(T obj1, T obj2, out IEnumerable<Difference> differences);\n\n        /// <summary>\n        /// Compares objects.\n        /// </summary>\n        /// <param name=\"type\">Type.</param>\n        /// <param name=\"obj1\">Object 1.</param>\n        /// <param name=\"obj2\">Object 2.</param>\n        /// <returns>True if objects are equal, otherwise false.</returns>\n        bool Compare(Type type, object obj1, object obj2);\n\n        /// <summary>\n        /// Compares objects.\n        /// </summary>\n        /// <typeparam name=\"T\">Type.</typeparam>\n        /// <param name=\"obj1\">Object 1.</param>\n        /// <param name=\"obj2\">Object 2.</param>\n        /// <returns>True if objects are equal, otherwise false.</returns>\n        bool Compare<T>(T obj1, T obj2);\n\n        /// <summary>\n        /// Calculates list of differences between objects.\n        /// </summary>\n        /// <param name=\"type\">Type.</param>\n        /// <param name=\"obj1\">Object 1.</param>\n        /// <param name=\"obj2\">Object 2.</param>\n        /// <returns>List of differences between objects.</returns>\n        IEnumerable<Difference> CalculateDifferences(Type type, object obj1, object obj2);\n\n        /// <summary>\n        /// Calculates list of differences between objects.\n        /// </summary>\n        /// <typeparam name=\"T\">Type.</typeparam>\n        /// <param name=\"obj1\">Object 1.</param>\n        /// <param name=\"obj2\">Object 2.</param>\n        /// <returns>List of differences between objects.</returns>\n        IEnumerable<Difference> CalculateDifferences<T>(T obj1, T obj2);\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/IComparersFactory.cs",
    "content": "using System;\n\nnamespace ObjectsComparer\n{\n    /// <summary>\n    /// Defines methods to create type specific comparers.\n    /// </summary>\n    public interface IComparersFactory\n    {\n        /// <summary>\n        /// Creates type specific comparer.\n        /// </summary>\n        /// <typeparam name=\"T\">Type.</typeparam>\n        /// <param name=\"settings\">Comparison Settings. Null by default.</param>\n        /// <param name=\"parentComparer\">Parent comparer. Null by default.</param>\n        /// <returns>Instance of <see cref=\"IComparer{T}\"/>.</returns>\n        IComparer<T> GetObjectsComparer<T>(ComparisonSettings settings = null, BaseComparer parentComparer = null);\n\n        /// <summary>\n        /// Creates type specific comparer.\n        /// </summary>\n        /// <param name=\"type\">Type.</param>\n        /// <param name=\"settings\">Comparison Settings. Null by default.</param>\n        /// <param name=\"parentComparer\">Parent comparer. Null by default.</param>\n        /// <returns>Instance of <see cref=\"IComparer\"/>.</returns>\n        IComparer GetObjectsComparer(Type type, ComparisonSettings settings = null, BaseComparer parentComparer = null);\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/IComparer~1.cs",
    "content": "using System.Collections.Generic;\n\nnamespace ObjectsComparer\n{\n    /// <summary>\n    /// Defines methods to compare complex objects of particular type.\n    /// </summary>\n    public interface IComparer<in T>: IBaseComparer\n    {\n        /// <summary>\n        /// Compares objects.\n        /// </summary>\n        /// <param name=\"obj1\">Object 1.</param>\n        /// <param name=\"obj2\">Object 2.</param>\n        /// <param name=\"differences\">List of differences.</param>\n        /// <returns>True if objects are equal, otherwise false.</returns>\n        bool Compare(T obj1, T obj2, out IEnumerable<Difference> differences);\n\n        /// <summary>\n        /// Compares objects.\n        /// </summary>\n        /// <param name=\"obj1\">Object 1.</param>\n        /// <param name=\"obj2\">Object 2.</param>\n        /// <returns>True if objects are equal, otherwise false.</returns>\n        bool Compare(T obj1, T obj2);\n\n        /// <summary>\n        /// Calculates list of differences between objects.\n        /// </summary>\n        /// <param name=\"obj1\">Object 1.</param>\n        /// <param name=\"obj2\">Object 2.</param>\n        /// <returns>List of differences between objects.</returns>\n        IEnumerable<Difference> CalculateDifferences(T obj1, T obj2);\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2019 Valerii Tereshchenko\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/ObjectsComparer.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFrameworks>netstandard2.1;netstandard2.0;netstandard1.6;net45</TargetFrameworks>\n    <Version>1.4.1</Version>\n    <Authors>Valerii Tereshchenko</Authors>\n    <Company />\n    <Product>Objects Data Comparer</Product>\n    <Description>Framework provides mechanism to compare classes, allows override comparison rules for specific properties and types.</Description>\n    <Copyright>Copyright © Valerii Tereshchenko 2019</Copyright>\n    <PackageLicenseUrl></PackageLicenseUrl>\n    <PackageProjectUrl>https://github.com/ValeraT1982/ObjectsComparer</PackageProjectUrl>\n    <PackageIconUrl>https://github.com/ValeraT1982/ObjectsComparer/blob/master/ObjectsDataComparer.png?raw=true</PackageIconUrl>\n    <RepositoryUrl>https://github.com/ValeraT1982/ObjectsComparer</RepositoryUrl>\n    <PackageTags>C#, compare, class, reflection, object, comparison</PackageTags>\n    <PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>\n    <PackageLicenseFile>LICENSE</PackageLicenseFile>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"System.Dynamic.Runtime\" Version=\"4.3.0\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <Folder Include=\"Properties\\\" />\n    <None Include=\"LICENSE\" Pack=\"true\" PackagePath=\"\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/ObjectsComparer.csproj.DotSettings",
    "content": "﻿<wpf:ResourceDictionary xml:space=\"preserve\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" xmlns:s=\"clr-namespace:System;assembly=mscorlib\" xmlns:ss=\"urn:shemas-jetbrains-com:settings-storage-xaml\" xmlns:wpf=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n\t<s:Boolean x:Key=\"/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=customcomparers/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=valuecomparers/@EntryIndexedValue\">True</s:Boolean></wpf:ResourceDictionary>"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Runtime.CompilerServices;\n\n[assembly: InternalsVisibleTo(\"ObjectsComparer.Tests\")]\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/Utils/MemberInfoExtensions.cs",
    "content": "﻿using System;\nusing System.Linq.Expressions;\nusing System.Reflection;\n\nnamespace ObjectsComparer.Utils\n{\n    internal static class MemberInfoExtensions\n    {\n        public static Type GetMemberType(this MemberInfo memberInfo)\n        {\n            var propertyInfo = memberInfo as PropertyInfo;\n            if (propertyInfo != null)\n            {\n                return propertyInfo.PropertyType;\n            }\n\n            var fieldInfo = memberInfo as FieldInfo;\n            if (fieldInfo != null)\n            {\n                return fieldInfo.FieldType;\n            }\n\n            throw new Exception(\"Unsupported Type\");\n        }\n\n        public static object GetMemberValue(this MemberInfo memberInfo, object obj)\n        {\n            var propertyInfo = memberInfo as PropertyInfo;\n            if (propertyInfo != null)\n            {\n                try\n                {\n                    return propertyInfo.GetValue(obj);\n                }\n                catch\n                {\n                    return $\"Unable to get value of property {memberInfo.Name} of type {memberInfo.DeclaringType}\";\n                }\n            }\n\n            var fieldInfo = memberInfo as FieldInfo;\n            if (fieldInfo != null)\n            {\n                return fieldInfo.GetValue(obj);\n            }\n\n            throw new Exception(\"Unsupported Type\");\n        }\n\n        public static string GetMethodName<T>(Expression<Action<T>> expression)\n        {\n            return ((MethodCallExpression)expression.Body).Method.Name;\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/Utils/PropertyHelper.cs",
    "content": "﻿using System;\nusing System.Linq.Expressions;\nusing System.Reflection;\n\nnamespace ObjectsComparer.Utils\n{\n    internal class PropertyHelper\n    {\n        public static MemberInfo GetMemberInfo<T>(Expression<Func<T>> memberLambda)\n        {\n            MemberExpression exp;\n\n            //this line is necessary, because sometimes the expression comes in as Convert(originalexpression)\n            switch (memberLambda.Body)\n            {\n                case UnaryExpression body:\n                    var unExp = body;\n                    if (unExp.Operand is MemberExpression operand)\n                    {\n                        exp = operand;\n                    }\n                    else\n                    {\n                        throw new ArgumentException();\n                    }\n\n                    break;\n                case MemberExpression _:\n                    exp = (MemberExpression)memberLambda.Body;\n                    break;\n                default:\n                    throw new ArgumentException();\n            }\n\n            return exp.Member;\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/Utils/TypeExtensions.cs",
    "content": "﻿using System;\nusing System.Linq;\nusing System.Reflection;\n\nnamespace ObjectsComparer.Utils\n{\n    internal static class TypeExtensions\n    {\n        public static bool InheritsFrom(this Type t1, Type t2)\n        {\n            if (null == t1 || null == t2)\n            {\n                return false;\n            }\n\n            if (t1 == t2)\n            {\n                return true;\n            }\n\n            if (t1.GetTypeInfo().IsGenericType && t1.GetTypeInfo().GetGenericTypeDefinition() == t2)\n            {\n                return true;\n            }\n\n            if (t1.GetTypeInfo().GetInterfaces().Any(i => i.GetTypeInfo().IsGenericType && i.GetGenericTypeDefinition() == t2 || i == t2))\n            {\n                return true;\n            }\n\n            return t1.GetTypeInfo().BaseType != null &&\n                   InheritsFrom(t1.GetTypeInfo().BaseType, t2);\n        }\n\n        public static bool IsComparable(this Type type)\n        {\n            return type.GetTypeInfo().IsPrimitive ||\n                   type.GetTypeInfo().IsEnum ||\n                   type.InheritsFrom(typeof(IComparable)) ||\n                   type.InheritsFrom(typeof(IComparable<>));\n        }\n\n        public static object GetDefaultValue(this Type t)\n        {\n            if (t.GetTypeInfo().IsValueType && Nullable.GetUnderlyingType(t) == null)\n            {\n                return Activator.CreateInstance(t);\n            }\n\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/ValueComparers/AbstractValueComparer.cs",
    "content": "﻿namespace ObjectsComparer\n{\n    /// <summary>\n    /// Implementation of <see cref=\"IValueComparer\"/> which provides simplest implementation of <see cref=\"ToString(object)\"/> method.\n    /// </summary>\n    public abstract class AbstractValueComparer: IValueComparer\n    {\n        public abstract bool Compare(object obj1, object obj2, ComparisonSettings settings);\n\n        public virtual string ToString(object value)\n        {\n            return value?.ToString();\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/ValueComparers/AbstractValueComparer~1.cs",
    "content": "﻿namespace ObjectsComparer\n{\n    /// <summary>\n    /// Implementation of <see cref=\"IValueComparer{T}\"/> which provides simplest implementation of <see cref=\"ToString(T)\"/> method and \n    /// <see cref=\"Compare(object, object, ComparisonSettings)\"/> method to call <see cref=\"Compare(T, T, ComparisonSettings)\"/>.\n    /// </summary>\n    /// <typeparam name=\"T\">Type of the objects.</typeparam>\n    public abstract class AbstractValueComparer<T>: AbstractValueComparer, IValueComparer<T>\n    {\n        /// <summary>\n        /// Comparers <paramref name=\"obj1\"/> and <paramref name=\"obj2\"/>.\n        /// </summary>\n        /// <param name=\"obj1\">Object 1.</param>\n        /// <param name=\"obj2\">Object 2.</param>\n        /// <param name=\"settings\">Instance of <see cref=\"ComparisonSettings\"/> class.</param>\n        /// <returns>true if the objects are considered equal; otherwise, false.</returns>\n        public abstract bool Compare(T obj1, T obj2, ComparisonSettings settings);\n\n        /// <summary>\n        /// Converts values of comparing objects to <see cref=\"string\"/>.\n        /// </summary>\n        /// <param name=\"value\">Object to convert.</param>\n        /// <returns>A string that represents <see cref=\"value\"/>.</returns>\n        public virtual string ToString(T value)\n        {\n            return base.ToString(value);\n        }\n\n        /// <summary>\n        /// Comparers <paramref name=\"obj1\"/> and <paramref name=\"obj2\"/>.\n        /// </summary>\n        /// <param name=\"obj1\">Object 1.</param>\n        /// <param name=\"obj2\">Object 2.</param>\n        /// <param name=\"settings\">Instance of <see cref=\"ComparisonSettings\"/> class.</param>\n        /// <returns>true if the objects are considered equal; otherwise, false.</returns>\n        public override bool Compare(object obj1, object obj2, ComparisonSettings settings)\n        {\n            return Compare((T) obj1, (T) obj2, settings);\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/ValueComparers/DefaultValueComparer.cs",
    "content": "﻿namespace ObjectsComparer\n{\n    /// <summary>\n    /// Default implementation of <see cref=\"IValueComparer\"/>\n    /// </summary>\n    public class DefaultValueComparer: IValueComparer\n    {\n        private static volatile IValueComparer _instance;\n        /// <summary>\n        /// Static <see cref=\"DefaultValueComparer\"/> instance.\n        /// </summary>\n        public static IValueComparer Instance\n        {\n            get\n            {\n                if (_instance != null)\n                {\n                    return _instance;\n                }\n\n                lock (SyncRoot)\n                {\n                    if (_instance == null)\n                    {\n                        _instance = new DefaultValueComparer();\n                    }\n                }\n\n                return _instance;\n            }\n        }\n\n        private static readonly object SyncRoot = new object();\n\n        /// <summary>\n        /// Comparers <paramref name=\"obj1\"/> and <paramref name=\"obj2\"/>.\n        /// </summary>\n        /// <param name=\"obj1\">Object 1.</param>\n        /// <param name=\"obj2\">Object 2.</param>\n        /// <param name=\"settings\">Instance of <see cref=\"ComparisonSettings\"/> class.</param>\n        /// <returns>true if the objects are considered equal; otherwise, false.</returns>\n        public bool Compare(object obj1, object obj2, ComparisonSettings settings)\n        {\n            if (obj1 == null || obj2 == null)\n            {\n                return obj1 == obj2;\n            }\n\n            return obj1.Equals(obj2);\n        }\n\n        /// <summary>\n        /// Converts values of comparing objects to <see cref=\"string\"/>.\n        /// </summary>\n        /// <param name=\"value\">Object to convert.</param>\n        /// <returns>A string that represents <see cref=\"value\"/>.</returns>\n        public string ToString(object value)\n        {\n            return value?.ToString() ?? string.Empty;\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/ValueComparers/DefaultValueValueComparer.cs",
    "content": "﻿namespace ObjectsComparer\n{\n    /// <summary>\n    /// Allows to consider provided value and default value of type <see cref=\"T\"/> as equal values.\n    /// </summary>\n    /// <typeparam name=\"T\">Type of the objects.</typeparam>\n    public class DefaultValueValueComparer<T> : IValueComparer\n    {\n        private readonly T _defaultValue;\n        private readonly T _typeDefaultValue;\n        private readonly IValueComparer _valueComparer;\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"DefaultValueValueComparer{T}\" /> class. \n        /// </summary>\n        /// <param name=\"defaultValue\">Default value.</param>\n        /// <param name=\"valueComparer\">Instance of <see cref=\"IValueComparer\"/> which is used when values are not defaults.</param>\n        public DefaultValueValueComparer(T defaultValue, IValueComparer valueComparer)\n        {\n            _defaultValue = defaultValue;\n            _valueComparer = valueComparer;\n            _typeDefaultValue = default(T);\n        }\n\n        /// <summary>\n        /// Comparers <paramref name=\"obj1\"/> and <paramref name=\"obj2\"/>.\n        /// </summary>\n        /// <param name=\"obj1\">Object 1.</param>\n        /// <param name=\"obj2\">Object 2.</param>\n        /// <param name=\"settings\">Instance of <see cref=\"ComparisonSettings\"/> class.</param>\n        /// <returns>true if the objects are considered equal; otherwise, false.</returns>\n        public bool Compare(object obj1, object obj2, ComparisonSettings settings)\n        {\n            var isObj1Default = obj1?.Equals(_defaultValue) != false || obj1.Equals(_typeDefaultValue);\n            var isObj2Default = obj2?.Equals(_defaultValue) != false || obj2.Equals(_typeDefaultValue);\n\n            return isObj1Default && isObj2Default || _valueComparer.Compare(obj1, obj2, settings);\n        }\n\n        /// <summary>\n        /// Converts values of comparing objects to <see cref=\"string\"/>.\n        /// </summary>\n        /// <param name=\"value\">Object to convert.</param>\n        /// <returns>A string that represents <see cref=\"value\"/>.</returns>\n        public string ToString(object value)\n        {\n            return value?.Equals(_typeDefaultValue) != false ? _valueComparer.ToString(_defaultValue) : _valueComparer.ToString(value);\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/ValueComparers/DoNotCompareValueComparer.cs",
    "content": "﻿namespace ObjectsComparer\n{\n    /// <summary>\n    /// Allows to ignore comparison. Considers all values as equal.\n    /// </summary>\n    public class DoNotCompareValueComparer : IValueComparer\n    {\n        private static volatile IValueComparer _instance;\n        /// <summary>\n        /// Static <see cref=\"DoNotCompareValueComparer\"/> instance.\n        /// </summary>\n        public static IValueComparer Instance\n        {\n            get\n            {\n                if (_instance != null)\n                {\n                    return _instance;\n                }\n\n                lock (SyncRoot)\n                {\n                    if (_instance == null)\n                    {\n                        _instance = new DoNotCompareValueComparer();\n                    }\n                }\n\n                return _instance;\n            }\n        }\n\n        private static readonly object SyncRoot = new object();\n\n        private DoNotCompareValueComparer() { }\n\n        /// <summary>\n        /// Comparers <paramref name=\"obj1\"/> and <paramref name=\"obj2\"/>.\n        /// </summary>\n        /// <param name=\"obj1\">Object 1.</param>\n        /// <param name=\"obj2\">Object 2.</param>\n        /// <param name=\"settings\">Instance of <see cref=\"ComparisonSettings\"/> class.</param>\n        /// <returns>true if the objects are considered equal; otherwise, false.</returns>\n        public bool Compare(object obj1, object obj2, ComparisonSettings settings)\n        {\n            return true;\n        }\n\n        /// <summary>\n        /// Converts values of comparing objects to <see cref=\"string\"/>.\n        /// </summary>\n        /// <param name=\"value\">Object to convert.</param>\n        /// <returns>A string that represents <see cref=\"value\"/>.</returns>\n        public string ToString(object value)\n        {\n            return string.Empty;\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/ValueComparers/DynamicValueComparer.cs",
    "content": "﻿using System;\nusing System.Reflection;\n\nnamespace ObjectsComparer\n{\n    /// <summary>\n    /// Allows to provide comparison rule as a function.\n    /// </summary>\n    /// <typeparam name=\"T\">Type of the objects.</typeparam>\n    public class DynamicValueComparer<T>: IValueComparer\n    {\n        private readonly Func<T, T, ComparisonSettings, bool> _compareFunction;\n        private readonly Func<T, string> _toStringFunction;\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"DynamicValueComparer{T}\" /> class. \n        /// </summary>\n        /// <param name=\"compareFunction\">Function to compare objects of type <see cref=\"T\"/>.</param>\n        public DynamicValueComparer(Func<T, T, ComparisonSettings, bool> compareFunction): this(compareFunction, obj => obj?.ToString() ?? string.Empty)\n        {\n            \n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"DynamicValueComparer{T}\" /> class. \n        /// </summary>\n        /// <param name=\"compareFunction\">Function to compare objects of type <see cref=\"T\"/>.</param>\n        /// <param name=\"toStringFunction\">Function to convert objects of type <see cref=\"T\"/> to <see cref=\"string\"/>.</param>\n        public DynamicValueComparer(Func<T, T, ComparisonSettings, bool> compareFunction, Func<T, string> toStringFunction)\n        {\n            _compareFunction = compareFunction ?? throw new ArgumentNullException(nameof(compareFunction));\n            _toStringFunction = toStringFunction ?? throw new ArgumentNullException(nameof(toStringFunction));\n        }\n\n        /// <summary>\n        /// Comparers <paramref name=\"obj1\"/> and <paramref name=\"obj2\"/>.\n        /// </summary>\n        /// <param name=\"obj1\">Object 1.</param>\n        /// <param name=\"obj2\">Object 2.</param>\n        /// <param name=\"settings\">Instance of <see cref=\"ComparisonSettings\"/> class.</param>\n        /// <returns>true if the objects are considered equal; otherwise, false.</returns>\n        public bool Compare(object obj1, object obj2, ComparisonSettings settings)\n        {\n            IsArgumentException(obj1, nameof(obj1));\n            IsArgumentException(obj2, nameof(obj2));\n\n            return _compareFunction((T)obj1, (T)obj2, settings);\n        }\n\n        /// <summary>\n        /// Converts values of comparing objects to <see cref=\"string\"/>.\n        /// </summary>\n        /// <param name=\"value\">Object to convert.</param>\n        /// <returns>A string that represents <see cref=\"value\"/>.</returns>\n        public string ToString(object value)\n        {\n            IsArgumentException(value, nameof(value));\n\n            return _toStringFunction((T)value);\n        }\n\n        // ReSharper disable once UnusedParameter.Local\n        private void IsArgumentException(object obj, string argumentName)\n        {\n            var t = typeof(T).GetTypeInfo();\n            \n            if (!(obj is T) && !((t.IsClass || Nullable.GetUnderlyingType(typeof(T)) != null) && obj == null))\n            {\n                throw new ArgumentException(argumentName);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/ValueComparers/IValueComparer.cs",
    "content": "﻿namespace ObjectsComparer\n{\n    /// <summary>\n    /// Defines a generalized comparison method to compare 2 objects.\n    /// </summary>\n    public interface IValueComparer\n    {\n        /// <summary>\n        /// Comparers <paramref name=\"obj1\"/> and <paramref name=\"obj2\"/>.\n        /// </summary>\n        /// <param name=\"obj1\">Object 1.</param>\n        /// <param name=\"obj2\">Object 2.</param>\n        /// <param name=\"settings\">Instance of <see cref=\"ComparisonSettings\"/> class.</param>\n        /// <returns>true if the objects are considered equal; otherwise, false.</returns>\n        bool Compare(object obj1, object obj2, ComparisonSettings settings);\n\n        /// <summary>\n        /// Converts values of comparing objects to <see cref=\"string\"/>.\n        /// </summary>\n        /// <param name=\"value\">Object to convert.</param>\n        /// <returns>A string that represents <see cref=\"value\"/>.</returns>\n        string ToString(object value);\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/ValueComparers/IValueComparer~1.cs",
    "content": "﻿namespace ObjectsComparer\n{\n    /// <summary>\n    /// Defines a generalized type-specific comparison method to compare 2 objects.\n    /// </summary>\n    /// <typeparam name=\"T\">Type of the objects.</typeparam>\n    public interface IValueComparer<in T>: IValueComparer\n    {\n        /// <summary>\n        /// Comparers <paramref name=\"obj1\"/> and <paramref name=\"obj2\"/>.\n        /// </summary>\n        /// <param name=\"obj1\">Object 1.</param>\n        /// <param name=\"obj2\">Object 2.</param>\n        /// <param name=\"settings\">Instance of <see cref=\"ComparisonSettings\"/> class.</param>\n        /// <returns>true if the objects are considered equal; otherwise, false.</returns>\n        bool Compare(T obj1, T obj2, ComparisonSettings settings);\n\n        /// <summary>\n        /// Converts values of comparing objects to <see cref=\"string\"/>.\n        /// </summary>\n        /// <param name=\"value\">Object to convert.</param>\n        /// <returns>A string that represents <see cref=\"value\"/>.</returns>\n        string ToString(T value);\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/ValueComparers/IgnoreCaseStringsValueComparer.cs",
    "content": "﻿using System;\n\nnamespace ObjectsComparer\n{\n    /// <summary>\n    /// Allows to compare string ignoring case.\n    /// </summary>\n    public class IgnoreCaseStringsValueComparer : AbstractValueComparer<string>\n    {\n        private static volatile IValueComparer _instance;\n        /// <summary>\n        /// Static <see cref=\"IgnoreCaseStringsValueComparer\"/> instance.\n        /// </summary>\n        public static IValueComparer Instance\n        {\n            get\n            {\n                if (_instance != null)\n                {\n                    return _instance;\n                }\n\n                lock (SyncRoot)\n                {\n                    if (_instance == null)\n                    {\n                        _instance = new IgnoreCaseStringsValueComparer();\n                    }\n                }\n\n                return _instance;\n            }\n        }\n\n        private static readonly object SyncRoot = new object();\n\n        /// <summary>\n        /// Comparers <paramref name=\"obj1\"/> and <paramref name=\"obj2\"/>.\n        /// </summary>\n        /// <param name=\"obj1\">Object 1.</param>\n        /// <param name=\"obj2\">Object 2.</param>\n        /// <param name=\"settings\">Instance of <see cref=\"ComparisonSettings\"/> class.</param>\n        /// <returns>true if the objects are considered equal; otherwise, false.</returns>\n        public override bool Compare(string obj1, string obj2, ComparisonSettings settings)\n        {\n            return string.Compare(obj1, obj2, StringComparison.CurrentCultureIgnoreCase) == 0;\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/ValueComparers/NulableStringsValueComparer.cs",
    "content": "﻿namespace ObjectsComparer\n{\n    /// <summary>\n    /// Allows to compare strings considering that null and empty string are equal.\n    /// </summary>\n    public class NulableStringsValueComparer: AbstractValueComparer<string>\n    {\n        private static volatile IValueComparer<string> _instance;\n        /// <summary>\n        /// Static <see cref=\"NulableStringsValueComparer\"/> instance.\n        /// </summary>\n        public static IValueComparer Instance\n        {\n            get\n            {\n                if (_instance != null)\n                {\n                    return _instance;\n                }\n\n                lock (SyncRoot)\n                {\n                    if (_instance == null)\n                    {\n                        _instance = new NulableStringsValueComparer();\n                    }\n                }\n\n                return _instance;\n            }\n        }\n\n        private static readonly object SyncRoot = new object();\n\n        /// <summary>\n        /// Comparers <paramref name=\"obj1\"/> and <paramref name=\"obj2\"/>.\n        /// </summary>\n        /// <param name=\"obj1\">Object 1.</param>\n        /// <param name=\"obj2\">Object 2.</param>\n        /// <param name=\"settings\">Instance of <see cref=\"ComparisonSettings\"/> class.</param>\n        /// <returns>true if the objects are considered equal; otherwise, false.</returns>\n        public override bool Compare(string obj1, string obj2, ComparisonSettings settings)\n        {\n            if (obj1 == null)\n            {\n                obj1 = string.Empty;\n            }\n\n            if (obj2 == null)\n            {\n                obj2 = string.Empty;\n            }\n\n            return obj1.Equals(obj2);\n        }\n\n        public override string ToString(string value)\n        {\n            return value ?? string.Empty;\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/ValueComparers/ToStringComparer.cs",
    "content": "﻿namespace ObjectsComparer\n{\n    public class ToStringComparer<T> : DynamicValueComparer<T>\n    {\n        public ToStringComparer() : \n            base((uri1, uri2, settings) => uri1?.ToString() == uri2?.ToString())\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer/ValueComparers/UriComparer.cs",
    "content": "﻿using System;\n\nnamespace ObjectsComparer\n{\n    public class UriComparer: DynamicValueComparer<Uri>\n    {\n        private static volatile UriComparer _instance;\n        /// <summary>\n        /// Static <see cref=\"UriComparer\"/> instance.\n        /// </summary>\n        public static IValueComparer Instance\n        {\n            get\n            {\n                if (_instance != null)\n                {\n                    return _instance;\n                }\n\n                lock (SyncRoot)\n                {\n                    if (_instance == null)\n                    {\n                        _instance = new UriComparer();\n                    }\n                }\n\n                return _instance;\n            }\n        }\n\n        private static readonly object SyncRoot = new object();\n\n        public UriComparer() : \n            base((uri1, uri2, settings) => uri1.OriginalString == uri2.OriginalString, (uri) => uri.OriginalString)\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Examples/BasicExamples/BasicExampleTests.cs",
    "content": "﻿using System.Collections;\nusing System.Collections.Generic;\nusing System.Dynamic;\nusing System.Globalization;\nusing System.Linq;\nusing System.Text;\nusing NUnit.Framework;\nusing static ObjectsComparer.Examples.OutputHelper;\n\n// ReSharper disable PossibleMultipleEnumeration\nnamespace ObjectsComparer.Examples.BasicExamples\n{\n    [TestFixture]\n    public class BasicExampleTests\n    {\n        #region Basic\n        [Test]\n        public void BasicEquality()\n        {\n            var a1 = new ClassA { StringProperty = \"String\", IntProperty = 1 };\n            var a2 = new ClassA { StringProperty = \"String\", IntProperty = 1 };\n            var comparer = new Comparer<ClassA>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differences);\n\n            ResultToOutput(isEqual, differences);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void BasicInequality()\n        {\n            var a1 = new ClassA { StringProperty = \"String\", IntProperty = 1 };\n            var a2 = new ClassA { StringProperty = \"String\", IntProperty = 2 };\n            var comparer = new Comparer<ClassA>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differences);\n\n            ResultToOutput(isEqual, differences);\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(1, differences.Count());\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"IntProperty\" && d.Value1 == \"1\" && d.Value2 == \"2\"));\n        }\n\n        [Test]\n        public void BasicInequalityWhenSubclass()\n        {\n            var a1 = new ClassA { SubClass = new SubClassA { BoolProperty = true } };\n            var a2 = new ClassA { SubClass = new SubClassA { BoolProperty = false } };\n            var comparer = new Comparer<ClassA>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differences);\n\n            ResultToOutput(isEqual, differences);\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(1, differences.Count());\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"SubClass.BoolProperty\" && d.Value1 == \"True\" && d.Value2 == \"False\"));\n        }\n        #endregion\n\n        #region Generic Enumerables\n        [Test]\n        public void GenericEnumerablesEquality()\n        {\n            var a1 = new[] { 1, 2, 3 };\n            var a2 = new[] { 1, 2, 3 };\n            var comparer = new Comparer<int[]>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differences);\n\n            ResultToOutput(isEqual, differences);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void GenericEnumerablesInequalityWhenDifferentLength()\n        {\n            var a1 = new[] { 1, 2 };\n            var a2 = new[] { 1, 2, 3 };\n            var comparer = new Comparer<int[]>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differences);\n\n            ResultToOutput(isEqual, differences);\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(1, differences.Count());\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Length\" && d.Value1 == \"2\" && d.Value2 == \"3\"));\n        }\n\n        [Test]\n        public void GenericEnumerablesInequalityWhenDifferentValue()\n        {\n            var a1 = new[] { 1, 2, 3 };\n            var a2 = new[] { 1, 4, 3 };\n            var comparer = new Comparer<int[]>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differences);\n\n            ResultToOutput(isEqual, differences);\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(1, differences.Count());\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"[1]\" && d.Value1 == \"2\" && d.Value2 == \"4\"));\n        }\n        #endregion\n\n        #region Hash Sets\n        [Test]\n        public void HashSetsEquality()\n        {\n            var a1 = new HashSet<int>{ 1, 2, 3 };\n            var a2 = new HashSet<int> { 1, 2, 3 };\n            var comparer = new Comparer<HashSet<int>>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differences);\n\n            ResultToOutput(isEqual, differences);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void HashSetsInequality()\n        {\n            var a1 = new HashSet<int> { 1, 2, 3 };\n            var a2 = new HashSet<int> { 2, 1, 4 };\n            var comparer = new Comparer<HashSet<int>>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differences);\n\n            ResultToOutput(isEqual, differences);\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(2, differences.Count());\n            Assert.IsTrue(differences.Any(d => d.DifferenceType == DifferenceTypes.MissedElementInFirstObject && d.Value2 == \"4\"));\n            Assert.IsTrue(differences.Any(d => d.DifferenceType == DifferenceTypes.MissedElementInSecondObject && d.Value1 == \"3\"));\n        }\n        #endregion\n\n        #region String Builder\n        [Test]\n        public void StringBuilderEquality()\n        {\n            var a1 = new StringBuilder(\"abc\");\n            var a2 = new StringBuilder(\"abc\");\n            var comparer = new Comparer<StringBuilder>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differences);\n\n            ResultToOutput(isEqual, differences);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void StringBuilderInequality()\n        {\n            var a1 = new StringBuilder(\"abc\");\n            var a2 = new StringBuilder(\"abd\");\n            var comparer = new Comparer<StringBuilder>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differences);\n\n            ResultToOutput(isEqual, differences);\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(1, differences.Count());\n            Assert.IsTrue(differences.Any(d => d.DifferenceType == DifferenceTypes.ValueMismatch && d.Value1 == \"abc\" && d.Value2 == \"abd\"));\n        }\n        #endregion\n\n        #region Non-Generic Enumerables\n        [Test]\n        public void NonGenericWhenDifferentTypes()\n        {\n            var a1 = new ArrayList { \"Str1\", \"Str2\" };\n            var a2 = new ArrayList { \"Str1\", 5 };\n            var comparer = new Comparer<ArrayList>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differences);\n\n            ResultToOutput(isEqual, differences);\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(\"[1]\", differences.First().MemberPath);\n            Assert.AreEqual(DifferenceTypes.TypeMismatch, differences.First().DifferenceType);\n            Assert.AreEqual(\"Str2\", differences.First().Value1);\n            Assert.AreEqual(\"5\", differences.First().Value2);\n        }\n        #endregion\n\n        #region Multidimensional Arrays\n        [Test]\n        public void MultidimensionalArraysInequality()\n        {\n            var a1 = new[] { new[] { 1, 2 } };\n            var a2 = new[] { new[] { 1, 3 } };\n            var comparer = new Comparer<int[][]>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differences);\n\n            ResultToOutput(isEqual, differences);\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(1, differences.Count());\n            Assert.AreEqual(\"[0][1]\", differences.First().MemberPath);\n            Assert.AreEqual(\"2\", differences.First().Value1);\n            Assert.AreEqual(\"3\", differences.First().Value2);\n        }\n\n        [Test]\n        public void MultidimensionalArraysSizeInequality1()\n        {\n            var a1 = new[] { new[] { 1, 2 } };\n            var a2 = new[] { new[] { 2, 2 }, new[] { 3, 5 } };\n            var comparer = new Comparer<int[][]>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differences);\n\n            ResultToOutput(isEqual, differences);\n\n            Assert.IsFalse(isEqual);\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(1, differences.Count());\n            Assert.AreEqual(\"Length\", differences.First().MemberPath);\n            Assert.AreEqual(\"1\", differences.First().Value1);\n            Assert.AreEqual(\"2\", differences.First().Value2);\n        }\n\n        [Test]\n        public void MultidimensionalArraysSizeInequality2()\n        {\n            var a1 = new[] { new[] { 1, 2 }, new[] { 3, 5 } };\n            var a2 = new[] { new[] { 1, 2 }, new[] { 3, 5, 6 } };\n            var comparer = new Comparer<int[][]>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differences);\n\n            ResultToOutput(isEqual, differences);\n\n            Assert.IsFalse(isEqual);\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(1, differences.Count());\n            Assert.AreEqual(\"[1].Length\", differences.First().MemberPath);\n            Assert.AreEqual(\"2\", differences.First().Value1);\n            Assert.AreEqual(\"3\", differences.First().Value2);\n        }\n\n        [Test]\n        public void IntIntInequality()\n        {\n            var a1 = new[,] { { 1, 2 } };\n            var a2 = new[,] { { 1, 3 } };\n            var comparer = new Comparer<int[,]>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differences);\n\n            ResultToOutput(isEqual, differences);\n\n            Assert.IsFalse(isEqual);\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(1, differences.Count());\n            Assert.AreEqual(\"[0,1]\", differences.First().MemberPath);\n            Assert.AreEqual(\"2\", differences.First().Value1);\n            Assert.AreEqual(\"3\", differences.First().Value2);\n        }\n\n        [Test]\n        public void MultidimensionalArraysSizeInequality3()\n        {\n            var a1 = new[,] { { 1, 2 }, { 1, 3 } };\n            var a2 = new[,] { { 1, 3, 4 }, { 1, 3, 8 } };\n            var comparer = new Comparer<int[,]>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differences);\n\n            ResultToOutput(isEqual, differences);\n\n            Assert.IsFalse(isEqual);\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(1, differences.Count());\n            Assert.AreEqual(\"Dimension1\", differences.First().MemberPath);\n            Assert.AreEqual(\"2\", differences.First().Value1);\n            Assert.AreEqual(\"3\", differences.First().Value2);\n        }\n        #endregion\n\n        #region Dynamic objects (ExpandoObject)\n        [Test]\n        public void ExpandoObject()\n        {\n            dynamic a1 = new ExpandoObject();\n            a1.Field1 = \"A\";\n            a1.Field2 = 5;\n            a1.Field4 = 4;\n            dynamic a2 = new ExpandoObject();\n            a2.Field1 = \"B\";\n            a2.Field3 = false;\n            a2.Field4 = \"C\";\n            var comparer = new Comparer();\n\n            var isEqual = comparer.Compare(a1, a2, out IEnumerable<Difference> differences);\n\n            ResultToOutput(isEqual, differences);\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(4, differences.Count());\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Field1\" && d.Value1 == \"A\" && d.Value2 == \"B\"));\n            Assert.IsTrue(differences.Any(d => d.DifferenceType == DifferenceTypes.MissedMemberInSecondObject && d.MemberPath == \"Field2\" && d.Value1 == \"5\"));\n            Assert.IsTrue(differences.Any(d => d.DifferenceType == DifferenceTypes.MissedMemberInFirstObject && d.MemberPath == \"Field3\" && d.Value2 == \"False\"));\n            Assert.IsTrue(differences.Any(d => d.DifferenceType == DifferenceTypes.TypeMismatch && d.MemberPath == \"Field4\" && d.Value1 == \"4\" && d.Value2 == \"C\"));\n        }\n\n        [Test]\n        public void ExpandoObjectWhenMissedFieldsAndUseDefaults()\n        {\n            dynamic a1 = new ExpandoObject();\n            a1.Field1 = \"A\";\n            a1.Field2 = 0;\n            dynamic a2 = new ExpandoObject();\n            a2.Field1 = \"B\";\n            a2.Field3 = false;\n            a2.Field4 = \"S\";\n            var comparer = new Comparer(new ComparisonSettings { UseDefaultIfMemberNotExist = true });\n\n            var isEqual = comparer.Compare(a1, a2, out IEnumerable<Difference> differences);\n\n            ResultToOutput(isEqual, differences);\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(2, differences.Count());\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Field1\" && d.Value1 == \"A\" && d.Value2 == \"B\"));\n            Assert.IsTrue(differences.Any(d => d.DifferenceType == DifferenceTypes.ValueMismatch && d.MemberPath == \"Field4\" && d.Value2 == \"S\"));\n        }\n        #endregion\n\n        #region Dynamic objects (DynamicObject)\n        private class DynamicDictionary : DynamicObject\n        {\n            // ReSharper disable once UnusedMember.Local\n            public int IntProperty { get; set; }\n\n            private readonly Dictionary<string, object> _dictionary = new Dictionary<string, object>();\n\n            public override bool TryGetMember(GetMemberBinder binder, out object result)\n            {\n                var name = binder.Name;\n\n                return _dictionary.TryGetValue(name, out result);\n            }\n\n            public override bool TrySetMember(SetMemberBinder binder, object value)\n            {\n                _dictionary[binder.Name] = value;\n\n                return true;\n            }\n\n            public override IEnumerable<string> GetDynamicMemberNames()\n            {\n                return _dictionary.Keys;\n            }\n        }\n\n        [Test]\n        public void DynamicObject()\n        {\n            dynamic a1 = new DynamicDictionary();\n            a1.Field1 = \"A\";\n            a1.Field3 = true;\n            dynamic a2 = new DynamicDictionary();\n            a2.Field1 = \"B\";\n            a2.Field2 = 8;\n            a2.Field3 = 1;\n            var comparer = new Comparer();\n\n            var isEqual = comparer.Compare(a1, a2, out IEnumerable<Difference> differences);\n\n            ResultToOutput(isEqual, differences);\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(3, differences.Count());\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Field1\" && d.Value1 == \"A\" && d.Value2 == \"B\"));\n            Assert.IsTrue(differences.Any(d => d.DifferenceType == DifferenceTypes.MissedMemberInFirstObject && d.MemberPath == \"Field2\" && d.Value2 == \"8\"));\n            Assert.IsTrue(differences.Any(d => d.DifferenceType == DifferenceTypes.TypeMismatch && d.MemberPath == \"Field3\" && d.Value1 == \"True\" && d.Value2 == \"1\"));\n        }\n        #endregion\n\n        #region Dynamic objects (compiler generated)\n        [Test]\n        public void CompilerGeneratedDynamicObjects()\n        {\n            dynamic a1 = new\n            {\n                Field1 = \"A\",\n                Field2 = 5,\n                Field3 = true\n            };\n            dynamic a2 = new\n            {\n                Field1 = \"B\",\n                Field2 = 8.0\n            };\n            var comparer = new Comparer();\n\n            var isEqual = comparer.Compare((object)a1, (object)a2, out var differences);\n\n            ResultToOutput(isEqual, differences);\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(3, differences.Count());\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Field1\" && d.Value1 == \"A\" && d.Value2 == \"B\"));\n            Assert.IsTrue(differences.Any(d => d.DifferenceType == DifferenceTypes.TypeMismatch && d.MemberPath == \"Field2\" && d.Value1 == \"5\" && d.Value2 == 8.0.ToString(CultureInfo.InvariantCulture)));\n            Assert.IsTrue(differences.Any(d => d.DifferenceType == DifferenceTypes.MissedMemberInSecondObject && d.MemberPath == \"Field3\" && d.Value1 == \"True\"));\n        }\n        #endregion\n\n        #region Overrides\n        public class MyValueComparer : AbstractValueComparer<string>\n        {\n            public override bool Compare(string obj1, string obj2, ComparisonSettings settings)\n            {\n                return obj1 == obj2; //Implement comparison logic here\n            }\n        }\n        [Test]\n        public void Overrides()\n        {\n            var comparer = new Comparer<ClassA>();\n\n            //  Type\n            //Use MyComparer to compare all members of type string \n            comparer.AddComparerOverride<string>(new MyValueComparer());\n            comparer.AddComparerOverride(typeof(string), new MyValueComparer());\n            //Use MyComparer to compare all members of type string except members which name starts with \"Xyz\"\n            comparer.AddComparerOverride<string>(new MyValueComparer(), member => !member.Name.StartsWith(\"Xyz\"));\n\n            //  Member Info\n            //Use MyValueComparer to compare StringProperty of ClassA\n            //comparer.AddComparerOverride(() => new ClassA().StringProperty, new MyValueComparer());\n            //comparer.AddComparerOverride(\n            //    typeof(ClassA).GetTypeInfo().GetMember(\"StringProperty\").First(),\n            //    new MyValueComparer());\n            //Compare StringProperty of ClassA by length. If length equal consider that values are equal\n            //comparer.AddComparerOverride(\n            //    () => new ClassA().StringProperty,\n            //    (s1, s2, parentSettings) => s1?.Length == s2?.Length,\n            //    s => s?.ToString());\n            //comparer.AddComparerOverride(\n            //    () => new ClassA().StringProperty,\n            //    (s1, s2, parentSettings) => s1?.Length == s2?.Length);\n\n            //  Member filter\n            //comparer.AddComparerOverride(new MyValueComparer(), m => m.Name == \"StringProperty\");\n\n            //  Member Name\n            comparer.AddComparerOverride(\"StringProperty\", new MyValueComparer());\n\n\n            //Exception\n            comparer.AddComparerOverride<string>(new MyValueComparer(), member => member.Name.StartsWith(\"String\"));\n            comparer.AddComparerOverride<string>(DoNotCompareValueComparer.Instance, member => member.Name.EndsWith(\"Property\"));\n\n            //var result = comparer.Compare(a1, a2);//Exception here\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Examples/BasicExamples/ClassA.cs",
    "content": "﻿namespace ObjectsComparer.Examples.BasicExamples\n{\n    public class ClassA\n    {\n        public string StringProperty { get; set; }\n\n        public int IntProperty { get; set; }\n\n        public SubClassA SubClass { get; set; }\n    }\n\n    public class SubClassA\n    {\n        public bool BoolProperty { get; set; }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Examples/Example1/Error.cs",
    "content": "﻿namespace ObjectsComparer.Examples.Example1\n{\n    public class Error\n    {\n        public int Id { get; set; }\n\n        public string Messgae { get; set; }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Examples/Example1/Example1Tests.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing NUnit.Framework;\nusing static ObjectsComparer.Examples.OutputHelper;\n\n// ReSharper disable PossibleMultipleEnumeration\nnamespace ObjectsComparer.Examples.Example1\n{\n    [TestFixture]\n    public class Example1Tests\n    {\n        private IComparer<Message> _comparer;\n\n        [SetUp]\n        public void SetUp()\n        {\n            _comparer = new Comparer<Message>(\n                new ComparisonSettings\n                {\n                    //Null and empty error lists are equal\n                    EmptyAndNullEnumerablesEqual = true\n                });\n            //Do not compare Dates \n            _comparer.AddComparerOverride<DateTime>(DoNotCompareValueComparer.Instance);\n            //Do not compare Id\n            _comparer.AddComparerOverride(() => new Message().Id, DoNotCompareValueComparer.Instance);\n            //Do not compare Message Text\n            _comparer.AddComparerOverride(() => new Error().Messgae, DoNotCompareValueComparer.Instance);\n        }\n\n        [Test]\n        public void EqualMessagesWithoutErrors()\n        {\n            var expectedMessage = new Message\n            {\n                MessageType = 1,\n                Status = 0\n            };\n\n            var actualMessage = new Message\n            {\n                Id = \"M12345\",\n                DateCreated = DateTime.Now,\n                DateReceived = DateTime.Now,\n                DateSent = DateTime.Now,\n                MessageType = 1,\n                Status = 0\n            };\n\n            var isEqual = _comparer.Compare(expectedMessage, actualMessage, out var differences);\n\n            ResultToOutput(isEqual, differences);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void EqualMessagesWithErrors()\n        {\n            var expectedMessage = new Message\n            {\n                MessageType = 1,\n                Status = 1,\n                Errors = new List<Error>\n                {\n                    new Error { Id = 2 },\n                    new Error { Id = 7 }\n                }\n            };\n\n            var actualMessage = new Message\n            {\n                Id = \"M12345\",\n                DateCreated = DateTime.Now,\n                DateReceived = DateTime.Now,\n                DateSent = DateTime.Now,\n                MessageType = 1,\n                Status = 1,\n                Errors = new List<Error>\n                {\n                    new Error { Id = 2, Messgae = \"Some error #2\" },\n                    new Error { Id = 7, Messgae = \"Some error #7\" }\n                }\n            };\n\n            var isEqual = _comparer.Compare(expectedMessage, actualMessage, out var differences);\n\n            ResultToOutput(isEqual, differences);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void UnequalMessages()\n        {\n            var expectedMessage = new Message\n            {\n                MessageType = 1,\n                Status = 1,\n                Errors = new List<Error>\n                {\n                    new Error { Id = 2, Messgae = \"Some error #2\" },\n                    new Error { Id = 8, Messgae = \"Some error #8\" }\n                }\n            };\n\n            var actualMessage = new Message\n            {\n                Id = \"M12345\",\n                DateCreated = DateTime.Now,\n                DateReceived = DateTime.Now,\n                DateSent = DateTime.Now,\n                MessageType = 1,\n                Status = 2,\n                Errors = new List<Error>\n                {\n                    new Error { Id = 2, Messgae = \"Some error #2\" },\n                    new Error { Id = 7, Messgae = \"Some error #7\" }\n                }\n            };\n\n            var isEqual = _comparer.Compare(expectedMessage, actualMessage, out var differences);\n\n            ResultToOutput(isEqual, differences);\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(2, differences.Count());\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Status\" && d.Value1 == \"1\" && d.Value2 == \"2\"));\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Errors[1].Id\" && d.Value1 == \"8\" && d.Value2 == \"7\"));\n        }\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Examples/Example1/Message.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\n\nnamespace ObjectsComparer.Examples.Example1\n{\n    public class Message\n    {\n        public string Id { get; set; }\n\n        public DateTime DateCreated { get; set; }\n\n        public DateTime DateSent { get; set; }\n\n        public DateTime DateReceived { get; set; }\n\n        public int MessageType { get; set; }\n\n        public int Status { get; set; }\n\n        public List<Error> Errors { get; set; }\n\n        public override string ToString()\n        {\n            return $\"Id:{Id}, Type:{MessageType}, Status:{Status}\";\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Examples/Example2/Example2Tests.cs",
    "content": "﻿using System;\nusing System.Linq;\nusing NUnit.Framework;\nusing static ObjectsComparer.Examples.OutputHelper;\n\n// ReSharper disable PossibleMultipleEnumeration\nnamespace ObjectsComparer.Examples.Example2\n{\n    [TestFixture]\n    public class Example2Tests\n    {\n        private MyComparersFactory _factory;\n        private IComparer<Person> _comparer;\n\n        [SetUp]\n        public void SetUp()\n        {\n            _factory = new MyComparersFactory();\n            _comparer = _factory.GetObjectsComparer<Person>();\n        }\n\n        [Test]\n        public void EqualPersonsTest()\n        {\n            var person1 = new Person\n            {\n                PersonId = Guid.NewGuid(),\n                FirstName = \"John\",\n                LastName = \"Doe\",\n                MiddleName = \"F\",\n                PhoneNumber = \"111-555-8888\"\n            };\n            var person2 = new Person\n            {\n                PersonId = Guid.NewGuid(),\n                FirstName = \"John\",\n                LastName = \"Doe\",\n                PhoneNumber = \"(111) 555 8888\"\n            };\n\n            var isEqual = _comparer.Compare(person1, person2, out var differences);\n\n            ResultToOutput(isEqual, differences);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void DifferentPersonsTest()\n        {\n            var person1 = new Person\n            {\n                PersonId = Guid.NewGuid(),\n                FirstName = \"Jack\",\n                LastName = \"Doe\",\n                MiddleName = \"F\",\n                PhoneNumber = \"111-555-8888\"\n            };\n            var person2 = new Person\n            {\n                PersonId = Guid.NewGuid(),\n                FirstName = \"John\",\n                LastName = \"Doe\",\n                MiddleName = \"L\",\n                PhoneNumber = \"222-555-9999\"\n            };\n\n            var isEqual = _comparer.Compare(person1, person2, out var differences);\n\n            ResultToOutput(isEqual, differences);\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(3, differences.Count());\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"FirstName\" && d.Value1 == \"Jack\" && d.Value2 == \"John\"));\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"MiddleName\" && d.Value1 == \"F\" && d.Value2 == \"L\"));\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"PhoneNumber\" && d.Value1 == \"111-555-8888\" && d.Value2 == \"222-555-9999\"));\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Examples/Example2/MyComparersFactory.cs",
    "content": "﻿using System;\n\nnamespace ObjectsComparer.Examples.Example2\n{\n    public class MyComparersFactory : ComparersFactory\n    {\n        public override IComparer<T> GetObjectsComparer<T>(ComparisonSettings settings = null,\n            BaseComparer parentComparer = null)\n        {\n            if (typeof(T) != typeof(Person))\n            {\n                return base.GetObjectsComparer<T>(settings, parentComparer);\n            }\n\n            var comparer = new Comparer<Person>(settings, parentComparer, this);\n            //Do not compare PersonId\n            comparer.AddComparerOverride<Guid>(DoNotCompareValueComparer.Instance);\n            //Sometimes MiddleName can be skipped. Compare only if property has value.\n            comparer.AddComparerOverride(\n                () => new Person().MiddleName,\n                (s1, s2, parentSettings) => string.IsNullOrWhiteSpace(s1) || string.IsNullOrWhiteSpace(s2) || s1 == s2);\n            comparer.AddComparerOverride(\n                () => new Person().PhoneNumber,\n                new PhoneNumberComparer());\n\n            return (IComparer<T>) comparer;\n\n        }\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Examples/Example2/Person.cs",
    "content": "﻿using System;\n\nnamespace ObjectsComparer.Examples.Example2\n{\n    public class Person\n    {\n        public Guid PersonId { get; set; }\n\n        public string FirstName { get; set; }\n\n        public string LastName { get; set; }\n\n        public string MiddleName { get; set; }\n\n        public string PhoneNumber { get; set; }\n\n        public override string ToString()\n        {\n            return $\"{FirstName} {MiddleName} {LastName} ({PhoneNumber})\";\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Examples/Example2/PhoneNumberComparer.cs",
    "content": "﻿using System.Linq;\n\nnamespace ObjectsComparer.Examples.Example2\n{\n    public class PhoneNumberComparer: AbstractValueComparer<string>\n    {\n        public override bool Compare(string obj1, string obj2, ComparisonSettings settings)\n        {\n            return ExtractDigits(obj1) == ExtractDigits(obj2);\n        }\n\n        private string ExtractDigits(string str)\n        {\n            return string.Join(\n                string.Empty, \n                (str ?? string.Empty)\n                    .ToCharArray()\n                    .Where(char.IsDigit));\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Examples/Example3/Example3Tests.cs",
    "content": "﻿using System;\nusing System.Dynamic;\nusing System.IO;\nusing System.Linq;\nusing System.Reflection;\nusing System.Text;\nusing Newtonsoft.Json;\nusing NUnit.Framework;\nusing static ObjectsComparer.Examples.OutputHelper;\n\n// ReSharper disable PossibleMultipleEnumeration\nnamespace ObjectsComparer.Examples.Example3\n{\n    [TestFixture]\n    public class Example3Tests\n    {\n        private Comparer _comparer;\n\n\n        [SetUp]\n        public void SetUp()\n        {\n            _comparer = new Comparer(new ComparisonSettings { UseDefaultIfMemberNotExist = true });\n            //Some fields should be ignored\n            _comparer.AddComparerOverride(\"ConnectionString\", DoNotCompareValueComparer.Instance);\n            _comparer.AddComparerOverride(\"Email\", DoNotCompareValueComparer.Instance);\n            _comparer.AddComparerOverride(\"Notifications\", DoNotCompareValueComparer.Instance);\n            //Smart Modes are disabled by default. These fields are not case sensitive\n            var disabledByDefaultComparer = new DefaultValueValueComparer<string>(\"Disabled\", IgnoreCaseStringsValueComparer.Instance);\n            _comparer.AddComparerOverride(\"SmartMode1\", disabledByDefaultComparer);\n            _comparer.AddComparerOverride(\"SmartMode2\", disabledByDefaultComparer);\n            _comparer.AddComparerOverride(\"SmartMode3\", disabledByDefaultComparer);\n            //http prefix in URLs should be ignored\n            var urlComparer = new DynamicValueComparer<string>(\n                (url1, url2, settings) => url1.Trim('/').Replace(@\"http://\", string.Empty) == url2.Trim('/').Replace(@\"http://\", string.Empty));\n            _comparer.AddComparerOverride(\"SomeUrl\", urlComparer);\n            _comparer.AddComparerOverride(\"SomeOtherUrl\", urlComparer);\n            //DataCompression is Off by default.\n            _comparer.AddComparerOverride(\"DataCompression\", new DefaultValueValueComparer<string>(\"Off\", NulableStringsValueComparer.Instance));\n            //ProcessTaskTimeout and TotalProcessTimeout fields have default values.\n            _comparer.AddComparerOverride(\"ProcessTaskTimeout\", new DefaultValueValueComparer<long>(100, DefaultValueComparer.Instance));\n            _comparer.AddComparerOverride(\"TotalProcessTimeout\", new DefaultValueValueComparer<long>(500, DefaultValueComparer.Instance));\n        }\n\n        [Test]\n        public void Settings1()\n        {\n            var settings0Json = LoadJson(\"Settings0.json\");\n            var settings0 = JsonConvert.DeserializeObject<ExpandoObject>(settings0Json);\n            var settings1Json = LoadJson(\"Settings1.json\");\n            var settings1 = JsonConvert.DeserializeObject<ExpandoObject>(settings1Json);\n\n            var isEqual = _comparer.Compare(settings0, settings1, out var differences);\n\n            ResultToOutput(isEqual, differences);\n            \n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void Settings2()\n        {\n            var settings0Json = LoadJson(\"Settings0.json\");\n            var settings0 = JsonConvert.DeserializeObject<ExpandoObject>(settings0Json);\n            var settings2Json = LoadJson(\"Settings2.json\");\n            var settings2 = JsonConvert.DeserializeObject<ExpandoObject>(settings2Json);\n\n            var isEqual = _comparer.Compare(settings0, settings2, out var differences);\n\n            ResultToOutput(isEqual, differences);\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(8, differences.Count());\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Settings.DataCompression\" && d.Value1 == \"On\" && d.Value2 == \"Off\"));\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Settings.SuperModes.SmartMode1\" && d.Value1 == \"Enabled\" && d.Value2 == \"Disabled\"));\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Timeouts.ProcessTaskTimeout\" && d.Value1 == \"100\" && d.Value2 == \"200\"));\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"BackupSettings.BackupIntervalUnit\" && d.Value1 == \"Day\" && d.Value2 == \"Week\"));\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"BackupSettings.BackupInterval\" && d.Value1 == \"100\" && d.Value2 == \"2\"));\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Logging.Enabled\" && d.Value1 == \"True\" && d.Value2 == \"False\"));\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Logging.MaximumFileSize\" && d.Value1 == \"20MB\" && d.Value2 == \"40MB\"));\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Logging.Level\" && d.Value1 == \"ALL\" && d.Value2 == \"ERROR\"));\n        }\n\n        private string LoadJson(string fileName)\n        {\n            var resourceStream = typeof(Example3Tests).GetTypeInfo()\n                .Assembly.GetManifestResourceStream(\"ObjectsComparer.Examples.Example3.\" + fileName);\n\n            if (resourceStream == null)\n            {\n                throw new Exception($\"Resource '{fileName}' not found\");\n            }\n\n            using (var reader = new StreamReader(resourceStream, Encoding.UTF8))\n            {\n                return reader.ReadToEnd();\n            }\n\n        }\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Examples/Example3/Settings0.json",
    "content": "{\n  \"ConnectionString\": \"USER ID=superuser;PASSWORD=superpassword;DATA SOURCE=localhost:1111\",\n  \"Email\": {\n    \"Port\": 25,\n    \"Host\": \"MyHost.com\",\n    \"EmailAddress\": \"test@MyHost.com\"\n  },\n  \"Settings\": {\n    \"DataCompression\": \"On\",\n    \"DataSourceType\": \"MultiDataSource\",\n    \"SomeUrl\": \"http://MyHost.com/VeryImportantData\",\n    \"SomeOtherUrl\": \"http://MyHost.com/NotSoImportantData/\",\n    \"CacheMode\": \"Memory\",\n    \"MaxCacheSize\": \"1GB\",\n    \"SuperModes\": {\n      \"SmartMode1\": \"Enabled\",\n      \"SmartMode2\": \"Disabled\",\n      \"SmartMode3\": \"Enabled\"\n    }\n  },\n  \"Timeouts\": {\n    \"TotalProcessTimeout\": 500,\n    \"ProcessTaskTimeout\": 100\n  },\n  \"BackupSettings\": {\n    \"BackupIntervalUnit\": \"Day\",\n    \"BackupInterval\": 100\n  },\n  \"Notifications\": [\n    {\n      \"Phone\": \"111-222-3333\"\n    },\n    {\n      \"Phone\": \"111-222-4444\"\n    },\n    {\n      \"EMail\": \"support@MyHost.com\"\n    }\n  ],\n  \"Logging\": {\n    \"Enabled\": true,\n    \"Pattern\": \"Logs\\\\MyApplication.%data{yyyyMMdd}.log\",\n    \"MaximumFileSize\": \"20MB\",\n    \"Level\": \"ALL\"\n  }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Examples/Example3/Settings1.json",
    "content": "{\n  \"ConnectionString\": \"USER ID=admin;PASSWORD=*****;DATA SOURCE=localhost:22222\",\n  \"Email\": {\n    \"Port\": 25,\n    \"Host\": \"MyHost.com\",\n    \"EmailAddress\": \"test@MyHost.com\"\n  },\n  \"Settings\": {\n    \"DataCompression\": \"On\",\n    \"DataSourceType\": \"MultiDataSource\",\n    \"SomeUrl\": \"MyHost.com/VeryImportantData\",\n    \"SomeOtherUrl\": \"MyHost.com/NotSoImportantData/\",\n    \"CacheMode\": \"Memory\",\n    \"MaxCacheSize\": \"1GB\",\n    \"SuperModes\": {\n      \"SmartMode1\": \"enabled\",\n      \"SmartMode3\": \"enabled\"\n    }\n  },\n  \"BackupSettings\": {\n    \"BackupIntervalUnit\": \"Day\",\n    \"BackupInterval\": 100\n  },\n  \"Notifications\": [\n    {\n      \"Phone\": \"111-222-3333\"\n    },\n    {\n      \"EMail\": \"support@MyHost.com\"\n    }\n  ],\n  \"Logging\": {\n    \"Enabled\": true,\n    \"Pattern\": \"Logs\\\\MyApplication.%data{yyyyMMdd}.log\",\n    \"MaximumFileSize\": \"20MB\",\n    \"Level\": \"ALL\"\n  }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Examples/Example3/Settings2.json",
    "content": "{\n  \"ConnectionString\": \"USER ID=superuser;PASSWORD=superpassword;DATA SOURCE=localhost:1111\",\n  \"Email\": {\n    \"Port\": 25,\n    \"Host\": \"MyHost.com\",\n    \"EmailAddress\": \"test@MyHost.com\"\n  },\n  \"Settings\": {\n    \"DataSourceType\": \"MultiDataSource\",\n    \"SomeUrl\": \"http://MyHost.com/VeryImportantData\",\n    \"SomeOtherUrl\": \"http://MyHost.com/NotSoImportantData/\",\n    \"CacheMode\": \"Memory\",\n    \"MaxCacheSize\": \"1GB\",\n    \"SuperModes\": {\n      \"SmartMode3\": \"Enabled\"\n    }\n  },\n  \"Timeouts\": {\n    \"TotalProcessTimeout\": 500,\n    \"ProcessTaskTimeout\": 200\n  },\n  \"BackupSettings\": {\n    \"BackupIntervalUnit\": \"Week\",\n    \"BackupInterval\": 2\n  },\n  \"Notifications\": [\n    {\n      \"EMail\": \"support@MyHost.com\"\n    }\n  ],\n  \"Logging\": {\n    \"Enabled\": false,\n    \"Pattern\": \"Logs\\\\MyApplication.%data{yyyyMMdd}.log\",\n    \"MaximumFileSize\": \"40MB\",\n    \"Level\": \"ERROR\"\n  }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Examples/Example4/CustomFormulaItemsComparer.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Linq;\n\nnamespace ObjectsComparer.Examples.Example4\n{\n    public class CustomFormulaItemsComparer: AbstractComparer<IList<FormulaItem>>\n    {\n        public CustomFormulaItemsComparer(ComparisonSettings settings, BaseComparer parentComparer, IComparersFactory factory) : base(settings, parentComparer, factory)\n        {\n        }\n\n        public override IEnumerable<Difference> CalculateDifferences(IList<FormulaItem> obj1, IList<FormulaItem> obj2)\n        {\n            if (obj1 == null && obj2 == null)\n            {\n                yield break;\n            }\n\n            if (obj1 == null || obj2 == null)\n            {\n                yield return new Difference(\"\", DefaultValueComparer.ToString(obj1) , DefaultValueComparer.ToString(obj2));\n                yield break;\n            }\n\n            if (obj1.Count != obj2.Count)\n            {\n                yield return new Difference(\"Count\", obj1.Count.ToString(), obj2.Count.ToString(),\n                        DifferenceTypes.NumberOfElementsMismatch);\n            }\n\n            foreach (var formulaItem in obj1)\n            {\n                var formulaItem2 = obj2.FirstOrDefault(fi => fi.Id == formulaItem.Id);\n\n                if (formulaItem2 != null)\n                {\n                    var comparer = Factory.GetObjectsComparer<FormulaItem>();\n\n                    foreach (var difference in comparer.CalculateDifferences(formulaItem, formulaItem2))\n                    {\n                        yield return difference.InsertPath($\"[Id={formulaItem.Id}]\");\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Examples/Example4/Example4Tests.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Linq;\nusing NUnit.Framework;\nusing static ObjectsComparer.Examples.OutputHelper;\n\n// ReSharper disable PossibleMultipleEnumeration\nnamespace ObjectsComparer.Examples.Example4\n{\n    [TestFixture]\n    public class Example4Tests\n    {\n        private MyComparersFactory _factory;\n        private IComparer<Formula> _comparer;\n\n        [SetUp]\n        public void SetUp()\n        {\n            _factory = new MyComparersFactory();\n            _comparer = _factory.GetObjectsComparer<Formula>();\n        }\n\n        [Test]\n        public void List_Of_Equal_Sizes_But_Is_Inequality()\n        {\n            var formula1 = new Formula\n            {\n                Id = 1,\n                Name = \"Formula 1\",\n                Items = new List<FormulaItem>\n                {\n                    new FormulaItem\n                    {\n                        Id = 1,\n                        Delay = 60,\n                        Name = \"Item 1\",\n                        Instruction = \"Instruction 1\"\n                    }\n                }\n            };\n\n            var formula2 = new Formula\n            {\n                Id = 1,\n                Name = \"Formula 1\",\n                Items = new List<FormulaItem>\n                {\n                    new FormulaItem\n                    {\n                        Id = 1,\n                        Delay = 80,\n                        Name = \"Item One\",\n                        Instruction = \"Instruction One\"\n                    }\n                }\n            };\n\n            var isEqual = _comparer.Compare(formula1, formula2, out var differences);\n\n            ResultToOutput(isEqual, differences);\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(3, differences.Count());\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Items[Id=1].Delay\" && d.Value1 == \"60\" && d.Value2 == \"80\"));\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Items[Id=1].Name\" && d.Value1 == \"Item 1\" && d.Value2 == \"Item One\"));\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Items[Id=1].Instruction\" && d.Value1 == \"Instruction 1\" && d.Value2 == \"Instruction One\"));\n        }\n\n        [Test]\n        public void List_Of_Different_Sizes_But_Is_Inequality()\n        {\n            var formula1 = new Formula\n            {\n                Id = 1,\n                Name = \"Formula 1\",\n                Items = new List<FormulaItem>\n                {\n                    new FormulaItem\n                    {\n                        Id = 1,\n                        Delay = 60,\n                        Name = \"Item 1\",\n                        Instruction = \"Instruction 1\"\n                    }\n                }\n            };\n\n            var formula2 = new Formula\n            {\n                Id = 1,\n                Name = \"Formula 1\",\n                Items = new List<FormulaItem>\n                {\n                    new FormulaItem\n                    {\n                        Id = 1,\n                        Delay = 80,\n                        Name = \"Item One\",\n                        Instruction = \"Instruction One\"\n                    },\n                    new FormulaItem\n                    {\n                        Id = 2,\n                        Delay = 30,\n                        Name = \"Item Two\",\n                        Instruction = \"Instruction Two\"\n                    }\n                }\n            };\n\n            var isEqual = _comparer.Compare(formula1, formula2, out var differences);\n\n            ResultToOutput(isEqual, differences);\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(4, differences.Count());\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Items.Count\" && d.Value1 == \"1\" && d.Value2 == \"2\"));\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Items[Id=1].Delay\" && d.Value1 == \"60\" && d.Value2 == \"80\"));\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Items[Id=1].Name\" && d.Value1 == \"Item 1\" && d.Value2 == \"Item One\"));\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Items[Id=1].Instruction\" && d.Value1 == \"Instruction 1\" && d.Value2 == \"Instruction One\"));\n        }\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Examples/Example4/Formula.cs",
    "content": "﻿using System.Collections.Generic;\n\nnamespace ObjectsComparer.Examples.Example4\n{\n    public class Formula\n    {\n        public long Id { get; set; }\n        public string Name { get; set; }\n        public IList<FormulaItem> Items { get; set; }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Examples/Example4/FormulaItem.cs",
    "content": "﻿namespace ObjectsComparer.Examples.Example4\n{\n    public class FormulaItem\n    {\n        public long Id { get; set; }\n        public int Delay { get; set; }\n        public string Name { get; set; }\n        public string Instruction { get; set; }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Examples/Example4/MyComparersFactory.cs",
    "content": "﻿using System.Collections.Generic;\n\nnamespace ObjectsComparer.Examples.Example4\n{\n    public class MyComparersFactory : ComparersFactory\n    {\n        public override IComparer<T> GetObjectsComparer<T>(ComparisonSettings settings = null,\n            BaseComparer parentComparer = null)\n        {\n            if (typeof(T) != typeof(IList<FormulaItem>))\n            {\n                return base.GetObjectsComparer<T>(settings, parentComparer);\n            }\n\n            var comparer = new CustomFormulaItemsComparer(settings, parentComparer, this);\n            \n            return (IComparer<T>) comparer;\n\n        }\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Examples/Example5/Error.cs",
    "content": "﻿namespace ObjectsComparer.Examples.Example5\n{\n    public class Error\n    {\n        [Ignore]\n        public int Id { get; set; }\n\n        public string Messgae { get; set; }\n\n        [Ignore]\n        public string Details { get; set; }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Examples/Example5/Example5Tests.cs",
    "content": "﻿using NUnit.Framework;\n\nnamespace ObjectsComparer.Examples.Example5\n{\n    [TestFixture]\n    public class Example5Tests\n    {\n        [Test]\n        public void IgnoreByAttribute()\n        {\n            var error1 = new Error\n            {\n                Id = 1,\n                Messgae = \"Error Message\",\n                Details = \"Error details\"\n            };\n\n            var error2 = new Error\n            {\n                Id = 2,\n                Messgae = \"Error Message\",\n                Details = \"Other error details\"\n            };\n\n            var comparer = new Comparer<Error>();\n            comparer.IgnoreMember(m => m.GetCustomAttributes(typeof(IgnoreAttribute), true).Length > 0);\n\n            var isEqual = comparer.Compare(error1, error2);\n\n            Assert.IsTrue(isEqual);\n        }\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Examples/Example5/IgnoreAttribute.cs",
    "content": "﻿using System;\n\nnamespace ObjectsComparer.Examples.Example5\n{\n    [AttributeUsage(AttributeTargets.Property)]\n    public class IgnoreAttribute: Attribute\n    {\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Examples/ObjectsComparer.Examples.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFrameworks>netcoreapp2.0;net45</TargetFrameworks>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <None Remove=\"Example3\\Settings0.json\" />\n    <None Remove=\"Example3\\Settings1.json\" />\n    <None Remove=\"Example3\\Settings2.json\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <EmbeddedResource Include=\"Example3\\Settings0.json\">\n      <CopyToOutputDirectory>Never</CopyToOutputDirectory>\n    </EmbeddedResource>\n    <EmbeddedResource Include=\"Example3\\Settings1.json\">\n      <CopyToOutputDirectory>Never</CopyToOutputDirectory>\n    </EmbeddedResource>\n    <EmbeddedResource Include=\"Example3\\Settings2.json\">\n      <CopyToOutputDirectory>Never</CopyToOutputDirectory>\n    </EmbeddedResource>\n  </ItemGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Microsoft.CSharp\" Version=\"4.5.0\" />\n    <PackageReference Include=\"Microsoft.NET.Test.Sdk\" Version=\"15.8.0\" />\n    <PackageReference Include=\"Newtonsoft.Json\" Version=\"11.0.2\" />\n    <PackageReference Include=\"NUnit\" Version=\"3.10.1\" />\n    <PackageReference Include=\"NUnit3TestAdapter\" Version=\"3.10.0\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\ObjectsComparer\\ObjectsComparer.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Examples/OutputHelper.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\n\n\nnamespace ObjectsComparer.Examples\n{\n    public static class OutputHelper\n    {\n        public static void ResultToOutput(bool isEqual, IEnumerable<Difference> differenses)\n        {\n            Debug.WriteLine(isEqual ? \"Objects are equal\" : string.Join(Environment.NewLine, differenses));\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/AbstractComparerGenericTests.cs",
    "content": "﻿using System.Collections.Generic;\nusing NSubstitute;\nusing NUnit.Framework;\n\nnamespace ObjectsComparer.Tests\n{\n    [TestFixture]\n    public class AbstractComparerGenericTests\n    {\n        private BaseComparer _parentComparerMock;\n        private IComparersFactory _factoryMock;\n        private AbstractComparer<int> _comparer;\n\n        [SetUp]\n        public void SetUp()\n        {\n            _factoryMock = Substitute.For<IComparersFactory>();\n            _parentComparerMock = Substitute.ForPartsOf<BaseComparer>(new ComparisonSettings(), null, _factoryMock);\n            _comparer =\n                Substitute.ForPartsOf<AbstractComparer<int>>(new ComparisonSettings(), _parentComparerMock, _factoryMock);\n        }\n\n        [Test]\n        public void CalculateDifferences()\n        {\n            _comparer.CalculateDifferences(1, 2);\n\n            _comparer.Received().CalculateDifferences(1, 2);\n        }\n\n        [Test]\n        public void CompareWithOutParameterWhenNotEqual()\n        {\n            var differences = new List<Difference> { new Difference(\"\", \"1\", \"2\") };\n            _comparer.CalculateDifferences(1, 2).Returns(differences);\n\n            var result = _comparer.Compare(1, 2, out var outDifferences);\n\n            Assert.IsFalse(result);\n            _comparer.Received().CalculateDifferences(1, 2);\n            Assert.AreEqual(differences, outDifferences);\n        }\n\n        [Test]\n        public void CompareWithOutParameterWhenEqual()\n        {\n            var differences = new List<Difference>();\n            _comparer.CalculateDifferences(1, 1).Returns(differences);\n\n            var result = _comparer.Compare(1, 1, out var outDifferences);\n\n            Assert.IsTrue(result);\n            _comparer.Received().CalculateDifferences(1, 1);\n            Assert.AreEqual(differences, outDifferences);\n        }\n\n        [Test]\n        public void CompareWithoutOutParameterWhenNotEqual()\n        {\n            var differences = new List<Difference> { new Difference(\"\", \"1\", \"2\") };\n            _comparer.CalculateDifferences(1, 2).Returns(differences);\n\n            var result = _comparer.Compare(1, 2);\n\n            Assert.IsFalse(result);\n            _comparer.Received().CalculateDifferences(1, 2);\n        }\n\n        [Test]\n        public void CompareWithoutOutParameterWhenEqual()\n        {\n            _comparer.CalculateDifferences(1, 2).Returns(new List<Difference>());\n\n            var result = _comparer.Compare(1, 2);\n\n            Assert.IsTrue(result);\n            _comparer.Received().CalculateDifferences(1, 2);\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/AbstractComparerTests.cs",
    "content": "﻿using System.Collections.Generic;\nusing NSubstitute;\nusing NUnit.Framework;\n\nnamespace ObjectsComparer.Tests\n{\n    [TestFixture]\n    public class AbstractComparerTests\n    {\n        private BaseComparer _parentComparerMock;\n        private IComparersFactory _factoryMock;\n        private AbstractComparer _comparer;\n\n        [SetUp]\n        public void SetUp()\n        {\n            _factoryMock = Substitute.For<IComparersFactory>();\n            _parentComparerMock = Substitute.ForPartsOf<BaseComparer>(new ComparisonSettings(), null, _factoryMock);\n            _comparer =\n                Substitute.ForPartsOf<AbstractComparer>(new ComparisonSettings(), _parentComparerMock, _factoryMock);\n        }\n\n        [Test]\n        public void CalculateDifferences()\n        {\n            _comparer.CalculateDifferences(\"string1\", \"string2\");\n\n            _comparer.Received().CalculateDifferences(typeof(string), \"string1\", \"string2\");\n        }\n\n        [Test]\n        public void CompareWithOutParameterWhenNotEqual()\n        {\n            var differences = new List<Difference> {new Difference(\"\", \"string1\", \"string2\")};\n            _comparer.CalculateDifferences(typeof(string), \"string1\", \"string2\").Returns(differences);\n\n            var result = _comparer.Compare(\"string1\", \"string2\", out var outDifferences);\n\n            Assert.IsFalse(result);\n            _comparer.Received().CalculateDifferences(typeof(string), \"string1\", \"string2\");\n            Assert.AreEqual(differences, outDifferences);\n        }\n\n        [Test]\n        public void CompareWithOutParameterWhenEqual()\n        {\n            var differences = new List<Difference>();\n            _comparer.CalculateDifferences(typeof(string), \"string1\", \"string1\").Returns(differences);\n\n            var result = _comparer.Compare(\"string1\", \"string1\", out var outDifferences);\n\n            Assert.IsTrue(result);\n            _comparer.Received().CalculateDifferences(typeof(string), \"string1\", \"string1\");\n            Assert.AreEqual(differences, outDifferences);\n        }\n\n        [Test]\n        public void CompareWithoutOutParameterWhenNotEqual()\n        {\n            var differences = new List<Difference> {new Difference(\"\", \"string1\", \"string2\")};\n            _comparer.CalculateDifferences(typeof(string), \"string1\", \"string2\").Returns(differences);\n\n            var result = _comparer.Compare(\"string1\", \"string2\");\n\n            Assert.IsFalse(result);\n            _comparer.Received().CalculateDifferences(typeof(string), \"string1\", \"string2\");\n        }\n\n        [Test]\n        public void CompareWithoutOutParameterWhenEqual()\n        {\n            _comparer.CalculateDifferences(typeof(string), \"string1\", \"string2\").Returns(new List<Difference>());\n\n            var result = _comparer.Compare(\"string1\", \"string2\");\n\n            Assert.IsTrue(result);\n            _comparer.Received().CalculateDifferences(typeof(string), \"string1\", \"string2\");\n        }\n\n        [Test]\n        public void NonGenericCompareWithOutParameterWhenNotEqual()\n        {\n            var differences = new List<Difference> {new Difference(\"\", \"string1\", \"string2\")};\n            _comparer.CalculateDifferences(typeof(string), \"string1\", \"string2\").Returns(differences);\n\n            var result = _comparer.Compare(typeof(string), \"string1\", \"string2\", out var outDifferences);\n\n            Assert.IsFalse(result);\n            _comparer.Received().CalculateDifferences(typeof(string), \"string1\", \"string2\");\n            Assert.AreEqual(differences, outDifferences);\n        }\n\n        [Test]\n        public void NonGenericCompareWithOutParameterWhenEqual()\n        {\n            var differences = new List<Difference>();\n            _comparer.CalculateDifferences(typeof(string), \"string1\", \"string1\").Returns(differences);\n\n            var result = _comparer.Compare(typeof(string), \"string1\", \"string1\", out var outDifferences);\n\n            Assert.IsTrue(result);\n            _comparer.Received().CalculateDifferences(typeof(string), \"string1\", \"string1\");\n            Assert.AreEqual(differences, outDifferences);\n        }\n\n        [Test]\n        public void NonGenericCompareWithoutOutParameterWhenNotEqual()\n        {\n            var differences = new List<Difference> {new Difference(\"\", \"string1\", \"string2\")};\n            _comparer.CalculateDifferences(typeof(string), \"string1\", \"string2\").Returns(differences);\n\n            var result = _comparer.Compare(typeof(string), \"string1\", \"string2\");\n\n            Assert.IsFalse(result);\n            _comparer.Received().CalculateDifferences(typeof(string), \"string1\", \"string2\");\n        }\n\n        [Test]\n        public void NonGenericCompareWithoutOutParameterWhenEqual()\n        {\n            _comparer.CalculateDifferences(typeof(string), \"string1\", \"string2\").Returns(new List<Difference>());\n\n            var result = _comparer.Compare(typeof(string), \"string1\", \"string2\");\n\n            Assert.IsTrue(result);\n            _comparer.Received().CalculateDifferences(typeof(string), \"string1\", \"string2\");\n        }\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/ComparerNonGenericTests.cs",
    "content": "﻿using System;\nusing System.Linq;\nusing NUnit.Framework;\nusing ObjectsComparer.Tests.TestClasses;\n\nnamespace ObjectsComparer.Tests\n{\n    [TestFixture]\n    public class ComparerNonGenericTests\n    {\n        [Test]\n        public void PropertyEquality()\n        {\n            var a1 = new A { IntProperty = 10, DateTimeProperty = new DateTime(2017, 1, 1), Property3 = 5 };\n            var a2 = new A { IntProperty = 10, DateTimeProperty = new DateTime(2017, 1, 1), Property3 = 8 };\n            var comparer = new Comparer();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void PropertyInequality()\n        {\n            var date1 = new DateTime(2017, 1, 1);\n            var date2 = new DateTime(2017, 1, 2);\n            var a1 = new A { IntProperty = 10, DateTimeProperty = date1 };\n            var a2 = new A { IntProperty = 8, DateTimeProperty = date2 };\n            var comparer = new Comparer();\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(\"IntProperty\", differences[0].MemberPath);\n            Assert.AreEqual(\"10\", differences[0].Value1);\n            Assert.AreEqual(\"8\", differences[0].Value2);\n            Assert.AreEqual(\"DateTimeProperty\", differences[1].MemberPath);\n            // ReSharper disable once SpecifyACultureInStringConversionExplicitly\n            Assert.AreEqual(date1.ToString(), differences[1].Value1);\n            // ReSharper disable once SpecifyACultureInStringConversionExplicitly\n            Assert.AreEqual(date2.ToString(), differences[1].Value2);\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/ComparerOverridesCollectionTests.cs",
    "content": "﻿using System;\nusing System.Reflection;\nusing NSubstitute;\nusing NUnit.Framework;\nusing ObjectsComparer.Exceptions;\n\nnamespace ObjectsComparer.Tests\n{\n    [TestFixture]\n    public class ComparerOverridesCollectionTests\n    {\n        [Test]\n        public void AddComparerByMemberInfoWhenMemberNull()\n        {\n            var valueComparer = Substitute.For<IValueComparer>();\n            var collection = new ComparerOverridesCollection();\n\n            // ReSharper disable once RedundantCast\n            Assert.Throws<ArgumentNullException>(() => collection.AddComparer((MemberInfo)null, valueComparer));\n        }\n\n        [Test]\n        public void AddComparerByMemberInfoWhenComparerNull()\n        {\n            var memberInfo = Substitute.ForPartsOf<MemberInfo>();\n            var collection = new ComparerOverridesCollection();\n\n            Assert.Throws<ArgumentNullException>(() => collection.AddComparer(memberInfo, null));\n        }\n\n        [Test]\n        public void AddComparerByMemberInfoWhenExists()\n        {\n            var memberInfo = Substitute.ForPartsOf<MemberInfo>();\n            var valueComparer = Substitute.For<IValueComparer>();\n            var collection = new ComparerOverridesCollection();\n            collection.AddComparer(memberInfo, valueComparer);\n\n            var exception = Assert.Throws<ValueComparerExistsException>(() => collection.AddComparer(memberInfo, valueComparer));\n            Assert.AreEqual(memberInfo, exception.MemberInfo);\n        }\n\n        [Test]\n        public void GetOverrideByMemberInfoComparer()\n        {\n            var memberInfo1 = Substitute.ForPartsOf<PropertyInfo>();\n            memberInfo1.PropertyType.Returns(typeof(string));\n            var valueComparer1 = Substitute.For<IValueComparer>();\n            var memberInfo2 = Substitute.ForPartsOf<PropertyInfo>();\n            memberInfo2.PropertyType.Returns(typeof(string));\n            var valueComparer2 = Substitute.For<IValueComparer>();\n            var collection = new ComparerOverridesCollection();\n            collection.AddComparer(memberInfo1, valueComparer1);\n            collection.AddComparer(memberInfo2, valueComparer2);\n\n            var valueComparer1FromCollection = collection.GetComparer(memberInfo1);\n            var valueComparer2FromCollection = collection.GetComparer(memberInfo2);\n\n            Assert.AreEqual(valueComparer1, valueComparer1FromCollection);\n            Assert.AreEqual(valueComparer2, valueComparer2FromCollection);\n        }\n\n        [Test]\n        public void GetComparerByMemberInfoWhenNotExists()\n        {\n            var memberInfo1 = Substitute.ForPartsOf<PropertyInfo>();\n            memberInfo1.PropertyType.Returns(typeof(string));\n            var valueComparer1 = Substitute.For<IValueComparer>();\n            var memberInfo2 = Substitute.ForPartsOf<PropertyInfo>();\n            memberInfo2.PropertyType.Returns(typeof(string));\n            var collection = new ComparerOverridesCollection();\n            collection.AddComparer(memberInfo1, valueComparer1);\n\n            var valueComparer2FromCollection = collection.GetComparer(memberInfo2);\n\n            Assert.IsNull(valueComparer2FromCollection);\n        }\n\n        [Test]\n        public void AddComparerByTypeWhenTypeNull()\n        {\n            var valueComparer = Substitute.For<IValueComparer>();\n            var collection = new ComparerOverridesCollection();\n\n            Assert.Throws<ArgumentNullException>(() => collection.AddComparer((Type)null, valueComparer));\n        }\n\n        [Test]\n        public void AddComparerByTypeWhenComparerNull()\n        {\n            var collection = new ComparerOverridesCollection();\n\n            Assert.Throws<ArgumentNullException>(() => collection.AddComparer(typeof(string), null));\n        }\n\n        [Test]\n        public void GetOverrideByTypeComparerByMemberInfo()\n        {\n            var valueComparer = Substitute.For<IValueComparer>();\n            var collection = new ComparerOverridesCollection();\n            var memberInfo = Substitute.ForPartsOf<PropertyInfo>();\n            memberInfo.PropertyType.Returns(typeof(string));\n            collection.AddComparer(typeof(string), valueComparer);\n\n            var valueComparerFromCollection = collection.GetComparer(memberInfo);\n\n            Assert.AreEqual(valueComparer, valueComparerFromCollection);\n        }\n\n        [Test]\n        public void GetOverrideByTypeComparerByType()\n        {\n            var valueComparer = Substitute.For<IValueComparer>();\n            var collection = new ComparerOverridesCollection();\n            var memberInfo = Substitute.ForPartsOf<PropertyInfo>();\n            memberInfo.PropertyType.Returns(typeof(string));\n            collection.AddComparer(typeof(string), valueComparer);\n\n            var valueComparerFromCollection = collection.GetComparer(typeof(string));\n\n            Assert.AreEqual(valueComparer, valueComparerFromCollection);\n        }\n\n        [Test]\n        public void GetComparerByTypeWhenNull()\n        {\n            var collection = new ComparerOverridesCollection();\n\n            Assert.Throws<ArgumentNullException>(() => collection.GetComparer((Type)null));\n        }\n\n        [Test]\n        public void GetOverrideByTypeComparerWhenTwoComparersMatchCriteria()\n        {\n            var valueComparer1 = Substitute.For<IValueComparer>();\n            var valueComparer2 = Substitute.For<IValueComparer>();\n            var collection = new ComparerOverridesCollection();\n            var memberInfo = Substitute.ForPartsOf<PropertyInfo>();\n            memberInfo.PropertyType.Returns(typeof(string));\n            memberInfo.Name.Returns(\"Prop1\");\n            collection.AddComparer(typeof(string), valueComparer1);\n            collection.AddComparer(typeof(string), valueComparer2, mi => mi.Name == \"Prop1\");\n\n            var exception = Assert.Throws<AmbiguousComparerOverrideResolutionException>(() => collection.GetComparer(memberInfo));\n            Assert.AreEqual(memberInfo, exception.MemberInfo);\n            Assert.AreEqual(\"Prop1\", exception.MemberName);\n        }\n\n        [Test]\n        public void GetOverrideByTypeComparerWhenOneComparerMatchCriteria()\n        {\n            var valueComparer1 = Substitute.For<IValueComparer>();\n            var valueComparer2 = Substitute.For<IValueComparer>();\n            var collection = new ComparerOverridesCollection();\n            var memberInfo = Substitute.ForPartsOf<PropertyInfo>();\n            memberInfo.PropertyType.Returns(typeof(string));\n            memberInfo.Name.Returns(\"Prop1\");\n            collection.AddComparer(typeof(string), valueComparer1, mi => mi.Name == \"Prop1\");\n            collection.AddComparer(typeof(string), valueComparer2, mi => mi.Name == \"Prop2\");\n\n            var valueComparerFromCollection = collection.GetComparer(memberInfo);\n\n            Assert.AreEqual(valueComparer1, valueComparerFromCollection);\n        }\n\n        [Test]\n        public void GetOverrideByTypeComparerWhenNoComparerMatchCriteria()\n        {\n            var valueComparer1 = Substitute.For<IValueComparer>();\n            var valueComparer2 = Substitute.For<IValueComparer>();\n            var collection = new ComparerOverridesCollection();\n            var memberInfo = Substitute.ForPartsOf<PropertyInfo>();\n            memberInfo.PropertyType.Returns(typeof(string));\n            memberInfo.Name.Returns(\"Prop1\");\n            collection.AddComparer(typeof(string), valueComparer1, mi => false);\n            collection.AddComparer(typeof(string), valueComparer2, mi => false);\n\n            var valueComparerFromCollection = collection.GetComparer(memberInfo);\n\n            Assert.IsNull(valueComparerFromCollection);\n        }\n\n        [Test]\n        public void GetOverrideByTypeWhenOnlyOneComparerDoNotHaveFilter()\n        {\n            var valueComparer1 = Substitute.For<IValueComparer>();\n            var valueComparer2 = Substitute.For<IValueComparer>();\n            var collection = new ComparerOverridesCollection();\n            collection.AddComparer(typeof(string), valueComparer1, mi => true);\n            collection.AddComparer(typeof(string), valueComparer2);\n\n            var valueComparerFromCollection = collection.GetComparer(typeof(string));\n\n            Assert.AreEqual(valueComparer2, valueComparerFromCollection);\n        }\n\n        [Test]\n        public void GetOverrideByTypeWhenMoreThanOneComparerMatchCriteria()\n        {\n            var valueComparer1 = Substitute.For<IValueComparer>();\n            var valueComparer2 = Substitute.For<IValueComparer>();\n            var collection = new ComparerOverridesCollection();\n            collection.AddComparer(typeof(string), valueComparer1);\n            collection.AddComparer(typeof(string), valueComparer2);\n\n            var exception = Assert.Throws<AmbiguousComparerOverrideResolutionException>(() => collection.GetComparer(typeof(string)));\n            Assert.AreEqual(typeof(string), exception.Type);\n        }\n\n        [Test]\n        public void GetOverrideByTypeWhenNoComparerMatchCriteria()\n        {\n            var valueComparer1 = Substitute.For<IValueComparer>();\n            var collection = new ComparerOverridesCollection();\n            collection.AddComparer(typeof(string), valueComparer1);\n\n            var valueComparerFromCollection = collection.GetComparer(typeof(int));\n\n            Assert.IsNull(valueComparerFromCollection);\n        }\n\n        [Test]\n        public void GetOverrideByTypeWhenAllComparersHaveFilters()\n        {\n            var valueComparer1 = Substitute.For<IValueComparer>();\n            var valueComparer2 = Substitute.For<IValueComparer>();\n            var collection = new ComparerOverridesCollection();\n            collection.AddComparer(typeof(string), valueComparer1, mi => true);\n            collection.AddComparer(typeof(string), valueComparer2, mi => true);\n\n            var valueComparerFromCollection = collection.GetComparer(typeof(string));\n\n            Assert.IsNull(valueComparerFromCollection);\n        }\n\n        [TestCase(null)]\n        [TestCase(\"\")]\n        [TestCase(\"  \")]\n        public void AddComparerByNameWhenNameEmpty(string memberName)\n        {\n            var valueComparer = Substitute.For<IValueComparer>();\n            var collection = new ComparerOverridesCollection();\n\n            Assert.Throws<ArgumentException>(() => collection.AddComparer(memberName, valueComparer));\n        }\n\n        [Test]\n        public void AddComparerByNameWhenComparerNull()\n        {\n            var collection = new ComparerOverridesCollection();\n\n            Assert.Throws<ArgumentNullException>(() => collection.AddComparer(\"Name\", null));\n        }\n\n        [Test]\n        public void GetOverrideByNameComparerByMemberInfo()\n        {\n            var valueComparer = Substitute.For<IValueComparer>();\n            var collection = new ComparerOverridesCollection();\n            var memberInfo = Substitute.ForPartsOf<PropertyInfo>();\n            memberInfo.PropertyType.Returns(typeof(string));\n            memberInfo.Name.Returns(\"Prop1\");\n            collection.AddComparer(\"Prop1\", valueComparer);\n\n            var valueComparerFromCollection = collection.GetComparer(memberInfo);\n\n            Assert.AreEqual(valueComparer, valueComparerFromCollection);\n        }\n\n        [Test]\n        public void GetOverrideByNameComparerByMemberInfoWhenTwoComparersMatchCriteria()\n        {\n            var valueComparer1 = Substitute.For<IValueComparer>();\n            var valueComparer2 = Substitute.For<IValueComparer>();\n            var collection = new ComparerOverridesCollection();\n            var memberInfo = Substitute.ForPartsOf<PropertyInfo>();\n            memberInfo.PropertyType.Returns(typeof(string));\n            memberInfo.Name.Returns(\"Prop1\");\n            collection.AddComparer(\"Prop1\", valueComparer1);\n            collection.AddComparer(\"Prop1\", valueComparer2, mi => true);\n\n            Assert.Throws<AmbiguousComparerOverrideResolutionException>(() => collection.GetComparer(memberInfo));\n        }\n\n        [Test]\n        public void GetOverrideByNameComparerByMemberInfoWhenNoComparerMatchCriteria()\n        {\n            var valueComparer1 = Substitute.For<IValueComparer>();\n            var valueComparer2 = Substitute.For<IValueComparer>();\n            var collection = new ComparerOverridesCollection();\n            var memberInfo = Substitute.ForPartsOf<PropertyInfo>();\n            memberInfo.PropertyType.Returns(typeof(string));\n            memberInfo.Name.Returns(\"Prop1\");\n            collection.AddComparer(\"Prop1\", valueComparer1, mi => false);\n            collection.AddComparer(\"Prop1\", valueComparer2, mi => false);\n\n            var valueComparerFromCollection = collection.GetComparer(memberInfo);\n\n            Assert.IsNull(valueComparerFromCollection);\n        }\n\n        [Test]\n        public void GetOverrideByNameComparerByMemberInfoWhenOneComparerMatchCriteria()\n        {\n            var valueComparer1 = Substitute.For<IValueComparer>();\n            var valueComparer2 = Substitute.For<IValueComparer>();\n            var collection = new ComparerOverridesCollection();\n            var memberInfo = Substitute.ForPartsOf<PropertyInfo>();\n            memberInfo.PropertyType.Returns(typeof(string));\n            memberInfo.Name.Returns(\"Prop1\");\n            collection.AddComparer(\"Prop1\", valueComparer1, mi => false);\n            collection.AddComparer(\"Prop1\", valueComparer2, mi => true);\n\n            var valueComparerFromCollection = collection.GetComparer(memberInfo);\n\n            Assert.AreEqual(valueComparer2, valueComparerFromCollection);\n        }\n\n        [Test]\n        public void GetComparerByMemberInfoWhenNull()\n        {\n            var collection = new ComparerOverridesCollection();\n\n            Assert.Throws<ArgumentNullException>(() => collection.GetComparer((MemberInfo)null));\n        }\n\n        [Test]\n        public void GetOverrideByNameComparerByMemberName()\n        {\n            var valueComparer = Substitute.For<IValueComparer>();\n            var collection = new ComparerOverridesCollection();\n            collection.AddComparer(\"Prop1\", valueComparer);\n\n            var valueComparerFromCollection = collection.GetComparer(\"Prop1\");\n\n            Assert.AreEqual(valueComparer, valueComparerFromCollection);\n        }\n\n        [Test]\n        public void GetOverrideByNameComparerByMemberNameWhenTwoComparersMatchCriteria()\n        {\n            var valueComparer1 = Substitute.For<IValueComparer>();\n            var valueComparer2 = Substitute.For<IValueComparer>();\n            var collection = new ComparerOverridesCollection();\n            collection.AddComparer(\"Prop1\", valueComparer1);\n            collection.AddComparer(\"Prop1\", valueComparer2);\n\n            Assert.Throws<AmbiguousComparerOverrideResolutionException>(() => collection.GetComparer(\"Prop1\"));\n        }\n\n        [Test]\n        public void GetOverrideByNameComparerByMemberNameWhenNoComparerMatchCriteria()\n        {\n            var valueComparer1 = Substitute.For<IValueComparer>();\n            var valueComparer2 = Substitute.For<IValueComparer>();\n            var collection = new ComparerOverridesCollection();\n            collection.AddComparer(\"Prop1\", valueComparer1, mi => false);\n            collection.AddComparer(\"Prop1\", valueComparer2, mi => false);\n\n            var valueComparerFromCollection = collection.GetComparer(\"Prop1\");\n\n            Assert.IsNull(valueComparerFromCollection);\n        }\n\n        [Test]\n        public void GetComparerByMemberNameWhenNull()\n        {\n            var collection = new ComparerOverridesCollection();\n\n            Assert.Throws<ArgumentNullException>(() => collection.GetComparer((string)null));\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/ComparerTests.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing NSubstitute;\nusing NUnit.Framework;\nusing ObjectsComparer.Tests.TestClasses;\n\nnamespace ObjectsComparer.Tests\n{\n    [TestFixture]\n    public class ComparerTests\n    {\n        [Test]\n        public void PropertyEquality()\n        {\n            var a1 = new A {IntProperty = 10, DateTimeProperty = new DateTime(2017, 1, 1), Property3 = 5};\n            var a2 = new A {IntProperty = 10, DateTimeProperty = new DateTime(2017, 1, 1), Property3 = 8};\n            var comparer = new Comparer<A>();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void PropertyInequality()\n        {\n            var date1 = new DateTime(2017, 1, 1);\n            var date2 = new DateTime(2017, 1, 2);\n            var a1 = new A {IntProperty = 10, DateTimeProperty = date1};\n            var a2 = new A {IntProperty = 8, DateTimeProperty = date2};\n            var comparer = new Comparer<A>();\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(\"IntProperty\", differences[0].MemberPath);\n            Assert.AreEqual(\"10\", differences[0].Value1);\n            Assert.AreEqual(\"8\", differences[0].Value2);\n            Assert.AreEqual(\"DateTimeProperty\", differences[1].MemberPath);\n            // ReSharper disable once SpecifyACultureInStringConversionExplicitly\n            Assert.AreEqual(date1.ToString(), differences[1].Value1);\n            // ReSharper disable once SpecifyACultureInStringConversionExplicitly\n            Assert.AreEqual(date2.ToString(), differences[1].Value2);\n        }\n\n        [Test]\n        public void ReadOnlyPropertyEquality()\n        {\n            var a1 = new A(1.99);\n            var a2 = new A(1.99);\n            var comparer = new Comparer<A>();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void ReadOnlyPropertyInequality()\n        {\n            var a1 = new A(1.99);\n            var a2 = new A(0.89);\n            var comparer = new Comparer<A>();\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(\"ReadOnlyProperty\", differences.First().MemberPath);\n            Assert.AreEqual(\"1.99\", differences.First().Value1);\n            Assert.AreEqual(\"0.89\", differences.First().Value2);\n        }\n\n        [Test]\n        public void ProtectedProperty()\n        {\n            var a1 = new A(true);\n            var a2 = new A(false);\n            var comparer = new Comparer<A>();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void FieldEquality()\n        {\n            var a1 = new A {Field = 9};\n            var a2 = new A {Field = 9};\n            var comparer = new Comparer<A>();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void FieldInequality()\n        {\n            var a1 = new A {Field = 10};\n            var a2 = new A {Field = 8};\n            var comparer = new Comparer<A>();\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(\"Field\", differences.First().MemberPath);\n            Assert.AreEqual(\"10\", differences.First().Value1);\n            Assert.AreEqual(\"8\", differences.First().Value2);\n        }\n\n        [Test]\n        public void ReadOnlyFieldEquality()\n        {\n            var a1 = new A(\"Str1\");\n            var a2 = new A(\"Str1\");\n            var comparer = new Comparer<A>();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void ReadOnlyFieldInequality()\n        {\n            var a1 = new A(\"Str1\");\n            var a2 = new A(\"Str2\");\n            var comparer = new Comparer<A>();\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(\"ReadOnlyField\", differences.First().MemberPath);\n            Assert.AreEqual(\"Str1\", differences.First().Value1);\n            Assert.AreEqual(\"Str2\", differences.First().Value2);\n        }\n\n        [Test]\n        public void ProtectedField()\n        {\n            var a1 = new A(5);\n            var a2 = new A(6);\n            var comparer = new Comparer<A>();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void ClassPropertyEquality()\n        {\n            var a1 = new A {ClassB = new B {Property1 = \"Str1\"}};\n            var a2 = new A {ClassB = new B {Property1 = \"Str1\"}};\n            var comparer = new Comparer<A>();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void ClassPropertyInequality()\n        {\n            var a1 = new A {ClassB = new B {Property1 = \"Str1\"}};\n            var a2 = new A {ClassB = new B {Property1 = \"Str2\"}};\n            var comparer = new Comparer<A>();\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(\"ClassB.Property1\", differences.First().MemberPath);\n            Assert.AreEqual(\"Str1\", differences.First().Value1);\n            Assert.AreEqual(\"Str2\", differences.First().Value2);\n        }\n\n        [Test]\n        public void ClassPropertyInequalityFirstNull()\n        {\n            var a1 = new A();\n            var a2 = new A {ClassB = new B {Property1 = \"Str2\"}};\n            var comparer = new Comparer<A>();\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(\"ClassB\", differences.First().MemberPath);\n            Assert.AreEqual(\"\", differences.First().Value1);\n            Assert.AreEqual(a2.ClassB.ToString(), differences.First().Value2);\n        }\n\n        [Test]\n        public void ClassPropertyInequalitySecondNull()\n        {\n            var a1 = new A {ClassB = new B {Property1 = \"Str2\"}};\n            var a2 = new A();\n            var comparer = new Comparer<A>();\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(\"ClassB\", differences.First().MemberPath);\n            Assert.AreEqual(a1.ClassB.ToString(), differences.First().Value1);\n            Assert.AreEqual(\"\", differences.First().Value2);\n        }\n\n        [Test]\n        public void NoRecursiveComparison()\n        {\n            var a1 = new A {ClassB = new B {Property1 = \"Str1\"}};\n            var a2 = new A {ClassB = new B {Property1 = \"Str2\"}};\n            var comparer = new Comparer<A>(new ComparisonSettings {RecursiveComparison = false});\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void InterfacePropertyEquality()\n        {\n            var a1 = new A {IntefaceProperty = new TestInterfaceImplementation1 {Property = \"Str1\"}};\n            var a2 = new A\n            {\n                IntefaceProperty = new TestInterfaceImplementation2 {Property = \"Str1\", AnotherProperty = 50}\n            };\n            var comparer = new Comparer<A>();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void InterfacePropertyInequality()\n        {\n            var a1 = new A {IntefaceProperty = new TestInterfaceImplementation1 {Property = \"Str1\"}};\n            var a2 = new A\n            {\n                IntefaceProperty = new TestInterfaceImplementation2 {Property = \"Str2\", AnotherProperty = 50}\n            };\n            var comparer = new Comparer<A>();\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(\"IntefaceProperty.Property\", differences.First().MemberPath);\n            Assert.AreEqual(\"Str1\", differences.First().Value1);\n            Assert.AreEqual(\"Str2\", differences.First().Value2);\n        }\n\n        [Test]\n        public void StructPropertyEquality()\n        {\n            var a1 = new A {StructProperty = new TestStruct {FieldA = \"FA\", FieldB = \"FB\"}};\n            var a2 = new A {StructProperty = new TestStruct {FieldA = \"FA\", FieldB = \"FB\"}};\n            var comparer = new Comparer<A>();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void StructPropertyInequality()\n        {\n            var a1 = new A {StructProperty = new TestStruct {FieldA = \"FA\", FieldB = \"FB\"}};\n            var a2 = new A {StructProperty = new TestStruct {FieldA = \"FA\", FieldB = \"FBB\"}};\n            var comparer = new Comparer<A>();\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(\"StructProperty.FieldB\", differences.First().MemberPath);\n            Assert.AreEqual(\"FB\", differences.First().Value1);\n            Assert.AreEqual(\"FBB\", differences.First().Value2);\n        }\n\n        [Test]\n        public void EnumPropertyEquality()\n        {\n            var a1 = new A {EnumProperty = TestEnum.Value1};\n            var a2 = new A {EnumProperty = TestEnum.Value1};\n            var comparer = new Comparer<A>();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void EnumPropertyInequality()\n        {\n            var a1 = new A {EnumProperty = TestEnum.Value1};\n            var a2 = new A {EnumProperty = TestEnum.Value2};\n            var comparer = new Comparer<A>();\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(\"EnumProperty\", differences.First().MemberPath);\n            Assert.AreEqual(\"Value1\", differences.First().Value1);\n            Assert.AreEqual(\"Value2\", differences.First().Value2);\n        }\n\n        [Test]\n        public void SetDefaultComparer()\n        {\n            var a1 = new A {TestProperty1 = \"ABC\", Field = 5};\n            var a2 = new A {TestProperty1 = \"BCD\", Field = 6};\n            var comparer = new Comparer<A>();\n            var valueComparer = Substitute.For<IValueComparer>();\n            valueComparer.Compare(Arg.Any<object>(), Arg.Any<object>(), Arg.Any<ComparisonSettings>()).Returns(true);\n            comparer.SetDefaultComparer(valueComparer);\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n            valueComparer.Received().Compare(Arg.Any<object>(), Arg.Any<object>(), Arg.Any<ComparisonSettings>());\n        }\n\n        [Test]\n        public void SetDefaultComparerNullException()\n        {\n            var comparer = new Comparer<A>();\n\n            Assert.Throws<ArgumentNullException>(() => comparer.SetDefaultComparer(null));\n        }\n\n        [Test]\n        public void InheritedAndBaseClassInequality()\n        {\n            var a1 = new A {ClassB = new B {Property1 = \"Str1\"}};\n            var a2 = new A {ClassB = new InheritedFromB {Property1 = \"Str2\", NewProperty = \"SomeValue\"}};\n            var comparer = new Comparer<A>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(1, differences.Count);\n            Assert.AreEqual(\"ClassB.Property1\", differences.First().MemberPath);\n            Assert.AreEqual(\"Str1\", differences.First().Value1);\n            Assert.AreEqual(\"Str2\", differences.First().Value2);\n        }\n\n        [Test]\n        public void InheritedAndBaseClassEquality()\n        {\n            var a1 = new A {ClassB = new B {Property1 = \"Str1\"}};\n            var a2 = new A {ClassB = new InheritedFromB {Property1 = \"Str1\", NewProperty = \"SomeValue\"}};\n            var comparer = new Comparer<A>();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [TestCase(FlagsEnum.Flag1 | FlagsEnum.Flag2, FlagsEnum.Flag1 | FlagsEnum.Flag3)]\n        [TestCase(FlagsEnum.Flag2, FlagsEnum.Flag3)]\n        [TestCase(FlagsEnum.Flag1, FlagsEnum.Flag1 | FlagsEnum.Flag2)]\n        public void FlagsInequality(FlagsEnum flags1, FlagsEnum flags2)\n        {\n            var a1 = new A {Flags = flags1};\n            var a2 = new A {Flags = flags2};\n            var comparer = new Comparer<A>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(1, differences.Count);\n            Assert.AreEqual(\"Flags\", differences.First().MemberPath);\n            Assert.AreEqual(flags1.ToString(), differences.First().Value1);\n            Assert.AreEqual(flags2.ToString(), differences.First().Value2);\n        }\n\n        [TestCase(FlagsEnum.Flag1 | FlagsEnum.Flag2, FlagsEnum.Flag1 | FlagsEnum.Flag2)]\n        [TestCase(FlagsEnum.Flag2, FlagsEnum.Flag2)]\n        public void FlagsEquality(FlagsEnum flags1, FlagsEnum flags2)\n        {\n            var a1 = new A {Flags = flags1};\n            var a2 = new A {Flags = flags2};\n            var comparer = new Comparer<A>();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void TypePropertyEquality()\n        {\n            var a1 = new A {TypeProperty = typeof(string)};\n            var a2 = new A {TypeProperty = typeof(string)};\n            var comparer = new Comparer<A>();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void TypePropertyInequality()\n        {\n            var a1 = new A {TypeProperty = typeof(string)};\n            var a2 = new A {TypeProperty = typeof(int)};\n            var comparer = new Comparer<A>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(1, differences.Count);\n            Assert.AreEqual(\"TypeProperty\", differences.First().MemberPath);\n        }\n\n        [Test]\n        public void TimeSpanEquality()\n        {\n            var a1 = new TimeSpan(123456789);\n            var a2 = new TimeSpan(123456789);\n            var comparer = new Comparer<TimeSpan>();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void TimeSpanInequality()\n        {\n            var a1 = new TimeSpan(123456789);\n            var a2 = new TimeSpan(123456788);\n            var comparer = new Comparer<TimeSpan>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(1, differences.Count);\n            Assert.AreEqual(string.Empty, differences.First().MemberPath);\n        }\n\n        [Test]\n        public void GuidEquality()\n        {\n            var a1 = new Guid(\"01234567890123456789012345678912\");\n            var a2 = new Guid(\"01234567890123456789012345678912\");\n            var comparer = new Comparer<Guid>();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void GuidInequality()\n        {\n            var a1 = new Guid(\"01234567890123456789012345678912\");\n            var a2 = new Guid(\"01234567890123456789012345678913\");\n            var comparer = new Comparer<Guid>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(1, differences.Count);\n            Assert.AreEqual(string.Empty, differences.First().MemberPath);\n        }\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/Comparer_CompilerGeneratedObjectsTests.cs",
    "content": "﻿using System.Linq;\nusing NUnit.Framework;\nusing NSubstitute;\n\nnamespace ObjectsComparer.Tests\n{\n    [TestFixture]\n    public class ComparerCompilerGeneratedObjectComparerObjectsTests\n    {\n        [Test]\n        public void DifferentValues()\n        {\n            dynamic a1 = new\n            {\n                Field1 = \"A\",\n                Field2 = 5,\n                Field3 = true\n            };\n            dynamic a2 = new\n            {\n                Field1 = \"B\",\n                Field2 = 8,\n                Field3 = false\n            };\n            var comparer = new Comparer();\n\n            var isEqual = comparer.Compare((object)a1, (object)a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(3, differences.Count);\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Field1\" && d.Value1 == \"A\" && d.Value2 == \"B\"));\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Field2\" && d.Value1 == \"5\" && d.Value2 == \"8\"));\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Field3\" && d.Value1 == \"True\" && d.Value2 == \"False\"));\n        }\n\n        [Test]\n        public void MissedFields()\n        {\n            dynamic a1 = new\n            {\n                Field1 = \"A\",\n                Field2 = 5\n            };\n            dynamic a2 = new\n            {\n                Field1 = \"B\",\n                Field2 = 8,\n                Field3 = false\n            };\n            var comparer = new Comparer();\n\n            var isEqual = comparer.Compare((object)a1, (object)a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(3, differences.Count);\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Field1\" && d.Value1 == \"A\" && d.Value2 == \"B\"));\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Field2\" && d.Value1 == \"5\" && d.Value2 == \"8\"));\n            Assert.IsTrue(differences.Any(d => d.DifferenceType == DifferenceTypes.MissedMemberInFirstObject && d.MemberPath == \"Field3\" && d.Value2 == \"False\"));\n        }\n\n        [Test]\n        public void MissedFieldsAndUseDefaults()\n        {\n            dynamic a1 = new\n            {\n                Field1 = \"A\",\n                Field2 = 5\n            };\n            dynamic a2 = new\n            {\n                Field1 = \"B\",\n                Field3 = false,\n                Field4 = \"S\"\n            };\n            var comparer = new Comparer(new ComparisonSettings { UseDefaultIfMemberNotExist = true });\n\n            var isEqual = comparer.Compare((object)a1, (object)a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(3, differences.Count);\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Field1\" && d.Value1 == \"A\" && d.Value2 == \"B\"));\n            Assert.IsTrue(differences.Any(d => d.DifferenceType == DifferenceTypes.ValueMismatch && d.MemberPath == \"Field2\" && d.Value1 == \"5\" && d.Value2 == \"0\"));\n            Assert.IsTrue(differences.Any(d => d.DifferenceType == DifferenceTypes.ValueMismatch && d.MemberPath == \"Field4\" && d.Value2 == \"S\"));\n        }\n\n        [Test]\n        public void Hierarchy()\n        {\n            dynamic a1Sub1 = new { FieldSub1 = 10 };\n            dynamic a1 = new { Field1 = a1Sub1 };\n            dynamic a2Sub1 = new { FieldSub1 = 8 };\n            dynamic a2 = new { Field1 = a2Sub1 };\n            var comparer = new Comparer();\n\n            var isEqual = comparer.Compare((object)a1, (object)a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(1, differences.Count);\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Field1.FieldSub1\" && d.Value1 == \"10\" && d.Value2 == \"8\"));\n        }\n\n        [Test]\n        public void DifferentTypes()\n        {\n            dynamic a1 = new\n            {\n                Field1 = \"A\",\n                Field2 = 5\n            };\n            dynamic a2 = new\n            {\n                Field1 = 5,\n                Field2 = \"5\"\n            };\n            var comparer = new Comparer();\n\n            var isEqual = comparer.Compare((object)a1, (object)a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(2, differences.Count);\n            Assert.IsTrue(differences.Any(\n                d => d.MemberPath == \"Field1\" && d.Value1 == \"A\" && d.Value2 == \"5\" && d.DifferenceType == DifferenceTypes.TypeMismatch));\n            Assert.IsTrue(differences.Any(\n                d => d.MemberPath == \"Field2\" && d.Value1 == \"5\" && d.Value2 == \"5\" && d.DifferenceType == DifferenceTypes.TypeMismatch));\n        }\n\n        [Test]\n        public void NullsAreEqual()\n        {\n            dynamic a1 = new\n            {\n                Field1 = (object)null\n            };\n            dynamic a2 = new\n            {\n                Field1 = (object)null\n            };\n            var comparer = new Comparer();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void NullAndMissedMemberAreNotEqual()\n        {\n            dynamic a1 = new\n            {\n                Field1 = (object)null\n            };\n            dynamic a2 = new\n            {\n                Field2 = (object)null\n            };\n            var comparer = new Comparer();\n\n            var isEqual = comparer.Compare((object)a1, (object)a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(2, differences.Count);\n            Assert.IsTrue(differences.Any(\n                d => d.MemberPath == \"Field1\" && d.DifferenceType == DifferenceTypes.MissedMemberInSecondObject));\n            Assert.IsTrue(differences.Any(\n                d => d.MemberPath == \"Field2\" && d.DifferenceType == DifferenceTypes.MissedMemberInFirstObject));\n        }\n\n        [Test]\n        public void NullValues()\n        {\n            dynamic a1 = new\n            {\n                Field1 = \"A\",\n                Field2 = (object)null\n            };\n            dynamic a2 = new\n            {\n                Field1 = (object)null,\n                Field2 = \"B\"\n            };\n            var comparer = new Comparer();\n\n            var isEqual = comparer.Compare((object)a1, (object)a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(2, differences.Count);\n            Assert.IsTrue(differences.Any(\n                d => d.MemberPath == \"Field1\" && d.Value1 == \"A\" && d.DifferenceType == DifferenceTypes.ValueMismatch));\n            Assert.IsTrue(differences.Any(\n                d => d.MemberPath == \"Field2\" && d.Value2 == \"B\" && d.DifferenceType == DifferenceTypes.ValueMismatch));\n        }\n\n        [Test]\n        public void ComparerOverrideWhenEqual()\n        {\n            dynamic a1 = new\n            {\n                Field1 = \"A\"\n            };\n            dynamic a2 = new\n            {\n                Field1 = \" A \"\n            };\n            var stringComparer = Substitute.For<IValueComparer>();\n            stringComparer.Compare(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<ComparisonSettings>()).Returns(true);\n            var comparer = new Comparer();\n            comparer.AddComparerOverride(\"Field1\", stringComparer);\n\n            var isEqual = comparer.Compare((object)a1, (object)a2);\n\n            Assert.IsTrue(isEqual);\n            stringComparer.Received().Compare(\"A\", \" A \", Arg.Any<ComparisonSettings>());\n        }\n\n        [Test]\n        public void ComparerOverrideWhenNotEqual()\n        {\n            dynamic a1 = new\n            {\n                Field1 = \"A\"\n            };\n            dynamic a2 = new\n            {\n                Field1 = \"B\"\n            };\n            var stringComparer = Substitute.For<IValueComparer>();\n            stringComparer.Compare(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<ComparisonSettings>()).Returns(false);\n            stringComparer.ToString(Arg.Any<object>()).Returns(callInfo => callInfo.Arg<object>()?.ToString());\n            var comparer = new Comparer();\n            comparer.AddComparerOverride(\"Field1\", stringComparer);\n\n            var isEqual = comparer.Compare((object)a1, (object)a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            stringComparer.Received().Compare(\"A\", \"B\", Arg.Any<ComparisonSettings>());\n            Assert.AreEqual(1, differences.Count);\n            Assert.IsTrue(differences.Any(\n                d => d.MemberPath == \"Field1\" && d.Value1 == \"A\" && d.Value2 == \"B\" && d.DifferenceType == DifferenceTypes.ValueMismatch));\n        }\n\n        [Test]\n        public void ComparerOverrideWhenNullAndValueType()\n        {\n            dynamic a1 = new\n            {\n                Field1 = (object)null\n            };\n            dynamic a2 = new\n            {\n                Field1 = 5\n            };\n            var comparer = new Comparer();\n            var intComparer = Substitute.For<IValueComparer>();\n            intComparer.Compare(Arg.Any<object>(), Arg.Any<object>(), Arg.Any<ComparisonSettings>()).Returns(false);\n            intComparer.ToString(5).Returns(\"5\");\n            comparer.AddComparerOverride<int>(intComparer);\n\n            var isEqual = comparer.Compare((object)a1, (object)a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(1, differences.Count);\n            Assert.IsTrue(differences.Any(\n                d => d.MemberPath == \"Field1\" && d.Value1 == string.Empty && d.Value2 == \"5\" && d.DifferenceType == DifferenceTypes.TypeMismatch));\n        }\n\n        [Test]\n        public void UseDefaultValuesWhenSubclassNotSpecified()\n        {\n            dynamic a1 = new\n            {\n                Field1 = new\n                {\n                    SubField1 = 0,\n                    SubField2 = (object) null,\n                    SubField3 = 0.0\n                }\n            };\n            dynamic a2 = new {};\n            var comparer = new Comparer(new ComparisonSettings { UseDefaultIfMemberNotExist = true });\n\n            var isEqual = comparer.Compare((object)a1, (object)a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void DifferenceWhenSubclassNotSpecified()\n        {\n            dynamic a1 = new\n            {\n                Field1 = new\n                {\n                    SubField1 = 0,\n                    SubField2 = (object)null,\n                    SubField3 = 0.0\n                }\n            };\n            dynamic a2 = new { };\n            var comparer = new Comparer();\n\n            var isEqual = comparer.Compare((object)a1, (object)a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(1, differences.Count);\n            Assert.IsTrue(differences.Any(\n                d => d.MemberPath == \"Field1\" && d.DifferenceType == DifferenceTypes.MissedMemberInSecondObject));\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/Comparer_DynamicObjectsTests.cs",
    "content": "﻿using System.Linq;\nusing NUnit.Framework;\nusing System.Collections.Generic;\nusing System.Dynamic;\nusing NSubstitute;\n\nnamespace ObjectsComparer.Tests\n{\n    [TestFixture]\n    public class ComparerDynamicObjectsTests\n    {\n        private class DynamicDictionary : DynamicObject\n        {\n            // ReSharper disable once UnusedMember.Local\n            public int IntProperty { get; set; }\n\n            private readonly Dictionary<string, object> _dictionary = new Dictionary<string, object>();\n            \n            public override bool TryGetMember(GetMemberBinder binder, out object result)\n            {\n                var name = binder.Name;\n\n                return _dictionary.TryGetValue(name, out result);\n            }\n\n            public override bool TrySetMember(SetMemberBinder binder, object value)\n            {\n                _dictionary[binder.Name] = value;\n\n                return true;\n            }\n\n            public override IEnumerable<string> GetDynamicMemberNames()\n            {\n                return _dictionary.Keys;\n            }\n        }\n\n\n        [Test]\n        public void DifferentValues()\n        {\n            dynamic a1 = new DynamicDictionary();\n            a1.Field1 = \"A\";\n            a1.Field2 = 5;\n            a1.Field3 = true;\n            dynamic a2 = new DynamicDictionary();\n            a2.Field1 = \"B\";\n            a2.Field2 = 8;\n            a2.Field3 = false;\n\n            var comparer = new Comparer();\n\n            var isEqual = comparer.Compare(a1, a2, out IEnumerable<Difference> differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(3, differences.Count);\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Field1\" && d.Value1 == \"A\" && d.Value2 == \"B\"));\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Field2\" && d.Value1 == \"5\" && d.Value2 == \"8\"));\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Field3\" && d.Value1 == \"True\" && d.Value2 == \"False\"));\n        }\n\n        [Test]\n        public void MissedFields()\n        {\n            dynamic a1 = new DynamicDictionary();\n            a1.Field1 = \"A\";\n            a1.Field2 = 5;\n            dynamic a2 = new DynamicDictionary();\n            a2.Field1 = \"B\";\n            a2.Field3 = false;\n            var comparer = new Comparer();\n\n            var isEqual = comparer.Compare(a1, a2, out IEnumerable<Difference> differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(3, differences.Count);\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Field1\" && d.Value1 == \"A\" && d.Value2 == \"B\"));\n            Assert.IsTrue(differences.Any(d => d.DifferenceType == DifferenceTypes.MissedMemberInSecondObject && d.MemberPath == \"Field2\" && d.Value1 == \"5\"));\n            Assert.IsTrue(differences.Any(d => d.DifferenceType == DifferenceTypes.MissedMemberInFirstObject && d.MemberPath == \"Field3\" && d.Value2 == \"False\"));\n        }\n\n        [Test]\n        public void MissedFieldsAndUseDefaults()\n        {\n            dynamic a1 = new DynamicDictionary();\n            a1.Field1 = \"A\";\n            a1.Field2 = 5;\n            dynamic a2 = new DynamicDictionary();\n            a2.Field1 = \"B\";\n            a2.Field3 = false;\n            a2.Field4 = \"S\";\n            var comparer = new Comparer(new ComparisonSettings { UseDefaultIfMemberNotExist = true });\n\n            var isEqual = comparer.Compare(a1, a2, out IEnumerable<Difference> differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(3, differences.Count);\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Field1\" && d.Value1 == \"A\" && d.Value2 == \"B\"));\n            Assert.IsTrue(differences.Any(d => d.DifferenceType == DifferenceTypes.ValueMismatch && d.MemberPath == \"Field2\" && d.Value1 == \"5\" && d.Value2 == \"0\"));\n            Assert.IsTrue(differences.Any(d => d.DifferenceType == DifferenceTypes.ValueMismatch && d.MemberPath == \"Field4\" && d.Value2 == \"S\"));\n        }\n\n        [Test]\n        public void Hierarchy()\n        {\n            dynamic a1Sub1 = new DynamicDictionary();\n            a1Sub1.Field1 = 10;\n            dynamic a1 = new DynamicDictionary();\n            a1.FieldSub1 = a1Sub1;\n            dynamic a2Sub1 = new DynamicDictionary();\n            a2Sub1.Field1 = 8;\n            dynamic a2 = new DynamicDictionary();\n            a2.FieldSub1 = a2Sub1;\n            var comparer = new Comparer();\n\n            var isEqual = comparer.Compare(a1, a2, out IEnumerable<Difference> differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(1, differences.Count);\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"FieldSub1.Field1\" && d.Value1 == \"10\" && d.Value2 == \"8\"));\n        }\n\n        [Test]\n        public void DifferentTypes()\n        {\n            dynamic a1 = new DynamicDictionary();\n            a1.Field1 = \"A\";\n            a1.Field2 = 5;\n            dynamic a2 = new DynamicDictionary();\n            a2.Field1 = 5;\n            a2.Field2 = \"5\";\n            var comparer = new Comparer();\n\n            var isEqual = comparer.Compare(a1, a2, out IEnumerable<Difference> differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(2, differences.Count);\n            Assert.IsTrue(differences.Any(\n                d => d.MemberPath == \"Field1\" && d.DifferenceType == DifferenceTypes.TypeMismatch));\n            Assert.IsTrue(differences.Any(\n                d => d.MemberPath == \"Field2\" && d.DifferenceType == DifferenceTypes.TypeMismatch));\n        }\n\n        [Test]\n        public void NullsAreEqual()\n        {\n            dynamic a1 = new DynamicDictionary();\n            a1.Field1 = null;\n            dynamic a2 = new DynamicDictionary();\n            a2.Field1 = null;\n            var comparer = new Comparer();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void NullAndMissedMemberAreNotEqual()\n        {\n            dynamic a1 = new DynamicDictionary();\n            a1.Field1 = null;\n            dynamic a2 = new DynamicDictionary();\n            a2.Field2 = null;\n            var comparer = new Comparer();\n\n            var isEqual = comparer.Compare(a1, a2, out IEnumerable<Difference> differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(2, differences.Count);\n            Assert.IsTrue(differences.Any(\n                d => d.MemberPath == \"Field1\" && d.DifferenceType == DifferenceTypes.MissedMemberInSecondObject));\n            Assert.IsTrue(differences.Any(\n                d => d.MemberPath == \"Field2\" && d.DifferenceType == DifferenceTypes.MissedMemberInFirstObject));\n        }\n\n        [Test]\n        public void NullValues()\n        {\n            dynamic a1 = new DynamicDictionary();\n            a1.Field1 = \"A\";\n            a1.Field2 = null;\n            dynamic a2 = new DynamicDictionary();\n            a2.Field1 = null;\n            a2.Field2 = \"B\";\n            var comparer = new Comparer();\n\n            var isEqual = comparer.Compare(a1, a2, out IEnumerable<Difference> differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(2, differences.Count);\n            Assert.IsTrue(differences.Any(\n                d => d.MemberPath == \"Field1\" && d.Value1 == \"A\" && d.DifferenceType == DifferenceTypes.ValueMismatch));\n            Assert.IsTrue(differences.Any(\n                d => d.MemberPath == \"Field2\" && d.Value2 == \"B\" && d.DifferenceType == DifferenceTypes.ValueMismatch));\n        }\n\n        [Test]\n        public void ComparerOverrideWhenEqual()\n        {\n            dynamic a1 = new DynamicDictionary();\n            a1.Field1 = \"A\";\n            dynamic a2 = new DynamicDictionary();\n            a2.Field1 = \" A \";\n            var stringComparer = Substitute.For<IValueComparer>();\n            stringComparer.Compare(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<ComparisonSettings>()).Returns(true);\n            var comparer = new Comparer();\n            comparer.AddComparerOverride(\"Field1\", stringComparer);\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n            stringComparer.Received().Compare(\"A\", \" A \", Arg.Any<ComparisonSettings>());\n        }\n\n        [Test]\n        public void ComparerOverrideWhenNotEqual()\n        {\n            dynamic a1 = new DynamicDictionary();\n            a1.Field1 = \"A\";\n            dynamic a2 = new DynamicDictionary();\n            a2.Field1 = \"B\";\n            var stringComparer = Substitute.For<IValueComparer>();\n            stringComparer.Compare(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<ComparisonSettings>()).Returns(false);\n            stringComparer.ToString(Arg.Any<string>()).Returns(callInfo => callInfo.Arg<string>());\n            var comparer = new Comparer();\n            comparer.AddComparerOverride(\"Field1\", stringComparer);\n\n            var isEqual = comparer.Compare(a1, a2, out IEnumerable<Difference> differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            stringComparer.Received().Compare(\"A\", \"B\", Arg.Any<ComparisonSettings>());\n            Assert.AreEqual(1, differences.Count);\n            Assert.IsTrue(differences.Any(\n                d => d.MemberPath == \"Field1\" && d.Value1 == \"A\" && d.Value2 == \"B\" && d.DifferenceType == DifferenceTypes.ValueMismatch));\n        }\n\n        [Test]\n        public void ComparerOverrideWhenNullAndValueType()\n        {\n            dynamic a1 = new DynamicDictionary();\n            a1.Field1 = null;\n            dynamic a2 = new DynamicDictionary();\n            a2.Field1 = 5.0;\n            var comparer = new Comparer();\n            var doubleComparer = Substitute.For<IValueComparer>();\n            doubleComparer.Compare(Arg.Any<object>(), Arg.Any<object>(), Arg.Any<ComparisonSettings>()).Returns(false);\n            doubleComparer.ToString(5.0).Returns(\"5.0\");\n            comparer.AddComparerOverride<double>(doubleComparer);\n\n            var isEqual = comparer.Compare(a1, a2, out IEnumerable<Difference> differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(1, differences.Count);\n            Assert.IsTrue(differences.Any(\n                d => d.MemberPath == \"Field1\" && d.Value1 == string.Empty && d.Value2 == \"5.0\" && d.DifferenceType == DifferenceTypes.TypeMismatch));\n        }\n\n        [Test]\n        public void ComperaNonDynamicProperty()\n        {\n            dynamic a1 = new DynamicDictionary();\n            a1.IntProperty = 5;\n            dynamic a2 = new DynamicDictionary();\n            a2.IntProperty = 7;\n            var comparer = new Comparer();\n\n            var isEqual = comparer.Compare(a1, a2, out IEnumerable<Difference> differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(1, differences.Count);\n            Assert.IsTrue(differences.Any(\n                d => d.MemberPath == \"IntProperty\" && d.Value1 == \"5\" && d.Value2 == \"7\" && d.DifferenceType == DifferenceTypes.ValueMismatch));\n        }\n\n        [Test]\n        public void UseDefaultValuesWhenSubclassNotSpecified()\n        {\n            dynamic a1 = new DynamicDictionary();\n            a1.Field1 = new DynamicDictionary();\n            a1.Field1.SubField1 = 0;\n            a1.Field1.SubField2 = null;\n            a1.Field1.SubField3 = 0.0;\n            dynamic a2 = new DynamicDictionary();\n            var comparer = new Comparer(new ComparisonSettings { UseDefaultIfMemberNotExist = true });\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void DifferenceWhenSubclassNotSpecified()\n        {\n            dynamic a1 = new DynamicDictionary();\n            a1.Field1 = new DynamicDictionary();\n            a1.Field1.SubField1 = 0;\n            a1.Field1.SubField2 = null;\n            a1.Field1.SubField3 = 0.0;\n            dynamic a2 = new DynamicDictionary();\n            var comparer = new Comparer();\n\n            var isEqual = comparer.Compare(a1, a2, out IEnumerable<Difference> differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(1, differences.Count);\n            Assert.IsTrue(differences.Any(\n                d => d.MemberPath == \"Field1\" && d.DifferenceType == DifferenceTypes.MissedMemberInSecondObject));\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/Comparer_ExpandoObjectsTests.cs",
    "content": "﻿using System.Linq;\nusing NUnit.Framework;\nusing System.Collections.Generic;\nusing System.Dynamic;\nusing Newtonsoft.Json;\nusing NSubstitute;\n\nnamespace ObjectsComparer.Tests\n{\n    [TestFixture]\n    public class ComparerExpandoObjectsTests\n    {\n        [Test]\n        public void DifferentValues()\n        {\n            dynamic a1 = new ExpandoObject();\n            a1.Field1 = \"A\";\n            a1.Field2 = 5;\n            a1.Field3 = true;\n            dynamic a2 = new ExpandoObject();\n            a2.Field1 = \"B\";\n            a2.Field2 = 8;\n            a2.Field3 = false;\n\n            var comparer = new Comparer();\n\n            var isEqual = comparer.Compare(a1, a2, out IEnumerable<Difference> differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(3, differences.Count);\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Field1\" && d.Value1 == \"A\" && d.Value2 == \"B\"));\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Field2\" && d.Value1 == \"5\" && d.Value2 == \"8\"));\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Field3\" && d.Value1 == \"True\" && d.Value2 == \"False\"));\n        }\n\n        [Test]\n        public void MissedFields()\n        {\n            dynamic a1 = new ExpandoObject();\n            a1.Field1 = \"A\";\n            a1.Field2 = 5;\n            dynamic a2 = new ExpandoObject();\n            a2.Field1 = \"B\";\n            a2.Field3 = false;\n            var comparer = new Comparer();\n\n            var isEqual = comparer.Compare(a1, a2, out IEnumerable<Difference> differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(3, differences.Count);\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Field1\" && d.Value1 == \"A\" && d.Value2 == \"B\"));\n            Assert.IsTrue(differences.Any(d => d.DifferenceType == DifferenceTypes.MissedMemberInSecondObject && d.MemberPath == \"Field2\" && d.Value1 == \"5\"));\n            Assert.IsTrue(differences.Any(d => d.DifferenceType == DifferenceTypes.MissedMemberInFirstObject && d.MemberPath == \"Field3\" && d.Value2 == \"False\"));\n        }\n\n        [Test]\n        public void MissedFieldsAndUseDefaults()\n        {\n            dynamic a1 = new ExpandoObject();\n            a1.Field1 = \"A\";\n            a1.Field2 = 5;\n            dynamic a2 = new ExpandoObject();\n            a2.Field1 = \"B\";\n            a2.Field3 = false;\n            a2.Field4 = \"S\";\n            var comparer = new Comparer(new ComparisonSettings { UseDefaultIfMemberNotExist = true });\n\n            var isEqual = comparer.Compare(a1, a2, out IEnumerable<Difference> differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(3, differences.Count);\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Field1\" && d.Value1 == \"A\" && d.Value2 == \"B\"));\n            Assert.IsTrue(differences.Any(d => d.DifferenceType == DifferenceTypes.ValueMismatch && d.MemberPath == \"Field2\" && d.Value1 == \"5\" && d.Value2 == \"0\"));\n            Assert.IsTrue(differences.Any(d => d.DifferenceType == DifferenceTypes.ValueMismatch && d.MemberPath == \"Field4\" && d.Value2 == \"S\"));\n        }\n\n        [Test]\n        public void Hierarchy()\n        {\n            dynamic a1Sub1 = new ExpandoObject();\n            a1Sub1.Field1 = 10;\n            dynamic a1 = new ExpandoObject();\n            a1.FieldSub1 = a1Sub1;\n            dynamic a2Sub1 = new ExpandoObject();\n            a2Sub1.Field1 = 8;\n            dynamic a2 = new ExpandoObject();\n            a2.FieldSub1 = a2Sub1;\n            var comparer = new Comparer();\n\n            var isEqual = comparer.Compare(a1, a2, out IEnumerable<Difference> differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(1, differences.Count);\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"FieldSub1.Field1\" && d.Value1 == \"10\" && d.Value2 == \"8\"));\n        }\n\n        [Test]\n        public void DifferentTypes()\n        {\n            dynamic a1 = new ExpandoObject();\n            a1.Field1 = \"A\";\n            a1.Field2 = 5;\n            dynamic a2 = new ExpandoObject();\n            a2.Field1 = 5;\n            a2.Field2 = \"5\";\n            var comparer = new Comparer();\n\n            var isEqual = comparer.Compare(a1, a2, out IEnumerable<Difference> differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(2, differences.Count);\n            Assert.IsTrue(differences.Any(\n                d => d.MemberPath == \"Field1\" && d.DifferenceType == DifferenceTypes.TypeMismatch));\n            Assert.IsTrue(differences.Any(\n                d => d.MemberPath == \"Field2\" && d.DifferenceType == DifferenceTypes.TypeMismatch));\n        }\n\n        [Test]\n        public void NullsAreEqual()\n        {\n            dynamic a1 = new ExpandoObject();\n            a1.Field1 = null;\n            dynamic a2 = new ExpandoObject();\n            a2.Field1 = null;\n            var comparer = new Comparer();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void NullAndMissedMemberAreNotEqual()\n        {\n            dynamic a1 = new ExpandoObject();\n            a1.Field1 = null;\n            dynamic a2 = new ExpandoObject();\n            a2.Field2 = null;\n            var comparer = new Comparer();\n\n            var isEqual = comparer.Compare(a1, a2, out IEnumerable<Difference> differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(2, differences.Count);\n            Assert.IsTrue(differences.Any(\n                d => d.MemberPath == \"Field1\" && d.DifferenceType == DifferenceTypes.MissedMemberInSecondObject));\n            Assert.IsTrue(differences.Any(\n                d => d.MemberPath == \"Field2\" && d.DifferenceType == DifferenceTypes.MissedMemberInFirstObject));\n        }\n\n        [Test]\n        public void NullValues()\n        {\n            dynamic a1 = new ExpandoObject();\n            a1.Field1 = \"A\";\n            a1.Field2 = null;\n            dynamic a2 = new ExpandoObject();\n            a2.Field1 = null;\n            a2.Field2 = \"B\";\n            var comparer = new Comparer();\n\n            var isEqual = comparer.Compare(a1, a2, out IEnumerable<Difference> differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(2, differences.Count);\n            Assert.IsTrue(differences.Any(\n                d => d.MemberPath == \"Field1\" && d.Value1 == \"A\" && d.DifferenceType == DifferenceTypes.ValueMismatch));\n            Assert.IsTrue(differences.Any(\n                d => d.MemberPath == \"Field2\" && d.Value2 == \"B\" && d.DifferenceType == DifferenceTypes.ValueMismatch));\n        }\n\n        [Test]\n        public void ComparerOverrideWhenEqual()\n        {\n            dynamic a1 = new ExpandoObject();\n            a1.Field1 = \"A\";\n            dynamic a2 = new ExpandoObject();\n            a2.Field1 = \" A \";\n            var stringComparer = Substitute.For<IValueComparer>();\n            stringComparer.Compare(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<ComparisonSettings>()).Returns(true);\n            var comparer = new Comparer();\n            comparer.AddComparerOverride(\"Field1\", stringComparer);\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n            stringComparer.Received().Compare(\"A\", \" A \", Arg.Any<ComparisonSettings>());\n        }\n\n        [Test]\n        public void ComparerOverrideWhenNotEqual()\n        {\n            dynamic a1 = new ExpandoObject();\n            a1.Field1 = \"A\";\n            dynamic a2 = new ExpandoObject();\n            a2.Field1 = \"B\";\n            var stringComparer = Substitute.For<IValueComparer>();\n            stringComparer.Compare(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<ComparisonSettings>()).Returns(false);\n            stringComparer.ToString(Arg.Any<string>()).Returns(callInfo => callInfo.Arg<string>());\n            var comparer = new Comparer();\n            comparer.AddComparerOverride(\"Field1\", stringComparer);\n\n            var isEqual = comparer.Compare(a1, a2, out IEnumerable<Difference> differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            stringComparer.Received().Compare(\"A\", \"B\", Arg.Any<ComparisonSettings>());\n            Assert.AreEqual(1, differences.Count);\n            Assert.IsTrue(differences.Any(\n                d => d.MemberPath == \"Field1\" && d.Value1 == \"A\" && d.Value2 == \"B\" && d.DifferenceType == DifferenceTypes.ValueMismatch));\n        }\n\n        [Test]\n        public void ComparerOverrideWhenNullAndValueType()\n        {\n            dynamic a1 = new ExpandoObject();\n            a1.Field1 = null;\n            dynamic a2 = new ExpandoObject();\n            a2.Field1 = 5;\n            var comparer = new Comparer();\n            var intComparer = Substitute.For<IValueComparer>();\n            intComparer.Compare(Arg.Any<object>(), Arg.Any<object>(), Arg.Any<ComparisonSettings>()).Returns(false);\n            intComparer.ToString(5).Returns(\"5\");\n            comparer.AddComparerOverride<int>(intComparer);\n\n            var isEqual = comparer.Compare(a1, a2, out IEnumerable<Difference> differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(1, differences.Count);\n            Assert.IsTrue(differences.Any(\n                d => d.MemberPath == \"Field1\" && d.Value1 == string.Empty && d.Value2 == \"5\" && d.DifferenceType == DifferenceTypes.TypeMismatch));\n        }\n\n        [Test]\n        public void UseDefaultValuesWhenSubclassNotSpecified()\n        {\n            dynamic a1 = new ExpandoObject();\n            a1.Field1 = new ExpandoObject();\n            a1.Field1.SubField1 = 0;\n            a1.Field1.SubField2 = null;\n            a1.Field1.SubField3 = 0.0;\n            dynamic a2 = new ExpandoObject();\n            var comparer = new Comparer(new ComparisonSettings { UseDefaultIfMemberNotExist = true });\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void DifferenceWhenSubclassNotSpecified()\n        {\n            dynamic a1 = new ExpandoObject();\n            a1.Field1 = new ExpandoObject();\n            a1.Field1.SubField1 = 0;\n            a1.Field1.SubField2 = null;\n            a1.Field1.SubField3 = 0.0;\n            dynamic a2 = new ExpandoObject();\n            var comparer = new Comparer();\n\n            var isEqual = comparer.Compare(a1, a2, out IEnumerable<Difference> differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(1, differences.Count);\n            Assert.IsTrue(differences.Any(\n                d => d.MemberPath == \"Field1\" && d.DifferenceType == DifferenceTypes.MissedMemberInSecondObject));\n        }\n\n        [Test]\n        public void ExpandoObjectWithCollections()\n        {\n            var comparer = new Comparer(new ComparisonSettings { RecursiveComparison = true });\n\n            dynamic a1 = JsonConvert.DeserializeObject<ExpandoObject>(\n                \"{ \\\"Transaction\\\": [ { \\\"Name\\\": \\\"abc\\\", \\\"No\\\": 101 } ] }\");\n\n            dynamic a2 = JsonConvert.DeserializeObject<ExpandoObject>(\n                \"{ \\\"Transaction\\\": [ { \\\"Name\\\": \\\"abc\\\", \\\"No\\\": 102 } ] }\");\n\n            var isEqual = comparer.Compare(a1, a2, out IEnumerable<Difference> differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            Assert.AreEqual(1, differences.Count);\n            Assert.IsTrue(differences.Any(\n                d => d.MemberPath == \"Transaction[0].No\" && d.DifferenceType == DifferenceTypes.ValueMismatch));\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/Comparer_GenericEnumerableTests.cs",
    "content": "﻿using System.Collections.ObjectModel;\nusing System.Linq;\nusing NUnit.Framework;\nusing ObjectsComparer.Tests.TestClasses;\nusing System.Collections.Generic;\n\nnamespace ObjectsComparer.Tests\n{\n    [TestFixture]\n    public class ComparerGenericEnumerableTests\n    {\n        [Test]\n        public void ValueTypeArrayEquality()\n        {\n            var a1 = new A { IntArray = new[] { 1, 2 } };\n            var a2 = new A { IntArray = new[] { 1, 2 } };\n            var comparer = new Comparer<A>();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void PrimitiveTypeArrayInequalityCount()\n        {\n            var a1 = new A { IntArray = new[] { 1, 2 } };\n            var a2 = new A { IntArray = new[] { 1, 2, 3 } };\n            var comparer = new Comparer<A>();\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(1, differences.Count);\n            Assert.AreEqual(\"IntArray.Length\", differences[0].MemberPath);\n            Assert.AreEqual(\"2\", differences[0].Value1);\n            Assert.AreEqual(\"3\", differences[0].Value2);\n        }\n\n        [Test]\n        public void PrimitiveTypeArrayInequalityMember()\n        {\n            var a1 = new A { IntArray = new[] { 1, 2 } };\n            var a2 = new A { IntArray = new[] { 1, 3 } };\n            var comparer = new Comparer<A>();\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(\"IntArray[1]\", differences.First().MemberPath);\n            Assert.AreEqual(\"2\", differences.First().Value1);\n            Assert.AreEqual(\"3\", differences.First().Value2);\n        }\n\n        [Test]\n        public void PrimitiveTypeArrayInequalityFirstNull()\n        {\n            var a1 = new A();\n            var a2 = new A { IntArray = new int[0] };\n            var comparer = new Comparer<A>();\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(\"IntArray\", differences.First().MemberPath);\n            Assert.AreEqual(string.Empty, differences.First().Value1);\n            Assert.AreEqual(a2.IntArray.ToString(), differences.First().Value2);\n            Assert.AreEqual(DifferenceTypes.ValueMismatch, differences.First().DifferenceType);\n        }\n\n        [Test]\n        public void PrimitiveTypeArrayInequalitySecondNull()\n        {\n            var a1 = new A { IntArray = new int[0] };\n            var a2 = new A();\n            var comparer = new Comparer<A>();\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(\"IntArray\", differences.First().MemberPath);\n            Assert.AreEqual(a1.IntArray.ToString(), differences.First().Value1);\n            Assert.AreEqual(string.Empty, differences.First().Value2);\n        }\n\n        [Test]\n        public void ClassArrayEquality()\n        {\n            var a1 = new A { ArrayOfB = new[] { new B { Property1 = \"Str1\" }, new B { Property1 = \"Str2\" } } };\n            var a2 = new A { ArrayOfB = new[] { new B { Property1 = \"Str1\" }, new B { Property1 = \"Str2\" } } };\n            var comparer = new Comparer<A>();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void ClassArrayInequalityCount()\n        {\n            var a1 = new A { ArrayOfB = new[] { new B { Property1 = \"Str1\" } } };\n            var a2 = new A { ArrayOfB = new[] { new B { Property1 = \"Str1\" }, new B { Property1 = \"Str2\" } } };\n            var comparer = new Comparer<A>();\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(1, differences.Count);\n            Assert.AreEqual(\"ArrayOfB.Length\", differences[0].MemberPath);\n            Assert.AreEqual(\"1\", differences[0].Value1);\n            Assert.AreEqual(\"2\", differences[0].Value2);\n        }\n\n        [Test]\n        public void ClassArrayInequalityProperty()\n        {\n            var a1 = new A { ArrayOfB = new[] { new B { Property1 = \"Str1\" }, new B { Property1 = \"Str2\" } } };\n            var a2 = new A { ArrayOfB = new[] { new B { Property1 = \"Str1\" }, new B { Property1 = \"Str3\" } } };\n            var comparer = new Comparer<A>();\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(\"ArrayOfB[1].Property1\", differences.First().MemberPath);\n            Assert.AreEqual(\"Str2\", differences.First().Value1);\n            Assert.AreEqual(\"Str3\", differences.First().Value2);\n        }\n\n        [Test]\n        public void CollectionEquality()\n        {\n            var a1 = new A { CollectionOfB = new Collection<B> { new B { Property1 = \"Str1\" }, new B { Property1 = \"Str2\" } } };\n            var a2 = new A { CollectionOfB = new Collection<B> { new B { Property1 = \"Str1\" }, new B { Property1 = \"Str2\" } } };\n            var comparer = new Comparer<A>();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void CollectionInequalityCount()\n        {\n            var a1 = new A { CollectionOfB = new Collection<B> { new B { Property1 = \"Str1\" }, new B { Property1 = \"Str2\" } } };\n            var a2 = new A { CollectionOfB = new Collection<B> { new B { Property1 = \"Str1\" } } };\n            var comparer = new Comparer<A>();\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(1, differences.Count);\n            Assert.AreEqual(DifferenceTypes.NumberOfElementsMismatch, differences[0].DifferenceType);\n            Assert.AreEqual(\"CollectionOfB\", differences[0].MemberPath);\n            Assert.AreEqual(\"2\", differences[0].Value1);\n            Assert.AreEqual(\"1\", differences[0].Value2);\n        }\n\n        [Test]\n        public void CollectionAndNullInequality()\n        {\n            var a1 = new A { CollectionOfB = new Collection<B> { new B { Property1 = \"Str1\" }, new B { Property1 = \"Str2\" } } };\n            var a2 = new A();\n            var comparer = new Comparer<A>();\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(1, differences.Count);\n            Assert.AreEqual(\"CollectionOfB\", differences[0].MemberPath);\n            Assert.AreEqual(a1.CollectionOfB.ToString(), differences[0].Value1);\n            Assert.AreEqual(string.Empty, differences[0].Value2);\n        }\n\n        [Test]\n        public void NullAndCollectionInequality()\n        {\n            var a1 = new A();\n            var a2 = new A { CollectionOfB = new Collection<B> { new B { Property1 = \"Str1\" }, new B { Property1 = \"Str2\" } } };\n            var comparer = new Comparer<A>();\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(1, differences.Count);\n            Assert.AreEqual(\"CollectionOfB\", differences[0].MemberPath);\n            Assert.AreEqual(string.Empty, differences[0].Value1);\n            Assert.AreEqual(a2.CollectionOfB.ToString(), differences[0].Value2);\n        }\n\n        [Test]\n        public void IgnoreAttributeComparisonEquality()\n        {\n            var a1 = new Parent();\n            a1.Child1.Add(new ParentChild(a1, \"Child1\"));\n            a1.Child1.Add(new ParentChild(a1, \"Child2\"));\n\n            var a2 = new Parent();\n            a2.Child1.Add(new ParentChild(a2, \"Child1\"));\n            a2.Child1.Add(new ParentChild(a2, \"Child2\"));\n\n            var comparer = new Comparer<Parent>();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void IgnoreAttributeComparisonInEquality()\n        {\n            var a1 = new Parent();\n            a1.Child1.Add(new ParentChild(a1, \"Child1\"));\n            a1.Child1.Add(new ParentChild(a1, \"Child2\"));\n\n            var a2 = new Parent();\n            a2.Child1.Add(new ParentChild(a2, \"Child1\"));\n            a2.Child1.Add(new ParentChild(a2, \"Child3\"));\n\n            var comparer = new Comparer<Parent>();\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(\"Child1[1].Property1\", differences.First().MemberPath);\n            Assert.AreEqual(\"Child2\", differences.First().Value1);\n            Assert.AreEqual(\"Child3\", differences.First().Value2);\n        }\n\n\n\n        [Test]\n        public void IgnoreAttributeComparisonDeepEquality()\n        {\n          var a1 = new Parent();\n          a1.Child1.Add(new ParentChild(a1,\n                                        \"Child1\",\n                                        new ObservableCollection <Child>\n                                        {\n                                            new Child(\"p1\",\n                                                      \"p2\"),\n                                            new Child(\"p3\",\n                                                      \"p4\")\n                                        }\n                                       ));\n          a1.Child1.Add(new ParentChild(a1,\n                                        \"Child2\",\n                                        new ObservableCollection <Child>\n                                        {\n                                            new Child(\"p1\",\n                                                      \"p2\"),\n                                            new Child(\"p3\",\n                                                      \"p4\")\n                                        }\n                                       ));\n\n          var a2 = new Parent();\n          a2.Child1.Add(new ParentChild(a1,\n                                        \"Child1\",\n                                        new ObservableCollection <Child>\n                                        {\n                                            new Child(\"p1\",\n                                                      \"p2\"),\n                                            new Child(\"p3\",\n                                                      \"p4\")\n                                        }\n                                       ));\n          a2.Child1.Add(new ParentChild(a1,\n                                        \"Child2\",\n                                        new ObservableCollection <Child>\n                                        {\n                                            new Child(\"p1\",\n                                                      \"p2\"),\n                                            new Child(\"p3\",\n                                                      \"p5\")\n                                        }\n                                       ));\n\n          var comparer = new Comparer <Parent>();\n\n          var isEqual = comparer.Compare(a1,\n                                         a2);\n\n          Assert.IsTrue(isEqual);\n        }\n\n\n\n        [Test]\n        public void IgnoreAttributeComparisonDeepInEquality()\n        {\n          var a1 = new Parent();\n          a1.Child1.Add(new ParentChild(a1,\n                                        \"Child1\",\n                                        new ObservableCollection <Child>\n                                        {\n                                            new Child(\"p1\",\n                                                      \"p2\"),\n                                            new Child(\"p3\",\n                                                      \"p4\")\n                                        }\n                                       ));\n          a1.Child1.Add(new ParentChild(a1,\n                                        \"Child2\",\n                                        new ObservableCollection <Child>()\n                                        {\n                                            new Child(\"p1\",\n                                                      \"p2\"),\n                                            new Child(\"p3\",\n                                                      \"p4\")\n                                        }\n                                       ));\n\n          var a2 = new Parent();\n          a2.Child1.Add(new ParentChild(a1,\n                                        \"Child1\",\n                                        new ObservableCollection <Child>\n                                        {\n                                            new Child(\"p1\",\n                                                      \"p2\"),\n                                            new Child(\"p3\",\n                                                      \"p4\")\n                                        }\n                                       ));\n          a2.Child1.Add(new ParentChild(a1,\n                                        \"Child2\",\n                                        new ObservableCollection <Child>\n                                        {\n                                            new Child(\"p1\",\n                                                      \"p2\"),\n                                            new Child(\"p5\",\n                                                      \"p6\")\n                                        }\n                                       ));\n\n          var comparer = new Comparer <Parent>();\n\n          var differences = comparer.CalculateDifferences(a1,\n                                                          a2).ToList();\n\n          CollectionAssert.IsNotEmpty(differences);\n          Assert.AreEqual(\"Child1[1].Children[1].Property1\",\n                          differences.First().MemberPath);\n          Assert.AreEqual(\"p3\",\n                          differences.First().Value1);\n          Assert.AreEqual(\"p5\",\n                          differences.First().Value2);\n        }\n\n\n\n        [Test]\n        public void CollectionInequalityProperty()\n        {\n            var a1 = new A { CollectionOfB = new Collection<B> { new B { Property1 = \"Str1\" }, new B { Property1 = \"Str2\" } } };\n            var a2 = new A { CollectionOfB = new Collection<B> { new B { Property1 = \"Str1\" }, new B { Property1 = \"Str3\" } } };\n            var comparer = new Comparer<A>();\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(\"CollectionOfB[1].Property1\", differences.First().MemberPath);\n            Assert.AreEqual(\"Str2\", differences.First().Value1);\n            Assert.AreEqual(\"Str3\", differences.First().Value2);\n        }\n\n        [Test]\n        public void ClassImplementsCollectionEquality()\n        {\n            var a1 = new A { ClassImplementsCollectionOfB = new CollectionOfB { new B { Property1 = \"Str1\" }, new B { Property1 = \"Str2\" } } };\n            var a2 = new A { ClassImplementsCollectionOfB = new CollectionOfB { new B { Property1 = \"Str1\" }, new B { Property1 = \"Str2\" } } };\n            var comparer = new Comparer<A>();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void ClassImplementsCollectionInequalityCount()\n        {\n            var a1 = new A { ClassImplementsCollectionOfB = new CollectionOfB { new B { Property1 = \"Str1\" }, new B { Property1 = \"Str2\" } } };\n            var a2 = new A { ClassImplementsCollectionOfB = new CollectionOfB { new B { Property1 = \"Str1\" } } };\n            var comparer = new Comparer<A>();\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(1, differences.Count);\n            Assert.AreEqual(DifferenceTypes.NumberOfElementsMismatch, differences[0].DifferenceType);\n            Assert.AreEqual(\"ClassImplementsCollectionOfB\", differences[0].MemberPath);\n            Assert.AreEqual(\"2\", differences[0].Value1);\n            Assert.AreEqual(\"1\", differences[0].Value2);\n        }\n\n        [Test]\n        public void ClassImplementsCollectionInequalityProperty()\n        {\n            var a1 = new A { ClassImplementsCollectionOfB = new CollectionOfB { new B { Property1 = \"Str1\" }, new B { Property1 = \"Str2\" } } };\n            var a2 = new A { ClassImplementsCollectionOfB = new CollectionOfB { new B { Property1 = \"Str1\" }, new B { Property1 = \"Str3\" } } };\n            var comparer = new Comparer<A>();\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(\"ClassImplementsCollectionOfB[1].Property1\", differences.First().MemberPath);\n            Assert.AreEqual(\"Str2\", differences.First().Value1);\n            Assert.AreEqual(\"Str3\", differences.First().Value2);\n        }\n\n        [Test]\n        public void NullAndEmptyComparisonGenericInequality()\n        {\n            var a1 = new A { ListOfB = new List<B>() };\n            var a2 = new A();\n            var comparer = new Comparer<A>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(\"ListOfB\", differences.First().MemberPath);\n            Assert.AreEqual(a1.ListOfB.ToString(), differences.First().Value1);\n            Assert.AreEqual(string.Empty, differences.First().Value2);\n        }\n\n        [Test]\n        public void NullAndEmptyComparisonGenericEquality()\n        {\n            var a1 = new A { ListOfB = new List<B>() };\n            var a2 = new A();\n            var comparer = new Comparer<A>(new ComparisonSettings { EmptyAndNullEnumerablesEqual = true });\n\n            var isEqual = comparer.Compare(a1, a2, out var differences);\n\n            Assert.IsTrue(isEqual);\n            CollectionAssert.IsEmpty(differences);\n        }\n\n        [TestCase(FlagsEnum.Flag1 | FlagsEnum.Flag2, FlagsEnum.Flag1 | FlagsEnum.Flag3)]\n        [TestCase(FlagsEnum.Flag2, FlagsEnum.Flag3)]\n        [TestCase(FlagsEnum.Flag1, FlagsEnum.Flag1 | FlagsEnum.Flag2)]\n        public void FlagsInequality(FlagsEnum flags1, FlagsEnum flags2)\n        {\n            var a1 = new A { Flags = flags1 };\n            var a2 = new A { Flags = flags2 };\n            var comparer = new Comparer<A>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(1, differences.Count);\n            Assert.AreEqual(\"Flags\", differences.First().MemberPath);\n            Assert.AreEqual(flags1.ToString(), differences.First().Value1);\n            Assert.AreEqual(flags2.ToString(), differences.First().Value2);\n        }\n\n        [TestCase(FlagsEnum.Flag1 | FlagsEnum.Flag2, FlagsEnum.Flag1 | FlagsEnum.Flag2)]\n        [TestCase(FlagsEnum.Flag2, FlagsEnum.Flag2)]\n        public void FlagsEquality(FlagsEnum flags1, FlagsEnum flags2)\n        {\n            var a1 = new A { Flags = flags1 };\n            var a2 = new A { Flags = flags2 };\n            var comparer = new Comparer<A>();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void CollectionOfBCountInequality1()\n        {\n            var a1 = new A\n            {\n                EnumerableOfB = new[] { new B { Property1 = \"B1\" } }\n            };\n            var a2 = new A\n            {\n                EnumerableOfB = new[] { new B { Property1 = \"B1\" }, new B { Property1 = \"B2\" } }\n            };\n            var comparer = new Comparer<A>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(1, differences.Count);\n            Assert.AreEqual(\"EnumerableOfB\", differences.First().MemberPath);\n            Assert.AreEqual(DifferenceTypes.NumberOfElementsMismatch, differences.First().DifferenceType);\n            Assert.AreEqual(\"1\", differences.First().Value1);\n            Assert.AreEqual(\"2\", differences.First().Value2);\n        }\n\n        [Test]\n        public void CollectionOfBCountInequality2()\n        {\n            var a1 = new A\n            {\n                EnumerableOfB = new[] { new B { Property1 = \"B1\" } }\n            };\n            var a2 = new A\n            {\n                EnumerableOfB = new B[0]\n            };\n            var comparer = new Comparer<A>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(1, differences.Count);\n            Assert.AreEqual(\"EnumerableOfB\", differences.First().MemberPath);\n            Assert.AreEqual(DifferenceTypes.NumberOfElementsMismatch, differences.First().DifferenceType);\n            Assert.AreEqual(\"1\", differences.First().Value1);\n            Assert.AreEqual(\"0\", differences.First().Value2);\n        }\n\n\n        [Test]\n        public void HashSetEqualitySameOrder()\n        {\n            var a1 = new HashSet<string> { \"a\", \"b\" };\n            var a2 = new HashSet<string> { \"a\", \"b\" };\n            var comparer = new Comparer<HashSet<string>>();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void HashSetEqualityDifferentOrder()\n        {\n            var a1 = new HashSet<string> { \"a\", \"b\" };\n            var a2 = new HashSet<string> { \"b\", \"a\" };\n            var comparer = new Comparer<HashSet<string>>();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void HashSetInequalityDifferentElements()\n        {\n            var a1 = new HashSet<string> { \"a\", \"b\" };\n            var a2 = new HashSet<string> { \"a\", \"c\" };\n            var comparer = new Comparer<HashSet<string>>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(2, differences.Count);\n            Assert.IsTrue(differences.Any(\n                d => d.MemberPath == string.Empty && d.DifferenceType == DifferenceTypes.MissedElementInFirstObject && d.Value2 == \"c\"));\n            Assert.IsTrue(differences.Any(\n                d => d.MemberPath == string.Empty && d.DifferenceType == DifferenceTypes.MissedElementInSecondObject && d.Value1 == \"b\"));\n        }\n\n        [Test]\n        public void HashSetInequalityDifferentNumberOfElements()\n        {\n            var a1 = new HashSet<string> { \"a\", \"b\" };\n            var a2 = new HashSet<string> { \"a\", \"b\", \"c\" };\n            var comparer = new Comparer<HashSet<string>>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(1, differences.Count);\n            Assert.IsTrue(differences.Any(\n                d => d.MemberPath == string.Empty && d.DifferenceType == DifferenceTypes.MissedElementInFirstObject && d.Value2 == \"c\"));\n        }\n\n        [Test]\n        public void HashSetAndNullInequality()\n        {\n            var a = new HashSet<string> { \"a\", \"b\" };\n            var comparer = new Comparer<HashSet<string>>();\n\n            var isEqual = comparer.Compare(a, null, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(1, differences.Count);\n            Assert.IsTrue(differences.Any(\n                d => d.MemberPath == string.Empty && d.DifferenceType == DifferenceTypes.ValueMismatch));\n        }\n\n        [Test]\n        public void IgnoreCapacityForLists()\n        {\n            var a1 = new A\n            {\n                ListOfB = new List<B> { new B { Property1 = \"str2\" }, new B { Property1 = \"str2\" } }\n            };\n\n            var a2 = new A\n            {\n                ListOfB = new List<B> { new B { Property1 = \"str2\" }, new B { Property1 = \"str2\" } }\n            };\n\n            a1.ListOfB.TrimExcess();\n\n            var comparer = new Comparer<A>();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void CompareAsIList()\n        {\n            var list1 = new List<int> { 1, 2 };\n            var list2 = new List<int> { 1 };\n\n            var comparer = new Comparer<IList<int>>();\n\n            var differences = comparer.CalculateDifferences(list1, list2).ToList();\n\n            Assert.AreEqual(1, differences.Count);\n            Assert.AreEqual(DifferenceTypes.NumberOfElementsMismatch, differences.First().DifferenceType);\n            Assert.AreEqual(\"2\", differences.First().Value1);\n            Assert.AreEqual(\"1\", differences.First().Value2);\n        }\n\n        [Test]\n        public void DictionaryEqualitySameOrder()\n        {\n            var a1 = new Dictionary<int, string> { { 1, \"One\" }, { 2, \"Two\" } };\n            var a2 = new Dictionary<int, string> { { 1, \"One\" }, { 2, \"Two\" } };\n            var comparer = new Comparer<Dictionary<int, string>>();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void DictionaryInequalityDifferentOrder()\n        {\n            var a1 = new Dictionary<int, string> { { 1, \"One\" }, { 2, \"Two\" } };\n            var a2 = new Dictionary<int, string> { { 2, \"Two\" }, { 1, \"One\" } };\n            var comparer = new Comparer<Dictionary<int, string>>();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsFalse(isEqual);\n        }\n\n        [Test]\n        public void DictionaryInequalityDifferentNumberOfElements()\n        {\n            var a1 = new Dictionary<int, string> { { 1, \"One\" }, { 2, \"Two\" } };\n            var a2 = new Dictionary<int, string> { { 1, \"One\" }, { 2, \"Two\" }, { 3, \"Three\" } };\n            var comparer = new Comparer<Dictionary<int, string>>();\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            Assert.AreEqual(1, differences.Count);\n            Assert.AreEqual(DifferenceTypes.NumberOfElementsMismatch, differences.First().DifferenceType);\n        }\n\n        [Test]\n        public void DictionaryInequalityDifferentValue()\n        {\n            var a1 = new Dictionary<int, string> { { 1, \"One\" }, { 2, \"Two\" } };\n            var a2 = new Dictionary<int, string> { { 1, \"One\" }, { 2, \"Two!\" } };\n            var comparer = new Comparer<Dictionary<int, string>>();\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            Assert.AreEqual(1, differences.Count);\n            Assert.AreEqual(DifferenceTypes.ValueMismatch, differences.First().DifferenceType);\n            Assert.AreEqual(\"Two\", differences.First().Value1);\n            Assert.AreEqual(\"Two!\", differences.First().Value2);\n            Assert.AreEqual(\"[1].Value\", differences.First().MemberPath);\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/Comparer_IPEndPointTests.cs",
    "content": "﻿using System.Linq;\nusing System.Net;\nusing NUnit.Framework;\n\nnamespace ObjectsComparer.Tests\n{\n    [TestFixture]\n    public class Comparer_IPEndPointTests\n    {\n        [Test]\n        public void Equality()\n        {\n            var a1 = new IPEndPoint(50, 20);\n            var a2 = new IPEndPoint(50, 20);\n            var comparer = new Comparer<IPEndPoint>();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void Inequality()\n        {\n            var a1 = new IPEndPoint(50, 20);\n            var a2 = new IPEndPoint(52, 21);\n            var comparer = new Comparer<IPEndPoint>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(2, differences.Count);\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Address.Address\" && d.Value1 == \"50\" && d.Value2 == \"52\"));\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"Port\" && d.Value1 == \"20\" && d.Value2 == \"21\"));\n        }\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/Comparer_Issue24Tests.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing NSubstitute;\nusing NUnit.Framework;\nusing ObjectsComparer.Tests.TestClasses;\n\nnamespace ObjectsComparer.Tests\n{\n    [TestFixture]\n    public class Comparer_Issue24Tests\n    {\n        class MyComparersFactory : ComparersFactory\n        {\n            public override IComparer<T> GetObjectsComparer<T>(ComparisonSettings settings = null,\n                BaseComparer parentComparer = null)\n            {\n\n                IComparer<T> comparer = base.GetObjectsComparer<T>(settings, parentComparer);\n\n                if (parentComparer == null)\n                {\n                    comparer.AddComparerOverride(typeof(string), new IgnoreCaseStringsValueComparer());\n                }\n\n                return comparer;\n            }\n        }\n\n        class TestClassA\n        {\n            public string A { get; set; }\n        }\n\n        class TestClassB\n        {\n            public string B { get; set; }\n\n            public TestClassA ClassA { get; set; }\n        }\n\n        [Test]\n        public void OverrideString()\n        {\n            var b1 = new TestClassB\n            {\n                B = \"B\",\n                ClassA = new TestClassA\n                {\n                    A = \"A\"\n                }\n            };\n\n            var b2 = new TestClassB\n            {\n                B = \"b\",\n                ClassA = new TestClassA\n                {\n                    A = \"a\"\n                }\n            };\n\n            var settings = new ComparisonSettings();\n            var factory = new MyComparersFactory();\n\n            var comparer = factory.GetObjectsComparer<TestClassB>(settings);\n\n            var isEqual = comparer.Compare(b1, b2);\n\n            Assert.IsTrue(isEqual);\n        }\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/Comparer_MultidimensionalArraysTests.cs",
    "content": "﻿using System.Linq;\nusing NUnit.Framework;\nusing ObjectsComparer.Tests.TestClasses;\n\nnamespace ObjectsComparer.Tests\n{\n    [TestFixture]\n    public class ComparerMultidimensionalArraysTests\n    {\n        [Test]\n        public void IntOfIntInequality1()\n        {\n            var a1 = new MultidimensionalArrays { IntOfInt = new[] { new[] { 1, 2 } } };\n            var a2 = new MultidimensionalArrays { IntOfInt = new[] { new[] { 1, 3 } } };\n            var comparer = new Comparer<MultidimensionalArrays>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(1, differences.Count);\n            Assert.AreEqual(\"IntOfInt[0][1]\", differences[0].MemberPath);\n            Assert.AreEqual(\"2\", differences[0].Value1);\n            Assert.AreEqual(\"3\", differences[0].Value2);\n        }\n\n        [Test]\n        public void IntOfIntInequality2()\n        {\n            var a1 = new MultidimensionalArrays { IntOfInt = new[] { new[] { 1, 2 }, new[] { 3, 4 } } };\n            var a2 = new MultidimensionalArrays { IntOfInt = new[] { new[] { 2, 2 }, new[] { 3, 5 } } };\n            var comparer = new Comparer<MultidimensionalArrays>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(2, differences.Count);\n            Assert.AreEqual(\"IntOfInt[0][0]\", differences[0].MemberPath);\n            Assert.AreEqual(\"1\", differences[0].Value1);\n            Assert.AreEqual(\"2\", differences[0].Value2);\n            Assert.AreEqual(\"IntOfInt[1][1]\", differences[1].MemberPath);\n            Assert.AreEqual(\"4\", differences[1].Value1);\n            Assert.AreEqual(\"5\", differences[1].Value2);\n        }\n\n        [Test]\n        public void IntOfIntInequality3()\n        {\n            var a1 = new MultidimensionalArrays { IntOfInt = new[] { new[] { 1, 2 } } };\n            var a2 = new MultidimensionalArrays { IntOfInt = new[] { new[] { 2, 2 }, new[] { 3, 5 } } };\n            var comparer = new Comparer<MultidimensionalArrays>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(1, differences.Count);\n            Assert.AreEqual(\"IntOfInt.Length\", differences[0].MemberPath);\n            Assert.AreEqual(\"1\", differences[0].Value1);\n            Assert.AreEqual(\"2\", differences[0].Value2);\n        }\n\n        [Test]\n        public void IntOfIntInequality4()\n        {\n            var a1 = new MultidimensionalArrays { IntOfInt = new int[0][] };\n            var a2 = new MultidimensionalArrays { IntOfInt = new[] { new[] { 2, 2 } } };\n            var comparer = new Comparer<MultidimensionalArrays>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(1, differences.Count);\n            Assert.AreEqual(\"IntOfInt.Length\", differences[0].MemberPath);\n            Assert.AreEqual(\"0\", differences[0].Value1);\n            Assert.AreEqual(\"1\", differences[0].Value2);\n        }\n\n        [Test]\n        public void IntOfIntInequality5()\n        {\n            var a1 = new MultidimensionalArrays { IntOfInt = new[] { new[] { 1, 2 }, new[] { 3, 5 } } };\n            var a2 = new MultidimensionalArrays { IntOfInt = new[] { new[] { 1, 2 }, new[] { 3, 5, 6 } } };\n            var comparer = new Comparer<MultidimensionalArrays>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(1, differences.Count);\n            Assert.AreEqual(\"IntOfInt[1].Length\", differences[0].MemberPath);\n            Assert.AreEqual(\"2\", differences[0].Value1);\n            Assert.AreEqual(\"3\", differences[0].Value2);\n        }\n\n        [Test]\n        public void IntOfIntInequality6()\n        {\n            var a1 = new MultidimensionalArrays { IntOfInt = new int[0][] };\n            var a2 = new MultidimensionalArrays();\n            var comparer = new Comparer<MultidimensionalArrays>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(1, differences.Count);\n            Assert.AreEqual(\"IntOfInt\", differences[0].MemberPath);\n            Assert.AreEqual(typeof(int[][]).FullName, differences[0].Value1);\n            Assert.AreEqual(string.Empty, differences[0].Value2);\n        }\n\n        [Test]\n        public void IntOfIntInequality7()\n        {\n            var a1 = new MultidimensionalArrays();\n            var a2 = new MultidimensionalArrays { IntOfInt = new int[0][] };\n            var comparer = new Comparer<MultidimensionalArrays>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(1, differences.Count);\n            Assert.AreEqual(\"IntOfInt\", differences[0].MemberPath);\n            Assert.AreEqual(string.Empty, differences[0].Value1);\n            Assert.AreEqual(typeof(int[][]).FullName, differences[0].Value2);\n        }\n\n        [Test]\n        public void IntOfIntEquality1()\n        {\n            var a1 = new MultidimensionalArrays { IntOfInt = new[] { new[] { 1, 2 }, new[] { 3, 5 } } };\n            var a2 = new MultidimensionalArrays { IntOfInt = new[] { new[] { 1, 2 }, new[] { 3, 5 } } };\n            var comparer = new Comparer<MultidimensionalArrays>();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void IntOfIntEquality2()\n        {\n            var a1 = new MultidimensionalArrays { IntOfInt = new[] { new[] { 1, 2 }, new int[0] } };\n            var a2 = new MultidimensionalArrays { IntOfInt = new[] { new[] { 1, 2 }, new int[0] } };\n            var comparer = new Comparer<MultidimensionalArrays>();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void IntOfIntEquality3()\n        {\n            var a1 = new MultidimensionalArrays { IntOfInt = new int[0][] };\n            var a2 = new MultidimensionalArrays { IntOfInt = new int[0][] };\n            var comparer = new Comparer<MultidimensionalArrays>();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void IntOfIntEquality4()\n        {\n            var a1 = new MultidimensionalArrays { IntOfInt = new int[0][] };\n            var a2 = new MultidimensionalArrays();\n            var comparer = new Comparer<MultidimensionalArrays>(new ComparisonSettings { EmptyAndNullEnumerablesEqual = true });\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void IntOfIntEquality5()\n        {\n            var a1 = new MultidimensionalArrays();\n            var a2 = new MultidimensionalArrays { IntOfInt = new int[0][] };\n            var comparer = new Comparer<MultidimensionalArrays>(new ComparisonSettings { EmptyAndNullEnumerablesEqual = true });\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void IntIntInequality1()\n        {\n            var a1 = new MultidimensionalArrays { IntInt = new[,] { { 1, 2 } } };\n            var a2 = new MultidimensionalArrays { IntInt = new[,] { { 1, 3 }, { 1, 3 } } };\n            var comparer = new Comparer<MultidimensionalArrays>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(1, differences.Count);\n            Assert.AreEqual(\"IntInt.Dimension0\", differences[0].MemberPath);\n            Assert.AreEqual(\"1\", differences[0].Value1);\n            Assert.AreEqual(\"2\", differences[0].Value2);\n        }\n\n        [Test]\n        public void IntIntInequality2()\n        {\n            var a1 = new MultidimensionalArrays { IntInt = new[,] { { 1, 2 }, { 1, 3 } } };\n            var a2 = new MultidimensionalArrays { IntInt = new[,] { { 1, 3, 4 }, { 1, 3, 8 } } };\n            var comparer = new Comparer<MultidimensionalArrays>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(1, differences.Count);\n            Assert.AreEqual(\"IntInt.Dimension1\", differences[0].MemberPath);\n            Assert.AreEqual(\"2\", differences[0].Value1);\n            Assert.AreEqual(\"3\", differences[0].Value2);\n        }\n\n        [Test]\n        public void IntIntInequality3()\n        {\n            var a1 = new MultidimensionalArrays { IntInt = new[,] { { 1, 2 } } };\n            var a2 = new MultidimensionalArrays { IntInt = new[,] { { 1, 3 } } };\n            var comparer = new Comparer<MultidimensionalArrays>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(1, differences.Count);\n            Assert.AreEqual(\"IntInt[0,1]\", differences[0].MemberPath);\n            Assert.AreEqual(\"2\", differences[0].Value1);\n            Assert.AreEqual(\"3\", differences[0].Value2);\n        }\n\n        [Test]\n        public void IntIntInequality4()\n        {\n            var a1 = new MultidimensionalArrays { IntInt = new[,] { { 1, 2 }, { 3, 4 } } };\n            var a2 = new MultidimensionalArrays { IntInt = new[,] { { 0, 2 }, { 3, 4 } } };\n            var comparer = new Comparer<MultidimensionalArrays>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(1, differences.Count);\n            Assert.AreEqual(\"IntInt[0,0]\", differences[0].MemberPath);\n            Assert.AreEqual(\"1\", differences[0].Value1);\n            Assert.AreEqual(\"0\", differences[0].Value2);\n        }\n\n        [Test]\n        public void IntIntInequality5()\n        {\n            var a1 = new MultidimensionalArrays { IntInt = new[,] { { 1, 2 }, { 3, 0 } } };\n            var a2 = new MultidimensionalArrays { IntInt = new[,] { { 1, 2 }, { 3, 4 } } };\n            var comparer = new Comparer<MultidimensionalArrays>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(1, differences.Count);\n            Assert.AreEqual(\"IntInt[1,1]\", differences[0].MemberPath);\n            Assert.AreEqual(\"0\", differences[0].Value1);\n            Assert.AreEqual(\"4\", differences[0].Value2);\n        }\n\n        [Test]\n        public void IntIntInequality6()\n        {\n            var a1 = new MultidimensionalArrays { IntInt = null };\n            var a2 = new MultidimensionalArrays { IntInt = new int[0, 0] };\n            var comparer = new Comparer<MultidimensionalArrays>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(1, differences.Count);\n            Assert.AreEqual(\"IntInt\", differences[0].MemberPath);\n            Assert.AreEqual(string.Empty, differences[0].Value1);\n            Assert.AreEqual(typeof(int[,]).FullName, differences[0].Value2);\n        }\n\n        [Test]\n        public void IntIntInequality7()\n        {\n            var a1 = new MultidimensionalArrays { IntInt = new int[0, 0] };\n            var a2 = new MultidimensionalArrays { IntInt = null };\n            var comparer = new Comparer<MultidimensionalArrays>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(1, differences.Count);\n            Assert.AreEqual(\"IntInt\", differences[0].MemberPath);\n            Assert.AreEqual(typeof(int[,]).FullName, differences[0].Value1);\n            Assert.AreEqual(string.Empty, differences[0].Value2);\n        }\n\n        [Test]\n        public void IntIntEquality1()\n        {\n            var a1 = new MultidimensionalArrays { IntInt = new[,] { { 1, 2 }, { 3, 4 } } };\n            var a2 = new MultidimensionalArrays { IntInt = new[,] { { 1, 2 }, { 3, 4 } } };\n            var comparer = new Comparer<MultidimensionalArrays>();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void IntIntEquality2()\n        {\n            var a1 = new MultidimensionalArrays { IntInt = null };\n            var a2 = new MultidimensionalArrays { IntInt = new int[0, 0] };\n            var comparer = new Comparer<MultidimensionalArrays>(new ComparisonSettings { EmptyAndNullEnumerablesEqual = true });\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void IntIntEquality3()\n        {\n            var a1 = new MultidimensionalArrays { IntInt = new int[0, 0] };\n            var a2 = new MultidimensionalArrays { IntInt = null };\n            var comparer = new Comparer<MultidimensionalArrays>(new ComparisonSettings { EmptyAndNullEnumerablesEqual = true });\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/Comparer_NonGenericEnumerableTests.cs",
    "content": "﻿using System.Collections;\nusing System.Linq;\nusing NUnit.Framework;\nusing ObjectsComparer.Tests.TestClasses;\n\nnamespace ObjectsComparer.Tests\n{\n    [TestFixture]\n    public class ComparerNonGenericEnumerableTests\n    {\n        [Test]\n        public void Equality()\n        {\n            var a1 = new A { NonGenericEnumerable = new ArrayList { new B { Property1 = \"Str1\" }, new B { Property1 = \"Str2\" } } };\n            var a2 = new A { NonGenericEnumerable = new ArrayList { new B { Property1 = \"Str1\" }, new B { Property1 = \"Str2\" } } };\n            var comparer = new Comparer<A>();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void InequalityCount()\n        {\n            var a1 = new A { NonGenericEnumerable = new ArrayList { new B { Property1 = \"Str1\" }, new B { Property1 = \"Str2\" } } };\n            var a2 = new A { NonGenericEnumerable = new ArrayList { new B { Property1 = \"Str1\" } } };\n            var comparer = new Comparer<A>();\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(\"NonGenericEnumerable\", differences.First().MemberPath);\n            Assert.AreEqual(DifferenceTypes.NumberOfElementsMismatch, differences.First().DifferenceType);\n            Assert.AreEqual(\"2\", differences.First().Value1);\n            Assert.AreEqual(\"1\", differences.First().Value2);\n        }\n\n        [Test]\n        public void InequalityProperty()\n        {\n            var a1 = new A { NonGenericEnumerable = new ArrayList { new B { Property1 = \"Str1\" }, new B { Property1 = \"Str2\" } } };\n            var a2 = new A { NonGenericEnumerable = new ArrayList { new B { Property1 = \"Str1\" }, new B { Property1 = \"Str3\" } } };\n            var comparer = new Comparer<A>();\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(\"NonGenericEnumerable[1].Property1\", differences.First().MemberPath);\n            Assert.AreEqual(\"Str2\", differences.First().Value1);\n            Assert.AreEqual(\"Str3\", differences.First().Value2);\n        }\n\n        [Test]\n        public void NullElementsEquality()\n        {\n            var a1 = new A { NonGenericEnumerable = new ArrayList { null } };\n            var a2 = new A { NonGenericEnumerable = new ArrayList { null } };\n            var comparer = new Comparer<A>();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void NullAndNotNullElementsInequality()\n        {\n            var a1 = new A { NonGenericEnumerable = new ArrayList { null, \"Str1\" } };\n            var a2 = new A { NonGenericEnumerable = new ArrayList { \"Str2\", null } };\n            var comparer = new Comparer<A>();\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            Assert.AreEqual(2, differences.Count);\n            Assert.AreEqual(\"NonGenericEnumerable[0]\", differences[0].MemberPath);\n            Assert.AreEqual(string.Empty, differences[0].Value1);\n            Assert.AreEqual(\"Str2\", differences[0].Value2);\n            Assert.AreEqual(\"NonGenericEnumerable[1]\", differences[1].MemberPath);\n            Assert.AreEqual(\"Str1\", differences[1].Value1);\n            Assert.AreEqual(string.Empty, differences[1].Value2);\n        }\n\n        [Test]\n        public void InequalityType()\n        {\n            var a1 = new A { NonGenericEnumerable = new ArrayList { new B { Property1 = \"Str1\" }, new B { Property1 = \"Str2\" } } };\n            var a2 = new A { NonGenericEnumerable = new ArrayList { new B { Property1 = \"Str1\" }, \"Str3\" } };\n            var comparer = new Comparer<A>();\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(\"NonGenericEnumerable[1]\", differences.First().MemberPath);\n            Assert.AreEqual(\"ObjectsComparer.Tests.TestClasses.B\", differences.First().Value1);\n            Assert.AreEqual(\"Str3\", differences.First().Value2);\n        }\n\n        [Test]\n        public void DerivedClassEquality()\n        {\n            var a1 = new A { NonGenericEnumerableImplementation = new EnumerableImplementation(new ArrayList { new B { Property1 = \"Str1\" }, new B { Property1 = \"Str2\" } }) };\n            var a2 = new A { NonGenericEnumerableImplementation = new EnumerableImplementation(new ArrayList { new B { Property1 = \"Str1\" }, new B { Property1 = \"Str2\" } }) };\n            var comparer = new Comparer<A>();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void DerivedClassInequalityProperty()\n        {\n            var a1 = new A { NonGenericEnumerableImplementation = new EnumerableImplementation(new ArrayList { new B { Property1 = \"Str1\" }, new B { Property1 = \"Str2\" } }) { Property1 = \"Str3\" } };\n            var a2 = new A { NonGenericEnumerableImplementation = new EnumerableImplementation(new ArrayList { new B { Property1 = \"Str1\" }, new B { Property1 = \"Str2\" } }) { Property1 = \"Str4\" } };\n            var comparer = new Comparer<A>();\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(\"NonGenericEnumerableImplementation.Property1\", differences.First().MemberPath);\n            Assert.AreEqual(\"Str3\", differences.First().Value1);\n            Assert.AreEqual(\"Str4\", differences.First().Value2);\n        }\n\n        [Test]\n        public void NullAndEmptyInequality()\n        {\n            var a1 = new A { NonGenericEnumerable = new ArrayList() };\n            var a2 = new A();\n            var comparer = new Comparer<A>();\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(\"NonGenericEnumerable[]\", differences.First().MemberPath);\n            Assert.AreEqual(\"System.Collections.ArrayList\", differences.First().Value1);\n            Assert.AreEqual(string.Empty, differences.First().Value2);\n        }\n\n        [Test]\n        public void NullAndEmptyEquality()\n        {\n            var a1 = new A { NonGenericEnumerable = new ArrayList() };\n            var a2 = new A();\n            var comparer = new Comparer<A>(new ComparisonSettings { EmptyAndNullEnumerablesEqual = true });\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/Comparer_OverridesTests.cs",
    "content": "﻿using System.Linq;\nusing System.Reflection;\nusing NSubstitute;\nusing NUnit.Framework;\nusing ObjectsComparer.Tests.TestClasses;\n\nnamespace ObjectsComparer.Tests\n{\n    [TestFixture]\n    public class ComparerOverridesTests\n    {\n        [Test]\n        public void OverrideStringComparisonWhenEqual()\n        {\n            var a1 = new A { TestProperty1 = \"ABC\", TestProperty2 = \"ABC\", ClassB = new B { Property1 = \"Str1\" } };\n            var a2 = new A { TestProperty1 = \"BCD\", TestProperty2 = \"ABC\", ClassB = new B { Property1 = \"Str2\" } };\n            var comparer = new Comparer<A>();\n            var stringComparer = Substitute.For<IValueComparer>();\n            stringComparer.Compare(Arg.Any<object>(), Arg.Any<object>(), Arg.Any<ComparisonSettings>()).Returns(true);\n            comparer.AddComparerOverride<string>(stringComparer);\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n            stringComparer.Received().Compare(Arg.Any<object>(), Arg.Any<object>(), Arg.Any<ComparisonSettings>());\n        }\n\n        [Test]\n        public void OverrideStringComparisonWhenNotEqual()\n        {\n            var a1 = new A { TestProperty1 = \"ABC\", TestProperty2 = \"ABC\", ClassB = new B { Property1 = \"Str1\" } };\n            var a2 = new A { TestProperty1 = \"BCD\", TestProperty2 = \"ABC\", ClassB = new B { Property1 = \"Str1\" } };\n            var comparer = new Comparer<A>();\n            var stringComparer = Substitute.For<IValueComparer>();\n            stringComparer.Compare(Arg.Any<object>(), Arg.Any<object>(), Arg.Any<ComparisonSettings>()).Returns(false);\n            stringComparer.ToString(Arg.Any<object>()).Returns(info => (info.Arg<object>() ?? string.Empty).ToString());\n            comparer.AddComparerOverride(typeof(string), stringComparer);\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"TestProperty1\" && d.Value1 == \"ABC\" && d.Value2 == \"BCD\"));\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"TestProperty2\" && d.Value1 == \"ABC\" && d.Value2 == \"ABC\"));\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"ClassB.Property1\" && d.Value1 == \"Str1\" && d.Value2 == \"Str1\"));\n            stringComparer.Received().Compare(Arg.Any<object>(), Arg.Any<object>(), Arg.Any<ComparisonSettings>());\n            stringComparer.Received().ToString(Arg.Any<object>());\n        }\n\n        [Test]\n        public void OverrideIntComparisonWhenNotEqual()\n        {\n            var a1 = new A { IntArray = new[] { 1, 2 } };\n            var a2 = new A { IntArray = new[] { 1, 3 } };\n            var comparer = new Comparer<A>();\n            var stringComparer = Substitute.For<IValueComparer>();\n            stringComparer.Compare(Arg.Any<object>(), Arg.Any<object>(), Arg.Any<ComparisonSettings>()).Returns(false);\n            stringComparer.ToString(Arg.Any<object>()).Returns(info => (info.Arg<object>() ?? string.Empty).ToString());\n            comparer.AddComparerOverride<int>(stringComparer);\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"IntArray[0]\" && d.Value1 == \"1\" && d.Value2 == \"1\"));\n            Assert.IsTrue(differences.Any(d => d.MemberPath == \"IntArray[1]\" && d.Value1 == \"2\" && d.Value2 == \"3\"));\n            stringComparer.Received().Compare(Arg.Any<object>(), Arg.Any<object>(), Arg.Any<ComparisonSettings>());\n            stringComparer.Received().ToString(Arg.Any<object>());\n        }\n\n        [Test]\n        public void OverrideIntComparisonWhenEqual()\n        {\n            var a1 = new A { IntArray = new[] { 1, 2 } };\n            var a2 = new A { IntArray = new[] { 1, 3 } };\n            var comparer = new Comparer<A>();\n            var intComparer = Substitute.For<IValueComparer>();\n            intComparer.Compare(Arg.Any<object>(), Arg.Any<object>(), Arg.Any<ComparisonSettings>()).Returns(true);\n            intComparer.ToString(Arg.Any<object>()).Returns(info => (info.Arg<object>() ?? string.Empty).ToString());\n            comparer.AddComparerOverride(typeof(int), intComparer);\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n            intComparer.Received().Compare(Arg.Any<object>(), Arg.Any<object>(), Arg.Any<ComparisonSettings>());\n        }\n\n        [Test]\n        public void OverridePropertyComparisonWhenEqual()\n        {\n            var a1 = new A { TestProperty1 = \"ABC\" };\n            var a2 = new A { TestProperty1 = \"BCD\" };\n            var comparer = new Comparer<A>();\n            var valueComparer = Substitute.For<IValueComparer>();\n            valueComparer.Compare(Arg.Any<object>(), Arg.Any<object>(), Arg.Any<ComparisonSettings>()).Returns(true);\n            comparer.AddComparerOverride(() => a1.TestProperty1, valueComparer);\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n            valueComparer.Received(1).Compare(Arg.Any<object>(), Arg.Any<object>(), Arg.Any<ComparisonSettings>());\n        }\n\n        [Test]\n        public void OverrideBClassComparerWhenEqual()\n        {\n            var a1 = new A { ClassB = new B { Property1 = \"123-456-7898\" } };\n            var a2 = new A { ClassB = new B { Property1 = \"(123)-456-7898\" } };\n            var valueComparer = CreateClassBComparerAsPhone();\n            var comparer = new Comparer<A>();\n            comparer.AddComparerOverride<B>(valueComparer);\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void OverrideBClassComparerWhenNotEqual()\n        {\n            var a1 = new A { ClassB = new B { Property1 = \"123-456-7898\" } };\n            var a2 = new A { ClassB = new B { Property1 = \"(123)-456-7899\" } };\n            var valueComparer = CreateClassBComparerAsPhone();\n            var comparer = new Comparer<A>();\n            comparer.AddComparerOverride<B>(valueComparer);\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(\"ClassB\", differences[0].MemberPath);\n            Assert.AreEqual(\"123-456-7898\", differences[0].Value1);\n            Assert.AreEqual(\"(123)-456-7899\", differences[0].Value2);\n        }\n\n        [Test]\n        public void OverrideBClassProperty1ComparerWhenEqual()\n        {\n            var a1 = new A { ClassB = new B { Property1 = \"123-456-7898\" } };\n            var a2 = new A { ClassB = new B { Property1 = \"(123)-456-7898\" } };\n            var valueComparer = CreatePhoneComparer();\n            var comparer = new Comparer<A>();\n            comparer.AddComparerOverride(() => new B().Property1, valueComparer);\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void OverrideBClassProperty1ComparerWhenNotEqual()\n        {\n            var a1 = new A { ClassB = new B { Property1 = \"123-456-7898\" } };\n            var a2 = new A { ClassB = new B { Property1 = \"(123)-456-7899\" } };\n            var valueComparer = CreatePhoneComparer();\n            var comparer = new Comparer<A>();\n            comparer.AddComparerOverride(() => new B().Property1, valueComparer);\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(\"ClassB.Property1\", differences[0].MemberPath);\n            Assert.AreEqual(\"123-456-7898\", differences[0].Value1);\n            Assert.AreEqual(\"(123)-456-7899\", differences[0].Value2);\n        }\n\n        [Test]\n        public void OverrideBClassProperty1ComparerWithFunction()\n        {\n            var a1 = new A { ClassB = new B { Property1 = \"123-456-7898\" } };\n            var a2 = new A { ClassB = new B { Property1 = \"(123)-456-7898\" } };\n            var comparer = new Comparer<A>();\n            comparer.AddComparerOverride(\n                () => new B().Property1,\n                (phone1, phone2, parentSettings) => ExtractDigits(phone1) == ExtractDigits(phone2),\n                s => s);\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void OverrideBClassProperty1ComparerWithFunctionAndDefaultToString()\n        {\n            var a1 = new A { ClassB = new B { Property1 = \"123-456-7898\" } };\n            var a2 = new A { ClassB = new B { Property1 = \"(123)-456-7899\" } };\n            var comparer = new Comparer<A>();\n            comparer.AddComparerOverride(\n                () => new B().Property1,\n                (phone1, phone2, parentSettings) => ExtractDigits(phone1) == ExtractDigits(phone2));\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(\"ClassB.Property1\", differences[0].MemberPath);\n            Assert.AreEqual(\"123-456-7898\", differences[0].Value1);\n            Assert.AreEqual(\"(123)-456-7899\", differences[0].Value2);\n        }\n\n        [Test]\n        public void OverrideBClassProperty1ByMemberInfo()\n        {\n            var a1 = new A { ClassB = new B { Property1 = \"S1\" } };\n            var a2 = new A { ClassB = new B { Property1 = \"S2\" } };\n            var comparer = new Comparer<A>();\n            var valueComparer = Substitute.For<IValueComparer>();\n            valueComparer.Compare(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<ComparisonSettings>()).Returns(true);\n            comparer.AddComparerOverride(\n                typeof(B).GetTypeInfo().GetMember(\"Property1\").First(),\n                valueComparer);\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsEmpty(differences);\n            valueComparer.Received().Compare(\"S1\", \"S2\", Arg.Any<ComparisonSettings>());\n        }\n\n        [Test]\n        public void OverrideByName()\n        {\n            var a1 = new A { ClassB = new B { Property1 = \"123-456-7899\" } };\n            var a2 = new A { ClassB = new B { Property1 = \"(123)-456-7899\" } };\n            var comparer = new Comparer<A>();\n            var phoneComparer = Substitute.For<IValueComparer>();\n            phoneComparer.Compare(\"123-456-7899\", \"(123)-456-7899\", Arg.Any<ComparisonSettings>()).Returns(true);\n\n            comparer.AddComparerOverride(\"Property1\", phoneComparer);\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsEmpty(differences);\n            phoneComparer.Received().Compare(\"123-456-7899\", \"(123)-456-7899\", Arg.Any<ComparisonSettings>());\n        }\n\n        [Test]\n        public void OverrideMemberHighestPriority()\n        {\n            var a1 = new A { ClassB = new B { Property1 = \"S1\" } };\n            var a2 = new A { ClassB = new B { Property1 = \"S2\" } };\n            var comparer = new Comparer<A>();\n            var valueComparer1 = Substitute.For<IValueComparer>();\n            valueComparer1.Compare(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<ComparisonSettings>()).Returns(true);\n            var valueComparer2 = Substitute.For<IValueComparer>();\n            valueComparer2.Compare(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<ComparisonSettings>()).Returns(true);\n            var valueComparer3 = Substitute.For<IValueComparer>();\n            valueComparer3.Compare(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<ComparisonSettings>()).Returns(true);\n\n            comparer.AddComparerOverride<string>(valueComparer1);\n            comparer.AddComparerOverride(() => a1.ClassB.Property1, valueComparer2);\n            comparer.AddComparerOverride(\"Property1\", valueComparer3);\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsEmpty(differences);\n            valueComparer2.Received().Compare(\"S1\", \"S2\", Arg.Any<ComparisonSettings>());\n            valueComparer1.DidNotReceive().Compare(\"S1\", \"S2\", Arg.Any<ComparisonSettings>());\n            valueComparer3.DidNotReceive().Compare(\"S1\", \"S2\", Arg.Any<ComparisonSettings>());\n        }\n\n        [Test]\n        public void OverrideMemberHigherPriorityThanByName()\n        {\n            var a1 = new A { ClassB = new B { Property1 = \"S1\" } };\n            var a2 = new A { ClassB = new B { Property1 = \"S2\" } };\n            var comparer = new Comparer<A>();\n            var valueComparer1 = Substitute.For<IValueComparer>();\n            valueComparer1.Compare(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<ComparisonSettings>()).Returns(true);\n            var valueComparer2 = Substitute.For<IValueComparer>();\n            valueComparer2.Compare(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<ComparisonSettings>()).Returns(true);\n\n            comparer.AddComparerOverride<string>(valueComparer1);\n            comparer.AddComparerOverride(() => a1.ClassB.Property1, valueComparer1);\n            comparer.AddComparerOverride(\"Property1\", valueComparer2);\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsEmpty(differences);\n            valueComparer1.Received().Compare(\"S1\", \"S2\", Arg.Any<ComparisonSettings>());\n            valueComparer2.DidNotReceive().Compare(\"S1\", \"S2\", Arg.Any<ComparisonSettings>());\n        }\n\n        [Test]\n        public void OverrideTypeWithFilterAndByName()\n        {\n            var a1 = new A { ClassB = new B { Property1 = \"S1\" } };\n            var a2 = new A { ClassB = new B { Property1 = \"S2\" } };\n            var comparer = new Comparer<A>();\n            var valueComparer1 = Substitute.For<IValueComparer>();\n            valueComparer1.Compare(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<ComparisonSettings>()).Returns(true);\n            var valueComparer2 = Substitute.For<IValueComparer>();\n            valueComparer2.Compare(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<ComparisonSettings>()).Returns(true);\n\n            comparer.AddComparerOverride<string>(valueComparer1, memberInfo => memberInfo.Name != \"Property1\");\n            comparer.AddComparerOverride(\"Property1\", valueComparer2);\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            CollectionAssert.IsEmpty(differences);\n            valueComparer1.DidNotReceive().Compare(\"S1\", \"S2\", Arg.Any<ComparisonSettings>());\n            valueComparer2.Received().Compare(\"S1\", \"S2\", Arg.Any<ComparisonSettings>());\n        }\n\n        [Test]\n        public void IgnoreByName()\n        {\n            var a1 = new A { TestProperty1 = \"Str1\", IntProperty = 5};\n            var a2 = new A { TestProperty1 = \"Str2\", IntProperty = 5 };\n            var comparer = new Comparer<A>();\n            \n            comparer.IgnoreMember(\"TestProperty1\");\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void IgnoreByType()\n        {\n            var a1 = new A { TestProperty1 = \"Str1\", IntProperty = 5 };\n            var a2 = new A { TestProperty1 = \"Str2\", IntProperty = 5 };\n            var comparer = new Comparer<A>();\n\n            comparer.IgnoreMember<string>();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void IgnoreByMember()\n        {\n            var a1 = new A { TestProperty1 = \"Str1\", IntProperty = 5 };\n            var a2 = new A { TestProperty1 = \"Str2\", IntProperty = 5 };\n            var comparer = new Comparer<A>();\n\n            comparer.IgnoreMember(() => new A().TestProperty1);\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void OverrideByMemberFilter()\n        {\n            var a1 = new A { TestProperty1 = \"ABC\" };\n            var a2 = new A { TestProperty1 = \"ABC\" };\n            var comparer = new Comparer<A>();\n            var valueComparer = Substitute.For<IValueComparer>();\n            valueComparer.Compare(Arg.Any<object>(), Arg.Any<object>(), Arg.Any<ComparisonSettings>()).Returns(true);\n            comparer.AddComparerOverride(valueComparer, member => member.Name == \"TestProperty1\");\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n            valueComparer.Received(1).Compare(Arg.Is(\"ABC\"), Arg.Is(\"ABC\"), Arg.Any<ComparisonSettings>());\n        }\n\n        [Test]\n        public void IgnoreByMemberFilter()\n        {\n            var a1 = new A { TestProperty1 = \"ABC\" };\n            var a2 = new A { TestProperty1 = \"BCD\" };\n            var comparer = new Comparer<A>();\n            comparer.IgnoreMember(member => member.Name == \"TestProperty1\");\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        private IValueComparer CreateClassBComparerAsPhone()\n        {\n            var valueComparer = Substitute.For<IValueComparer>();\n            valueComparer.Compare(Arg.Any<object>(), Arg.Any<object>(), Arg.Any<ComparisonSettings>()).Returns(callInfo =>\n            {\n                var b1 = (B)callInfo.ArgAt<object>(0);\n                var b2 = (B)callInfo.ArgAt<object>(1);\n\n                return ExtractDigits(b1.Property1) == ExtractDigits(b2.Property1);\n            });\n            valueComparer.ToString(Arg.Any<object>()).Returns(callInfo => ((B)callInfo.Arg<object>()).Property1);\n\n            return valueComparer;\n        }\n\n        private IValueComparer CreatePhoneComparer()\n        {\n            var valueComparer = Substitute.For<IValueComparer>();\n            valueComparer.Compare(Arg.Any<object>(), Arg.Any<object>(), Arg.Any<ComparisonSettings>()).Returns(callInfo =>\n            {\n                var s1 = (string)callInfo.ArgAt<object>(0);\n                var s2 = (string)callInfo.ArgAt<object>(1);\n\n                return ExtractDigits(s1) == ExtractDigits(s2);\n            });\n            valueComparer.ToString(Arg.Any<object>()).Returns(callInfo => (string)callInfo.Arg<object>());\n\n            return valueComparer;\n        }\n\n        private string ExtractDigits(string str)\n        {\n            return string.Join(string.Empty, (str ?? string.Empty).ToCharArray().Where(char.IsDigit));\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/Comparer_StringBuilderTests.cs",
    "content": "﻿using System.Linq;\nusing NUnit.Framework;\nusing System.Text;\n\nnamespace ObjectsComparer.Tests\n{\n    [TestFixture]\n    public class Comparer_StringBuilderTests\n    {\n        [Test]\n        public void StringBuilderEquality()\n        {\n            var a1 = new StringBuilder(\"abc\");\n            var a2 = new StringBuilder(\"abc\");\n            var comparer = new Comparer<StringBuilder>();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void StringBuilderInequality()\n        {\n                var a1 = new StringBuilder(\"abc\");\n                var a2 = new StringBuilder(\"abd\");\n                var comparer = new Comparer<StringBuilder>();\n\n            var isEqual = comparer.Compare(a1, a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(1, differences.Count);\n            Assert.AreEqual(string.Empty, differences.First().MemberPath);\n            Assert.AreEqual(\"abc\", differences.First().Value1);\n            Assert.AreEqual(\"abd\", differences.First().Value2);\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/Comparer_UriTests.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing NUnit.Framework;\n\nnamespace ObjectsComparer.Tests\n{\n    [TestFixture]\n    public class Comparer_UriTests\n    {\n        public class TestClass\n        {\n            public IList<Uri> Urls { get; } = new List<Uri>();\n        }\n\n        [Test]\n        public void Equality()\n        {\n            var a1 = new { MyUri = new Uri(\"https://www.google.com/\") };\n            var a2 = new { MyUri = new Uri(\"https://www.google.com/\") };\n            var comparer = new Comparer();\n\n            var isEqual = comparer.Compare(a1, a2);\n\n            Assert.IsTrue(isEqual);\n        }\n\n        [Test]\n        public void Inequality()\n        {\n            var a1 = new { MyUri = new Uri(\"https://www.google.com/\") };\n            var a2 = new { MyUri = new Uri(\"https://www.yahoo.com/\") };\n            var comparer = new Comparer();\n\n            var isEqual = comparer.Compare(a1, a2, out var differencesEnum);\n            var differences = differencesEnum.ToList();\n\n            Assert.IsFalse(isEqual);\n            CollectionAssert.IsNotEmpty(differences);\n            Assert.AreEqual(1, differences.Count);\n            Assert.AreEqual(\"MyUri\", differences.First().MemberPath);\n        }\n\n        [Test]\n        public void ListOfUris()\n        {\n            var a1 = new TestClass();\n            a1.Urls.Add(new Uri(\"https://Test.com\"));\n            var a2 = new TestClass();\n            a2.Urls.Add(new Uri(\"https://Test.com\"));\n\n            var comparer = new ObjectsComparer.Comparer<TestClass>();\n            //  comparer.AddComparerOverride<IList<Uri>>(new ListOfUriComparer());\n            IEnumerable<Difference> differences = new List<Difference>();\n            comparer.Compare(a1, a2, out differences);\n        }\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/ComparersFactoryTests.cs",
    "content": "﻿using System.Collections.Generic;\nusing NSubstitute;\nusing NUnit.Framework;\nusing ObjectsComparer.Tests.TestClasses;\n\nnamespace ObjectsComparer.Tests\n{\n    [TestFixture]\n    public class ComparersFactoryTests\n    {\n        private IComparer<B> _comparerB;\n\n        private class CustomFactory : ComparersFactory\n        {\n            private readonly IComparer<B> _comparerB;\n\n            public CustomFactory(IComparer<B> comparerB)\n            {\n                _comparerB = comparerB;\n            }\n\n            public override IComparer<T> GetObjectsComparer<T>(ComparisonSettings settings = null, BaseComparer parentComparer = null)\n            {\n                if (typeof(T) == typeof(B))\n                {\n                    return (IComparer<T>)_comparerB;\n                }\n\n                return base.GetObjectsComparer<T>(settings, parentComparer);\n            }\n        }\n\n        [SetUp]\n        public void SetUp()\n        {\n            _comparerB = Substitute.For<IComparer<B>>();\n        }\n\n        [Test]\n        public void GetObjectsComparerGeneric()\n        {\n            var settings = new ComparisonSettings();\n            var factory = new ComparersFactory();\n\n            var comparer = factory.GetObjectsComparer<string>(settings);\n\n            Assert.AreEqual(settings, comparer.Settings);\n        }\n\n        [Test]\n        public void GetObjectsComparer()\n        {\n            var settings = new ComparisonSettings();\n            var factory = new ComparersFactory();\n\n            var comparer = factory.GetObjectsComparer(typeof(string), settings);\n\n            Assert.AreEqual(settings, comparer.Settings);\n        }\n\n        [Test]\n        public void CustomFactoryGenericMethod()\n        {\n            var factory = new CustomFactory(_comparerB);\n\n            var comparer = factory.GetObjectsComparer<B>();\n\n            Assert.AreEqual(_comparerB, comparer);\n        }\n\n        [Test]\n        public void CustomFactoryNongenericMethod()\n        {\n            var factory = new CustomFactory(_comparerB);\n            var comparer = factory.GetObjectsComparer(typeof(B));\n            var b1 = new B();\n            var b2 = new B();\n            _comparerB.CalculateDifferences(b1, b2).Returns(new List<Difference>());\n\n            var isEqual = comparer.Compare(typeof(B), b1, b2);\n\n            Assert.IsTrue(isEqual);\n            _comparerB.Received().CalculateDifferences(b1, b2);\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/ComparisonSettingsTests.cs",
    "content": "﻿using System.Collections.Generic;\nusing NUnit.Framework;\n\nnamespace ObjectsComparer.Tests\n{\n    [TestFixture]\n    public class ComparisonSettingsTests\n    {\n        [Test]\n        public void SetgetCustomSettingWithoutKey()\n        {\n            var settings = new ComparisonSettings();\n            settings.SetCustomSetting(\"test string\");\n\n            var settingValue = settings.GetCustomSetting<string>();\n\n            Assert.AreEqual(\"test string\", settingValue);\n        }\n\n        [Test]\n        public void SetgetCustomSettingWithKey()\n        {\n            var settings = new ComparisonSettings();\n            settings.SetCustomSetting(123, \"setting1\");\n            settings.SetCustomSetting(234, \"setting2\");\n\n            var setting1Value = settings.GetCustomSetting<int>(\"setting1\");\n            var setting2Value = settings.GetCustomSetting<int>(\"setting2\");\n\n            Assert.AreEqual(123, setting1Value);\n            Assert.AreEqual(234, setting2Value);\n        }\n\n        [Test]\n        public void WronkKey()\n        {\n            var settings = new ComparisonSettings();\n            settings.SetCustomSetting(123, \"setting1\");\n\n            Assert.Throws<KeyNotFoundException>(() => settings.GetCustomSetting<int>(\"wrongSettingKey\"));\n        }\n\n        [Test]\n        public void WronkType()\n        {\n            var settings = new ComparisonSettings();\n            settings.SetCustomSetting(123, \"setting1\");\n\n            Assert.Throws<KeyNotFoundException>(() => settings.GetCustomSetting<double>(\"setting1\"));\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/CustomComparers/AbstractValueComparerTests.cs",
    "content": "﻿using NSubstitute;\nusing NUnit.Framework;\n\nnamespace ObjectsComparer.Tests\n{\n    [TestFixture]\n    public class AbstractValueComparerTests\n    {\n        [Test]\n        public void DefaultToString()\n        {\n            var valueComparer = Substitute.ForPartsOf<AbstractValueComparer<string>>();\n\n            var result = valueComparer.ToString(\"str1\");\n\n            Assert.AreEqual(\"str1\", result);\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/CustomComparers/DynamicValueComparerTests.cs",
    "content": "﻿using System;\nusing NUnit.Framework;\n\nnamespace ObjectsComparer.Tests\n{\n    [TestFixture]\n    public class DynamicValueComparerTests\n    {\n        [Test]\n        public void ConstructorNullCompareFunc()\n        {\n            // ReSharper disable once ObjectCreationAsStatement\n            Assert.Throws<ArgumentNullException>(() => new DynamicValueComparer<string>(null, s => s));\n        }\n\n        [Test]\n        public void ConstructorNullToStringFunc()\n        {\n            // ReSharper disable once ObjectCreationAsStatement\n            Assert.Throws<ArgumentNullException>(() => new DynamicValueComparer<string>((s1, s2, settings) => true, null));\n        }\n\n        [Test]\n        public void CompareWrongFirstArgument()\n        {\n            var comparer = new DynamicValueComparer<string>((s1, s2, settings) => true, s => s);\n\n            Assert.Throws<ArgumentException>(() => comparer.Compare(25, \"25\", new ComparisonSettings()));\n        }\n\n        [Test]\n        public void CompareWrongSecondArgument()\n        {\n            var comparer = new DynamicValueComparer<string>((s1, s2, settings) => true, s => s);\n\n            Assert.Throws<ArgumentException>(() => comparer.Compare(\"25\", 25, new ComparisonSettings()));\n        }\n\n        [Test]\n        public void CompareFirstArgumentNullNotValueType()\n        {\n            var comparer = new DynamicValueComparer<string>((s1, s2, settings) => s1 == s2, s => s);\n\n            var result = comparer.Compare(null, \"25\", new ComparisonSettings());\n\n            Assert.IsFalse(result);\n        }\n\n        [Test]\n        public void CompareFirstArgumentNullValueType()\n        {\n            var comparer = new DynamicValueComparer<int>((i1, i2, settings) => i1 == i2, i => i.ToString());\n\n            Assert.Throws<ArgumentException>(() => comparer.Compare(null, 25, new ComparisonSettings()));\n        }\n\n        [Test]\n        public void CompareSecondArgumentNullNotValueType()\n        {\n            var comparer = new DynamicValueComparer<string>((s1, s2, settings) => s1 == s2, s => s);\n\n            var result = comparer.Compare(\"23\", null, new ComparisonSettings());\n\n            Assert.IsFalse(result);\n        }\n\n        [Test]\n        public void CompareSecondArgumentNullValueType()\n        {\n            var comparer = new DynamicValueComparer<int>((i1, i2, settings) => i1 == i2, i => i.ToString());\n\n            Assert.Throws<ArgumentException>(() => comparer.Compare(23, null, new ComparisonSettings()));\n        }\n\n        [TestCase(\"Str1\", \"Str2\", true)]\n        [TestCase(\"Str1\", \"Str1\", true)]\n        [TestCase(\"Str1\", \"Str\", false)]\n        public void Compare(string str1, string str2, bool expectedResult)\n        {\n            var comparer = new DynamicValueComparer<string>((s1, s2, settings) => s1.Length == s2.Length, s => s);\n\n            var result = comparer.Compare(str1, str2, new ComparisonSettings());\n            var toString = comparer.ToString(str1);\n\n            Assert.AreEqual(expectedResult, result);\n            Assert.AreEqual(str1, toString);\n        }\n\n        [TestCase(\"Str1\", \"Str2\", true)]\n        [TestCase(\"Str1\", \"Str1\", true)]\n        [TestCase(\"Str1\", \"Str\", false)]\n        public void CompareWithDefaultToString(string str1, string str2, bool expectedResult)\n        {\n            var comparer = new DynamicValueComparer<string>((s1, s2, settings) => s1.Length == s2.Length);\n\n            var result = comparer.Compare(str1, str2, new ComparisonSettings());\n            var toString = comparer.ToString(str1);\n\n            Assert.AreEqual(expectedResult, result);\n            Assert.AreEqual(str1, toString);\n        }\n        \n        [Test]\n        public void NulableType()\n        {\n            DateTime? dt1 = null;\n            DateTime? dt2 = null;\n            var dateComparer = new DynamicValueComparer<DateTime?>\n                ((date1, date2, settings) => date1 == date2 || (date1 != null && date2 != null && date1.Value.Date == date2.Value.Date));\n            \n            var comparer = new Comparer<DateTime?>();\n            comparer.AddComparerOverride<DateTime?>(dateComparer);\n\n            var result = comparer.Compare(dt1, dt2);\n            \n            Assert.IsTrue(result);\n        }\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/CustomComparers/EnumerablesComparerGenericTests.cs",
    "content": "﻿using NUnit.Framework;\nusing System;\nusing System.Collections.Generic;\nusing ObjectsComparer.Tests.TestClasses;\n\nnamespace ObjectsComparer.Tests\n{\n    [TestFixture]\n    public class EnumerablesComparerGenericTests\n    {\n        [Test]\n        public void FirstParameterNotEnumerable()\n        {\n            var comparer = new EnumerablesComparer<string>(new ComparisonSettings(), null, null);\n            var obj1 = new List<int>();\n            var obj2 = new List<string>();\n\n            Assert.Throws<ArgumentException>(() => comparer.Compare(typeof(List<int>), obj1, obj2));\n        }\n\n        [Test]\n        public void SecondParameterNotEnumerable()\n        {\n            var comparer = new EnumerablesComparer<string>(new ComparisonSettings(), null, null);\n            var obj1 = new List<string>();\n            var obj2 = new A();\n\n            Assert.Throws<ArgumentException>(() => comparer.Compare(typeof(List<string>), obj1, obj2));\n        }\n\n        [Test]\n        public void TypeNotIEnumerable()\n        {\n            var comparer = new EnumerablesComparer<string>(new ComparisonSettings(), null, null);\n            var obj1 = new List<string>();\n            var obj2 = new List<string>();\n\n            Assert.Throws<ArgumentException>(() => comparer.Compare(typeof(int), obj1, obj2));\n        }\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/CustomComparers/EnumerablesComparerTests.cs",
    "content": "﻿using NUnit.Framework;\nusing System;\nusing System.Collections.Generic;\nusing ObjectsComparer.Tests.TestClasses;\n\nnamespace ObjectsComparer.Tests\n{\n    [TestFixture]\n    public class EnumerablesComparerTests\n    {\n        [Test]\n        public void FirstParameterNotEnumerable()\n        {\n            var comparer = new EnumerablesComparer(new ComparisonSettings(), null, null);\n            var obj1 = 25;\n            var obj2 = new List<string>();\n\n            Assert.Throws<ArgumentException>(() => comparer.Compare(typeof(List<string>), obj1, obj2));\n        }\n\n        [Test]\n        public void SecondParameterNotEnumerable()\n        {\n            var comparer = new EnumerablesComparer(new ComparisonSettings(), null, null);\n            var obj1 = new List<string>();\n            var obj2 = new A();\n\n            Assert.Throws<ArgumentException>(() => comparer.Compare(typeof(List<string>), obj1, obj2));\n        }\n\n        [Test]\n        public void TypeNotIEnumerable()\n        {\n            var comparer = new EnumerablesComparer(new ComparisonSettings(), null, null);\n            var obj1 = new List<string>();\n            var obj2 = new List<string>();\n\n            Assert.Throws<ArgumentException>(() => comparer.Compare(typeof(int), obj1, obj2));\n        }\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/CustomComparers/HashSetsComparerTests.cs",
    "content": "﻿using NUnit.Framework;\nusing System;\nusing System.Collections.Generic;\nusing ObjectsComparer.Tests.TestClasses;\n\nnamespace ObjectsComparer.Tests\n{\n    [TestFixture]\n    public class HashSetsComparerTests\n    {\n        [Test]\n        public void FirstParameterNotEnumerable()\n        {\n            var comparer = new HashSetsComparer<string>(new ComparisonSettings(), null, null);\n            var obj1 = 25;\n            var obj2 = new HashSet<string>();\n\n            Assert.Throws<ArgumentException>(() => comparer.Compare(typeof(HashSet<string>), obj1, obj2));\n        }\n\n        [Test]\n        public void SecondParameterNotEnumerable()\n        {\n            var comparer = new HashSetsComparer<string>(new ComparisonSettings(), null, null);\n            var obj1 = new HashSet<string>();\n            var obj2 = new A();\n\n            Assert.Throws<ArgumentException>(() => comparer.Compare(typeof(HashSet<string>), obj1, obj2));\n        }\n\n        [Test]\n        public void TypeNotHashSet()\n        {\n            var comparer = new HashSetsComparer<string>(new ComparisonSettings(), null, null);\n            var obj1 = new HashSet<string>();\n            var obj2 = new HashSet<string>();\n\n            Assert.Throws<ArgumentException>(() => comparer.Compare(typeof(int), obj1, obj2));\n        }\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/CustomComparers/TypesComparerTests.cs",
    "content": "﻿using NUnit.Framework;\nusing System;\n\nnamespace ObjectsComparer.Tests\n{\n    [TestFixture]\n    public class TypesComparerTests\n    {\n        [Test]\n        public void FirstParameterNotType()\n        {\n            var comparer = new TypesComparer(new ComparisonSettings(), null, null);\n            var obj1 = 25;\n            var obj2 = typeof(string);\n\n            Assert.Throws<ArgumentException>(() => comparer.Compare(typeof(Type), obj1, obj2));\n        }\n\n        [Test]\n        public void SecondParameterNotEnumerable()\n        {\n            var comparer = new TypesComparer(new ComparisonSettings(), null, null);\n            var obj1 = typeof(string);\n            var obj2 = 25;\n\n            Assert.Throws<ArgumentException>(() => comparer.Compare(typeof(Type), obj1, obj2));\n        }\n\n        [Test]\n        public void NullsAreEqual()\n        {\n            var comparer = new TypesComparer(new ComparisonSettings(), null, null);\n\n            Assert.IsTrue(comparer.Compare(typeof(Type), null, null));\n        }\n\n        [Test]\n        public void SameTypesAreEqual()\n        {\n            var comparer = new TypesComparer(new ComparisonSettings(), null, null);\n            var obj1 = typeof(string);\n            var obj2 = typeof(string);\n\n            Assert.IsTrue(comparer.Compare(typeof(Type), obj1, obj2));\n        }\n\n        [Test]\n        public void DifferentTypesAreNotEqual()\n        {\n            var comparer = new TypesComparer(new ComparisonSettings(), null, null);\n            var obj1 = typeof(string);\n            var obj2 = typeof(bool);\n\n            Assert.IsFalse(comparer.Compare(typeof(Type), obj1, obj2));\n        }\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/DifferenceTests.cs",
    "content": "﻿using NUnit.Framework;\n\nnamespace ObjectsComparer.Tests\n{\n    [TestFixture]\n    public class DifferenceTests\n    {\n        [Test]\n        public void DifferenceToString()\n        {\n            var difference = new Difference(\"Property1\", \"12345\", \"12346\");\n\n            var toString = difference.ToString();\n\n            Assert.IsTrue(toString.Contains(\"Property1\"));\n            Assert.IsTrue(toString.Contains(\"12345\"));\n            Assert.IsTrue(toString.Contains(\"12346\"));\n        }\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/ObjectsComparer.Tests.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <TargetFrameworks>netcoreapp3.0;netcoreapp2.0;net45</TargetFrameworks>\n  </PropertyGroup>\n  <ItemGroup>\n    <PackageReference Include=\"Microsoft.CSharp\" Version=\"4.5.0\" />\n    <PackageReference Include=\"Microsoft.NET.Test.Sdk\" Version=\"15.8.0\" />\n    <PackageReference Include=\"Newtonsoft.Json\" Version=\"11.0.2\" />\n    <PackageReference Include=\"NSubstitute\" Version=\"3.1.0\" />\n    <PackageReference Include=\"NUnit\" Version=\"3.10.1\" />\n    <PackageReference Include=\"NUnit3TestAdapter\" Version=\"3.10.0\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\ObjectsComparer\\ObjectsComparer.csproj\" />\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/ObjectsComparer.Tests.csproj.DotSettings",
    "content": "﻿<wpf:ResourceDictionary xml:space=\"preserve\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" xmlns:s=\"clr-namespace:System;assembly=mscorlib\" xmlns:ss=\"urn:shemas-jetbrains-com:settings-storage-xaml\" xmlns:wpf=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n\t<s:Boolean x:Key=\"/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=customcomparers/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=valuecomparers/@EntryIndexedValue\">True</s:Boolean></wpf:ResourceDictionary>"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/ParentInterfacePropertiesTests.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Linq;\nusing NUnit.Framework;\n\nnamespace ObjectsComparer.Tests\n{\n    [TestFixture]\n    public class ParentInterfacePropertiesTests\n    {\n        interface IParent\n        {\n            string Property1 { get; set; }\n        }\n\n        interface IChild : IParent\n        {\n            string Property2 { get; set; }\n        }\n\n        class Child : IChild\n        {\n            public string Property1 { get; set; }\n            public string Property2 { get; set; }\n        }\n\n        [Test]\n        public void ComparePropertyOfParentInterface()\n        {\n            var a1 = new Child {Property1 = \"str11\", Property2 = \"str12\"};\n            var a2 = new Child {Property1 = \"str21\", Property2 = \"str22\"};\n\n            var comparer = new Comparer<IChild>();\n\n            var differences = comparer.CalculateDifferences(a1, a2).ToList();\n\n            Assert.AreEqual(2, differences.Count);\n            Assert.AreEqual(\"Property1\", differences[1].MemberPath);\n            Assert.AreEqual(\"str11\", differences[1].Value1);\n            Assert.AreEqual(\"str21\", differences[1].Value2);\n            Assert.AreEqual(\"Property2\", differences[0].MemberPath);\n            Assert.AreEqual(\"str12\", differences[0].Value1);\n            Assert.AreEqual(\"str22\", differences[0].Value2);\n        }\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/TestClasses/A.cs",
    "content": "﻿using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.Diagnostics;\n\nnamespace ObjectsComparer.Tests.TestClasses\n{\n    internal class A\n    {\n        public int Field;\n\n        protected int ProtectedField;\n\n        public readonly string ReadOnlyField;\n\n        public int IntProperty { get; set; }\n\n        public string TestProperty1 { get; set; }\n\n        public string TestProperty2 { get; set; }\n\n        public DateTime DateTimeProperty { get; set; }\n\n        public double ReadOnlyProperty { get; }\n\n        protected bool ProtectedProperty { get; }\n\n        public B ClassB { get; set; }\n\n        public int[] IntArray { get; set; }\n\n        public B[] ArrayOfB { get; set; }\n\n        public Collection<B> CollectionOfB { get; set; }\n\n        public IEnumerable<B> EnumerableOfB { get; set; }\n\n        public List<B> ListOfB { get; set; }\n\n        public Dictionary<int, B> DictionaryOfB { get; set; }\n\n        public CollectionOfB ClassImplementsCollectionOfB { get; set; }\n\n        public ITestInterface IntefaceProperty { get; set; }\n\n        public TestStruct StructProperty { get; set; }\n\n        public TestEnum EnumProperty { get; set; }\n\n        public IEnumerable NonGenericEnumerable { get; set; }\n\n        public EnumerableImplementation NonGenericEnumerableImplementation { get; set; }\n\n        public FlagsEnum Flags { get; set; }\n\n        public Type TypeProperty { get; set; }\n\n        public int Property3\n        {\n            set\n            {\n                if (value > 3)\n                {\n                    Debug.WriteLine(\"value > 3\");\n                }\n            }\n        }\n\n        public A()\n        {\n            ReadOnlyProperty = 3.14;\n        }\n\n        public A(double readOnlyProperty)\n        {\n            ReadOnlyProperty = readOnlyProperty;\n        }\n\n        public A(int protectedField)\n        {\n            ProtectedField = protectedField;\n        }\n\n        public A(string readOnlyField)\n        {\n            ReadOnlyField = readOnlyField;\n        }\n\n        public A(bool protectedProperty)\n        {\n            ProtectedProperty = protectedProperty;\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/TestClasses/B.cs",
    "content": "﻿namespace ObjectsComparer.Tests.TestClasses\n{\n    public class B\n    {\n        public string Property1 { get; set; }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/TestClasses/Child.cs",
    "content": "﻿namespace ObjectsComparer.Tests.TestClasses\n{\n  using ObjectsComparer.Attributes;\n\n  public class Child\n  {\n    #region Constructors\n\n    public Child(string property1,\n                 string property2)\n    {\n      this.Property1 = property1;\n      this.Property2 = property2;\n    }\n\n    #endregion\n\n    #region Properties\n\n    public string Property1 { get; set; }\n\n    [IgnoreInComparison]\n    public string Property2 { get; set; }\n\n    #endregion\n  }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/TestClasses/CollectionOfB.cs",
    "content": "﻿using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.Diagnostics;\n\nnamespace ObjectsComparer.Tests.TestClasses\n{\n    internal class CollectionOfB: IProgress<string>, ICollection<B>\n    {\n        public string Property1 { get; set; }\n\n        public int Count => _collection.Count;\n        public bool IsReadOnly => false;\n\n        private readonly Collection<B> _collection;\n        \n        public CollectionOfB()\n        {\n            _collection = new Collection<B>();\n        }\n\n        public void Report(string value) => Debug.WriteLine(value);\n\n        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();\n\n        public IEnumerator<B> GetEnumerator() => _collection.GetEnumerator();\n\n        public void Add(B item) => _collection.Add(item);\n\n        public void Clear() => _collection.Clear();\n\n        public bool Contains(B item) => _collection.Contains(item);\n\n        public void CopyTo(B[] array, int arrayIndex) => _collection.CopyTo(array, arrayIndex);\n\n        public bool Remove(B item) => _collection.Remove(item);\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/TestClasses/EnumerableImplementation.cs",
    "content": "﻿using System.Collections;\n\nnamespace ObjectsComparer.Tests.TestClasses\n{\n    internal class EnumerableImplementation: IEnumerable\n    {\n        public string Property1 { get; set; }\n\n        private readonly IEnumerable _enumerable;\n\n        public EnumerableImplementation(IEnumerable enumerable)\n        {\n            _enumerable = enumerable;\n        }\n\n        public IEnumerator GetEnumerator()\n        {\n            return _enumerable.GetEnumerator();\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/TestClasses/FlagsEnum.cs",
    "content": "﻿using System;\n\nnamespace ObjectsComparer.Tests.TestClasses\n{\n    [Flags]\n    public enum FlagsEnum\n    {\n        Flag1 = 1,\n        Flag2 = 2,\n        Flag3 = 4,\n        Flag4 = 8\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/TestClasses/ITestInterface.cs",
    "content": "﻿namespace ObjectsComparer.Tests.TestClasses\n{\n    internal interface ITestInterface\n    {\n        string Property { get; set; }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/TestClasses/InheritedFromB.cs",
    "content": "﻿namespace ObjectsComparer.Tests.TestClasses\n{\n    internal class InheritedFromB: B\n    {\n        public string NewProperty { get; set; }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/TestClasses/MultidimensionalArrays.cs",
    "content": "﻿namespace ObjectsComparer.Tests.TestClasses\n{\n    public class MultidimensionalArrays\n    {\n        public int[,] IntInt { get; set; }\n\n        public int[][] IntOfInt { get; set; }\n\n        public int[][,] IntOfIntInt { get; set; }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/TestClasses/Parent.cs",
    "content": "﻿namespace ObjectsComparer.Tests.TestClasses\n{\n  using System.Collections.ObjectModel;\n\n  public class Parent\n    {\n        public string Property1 { get; set; }\n\n        public ObservableCollection <ParentChild> Child1 { get; set; } = new ObservableCollection <ParentChild>();\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/TestClasses/ParentChild.cs",
    "content": "﻿namespace ObjectsComparer.Tests.TestClasses\n{\n  using System.Collections.ObjectModel;\n\n  using NUnit.Framework;\n\n  using ObjectsComparer.Attributes;\n\n  public class ParentChild\n  {\n    #region Constructors\n\n    public ParentChild(Parent myParent,\n                       string property1)\n    {\n      this.MyParent = myParent;\n      this.Property1 = property1;\n    }\n\n\n\n    public ParentChild(Parent myParent,\n                       string property1,\n                       ObservableCollection <Child> children)\n    {\n      this.MyParent = myParent;\n      this.Property1 = property1;\n      this.Children = children;\n    }\n\n    #endregion\n\n    #region Properties\n\n    public string Property1 { get; set; }\n\n    [IgnoreInComparison]\n    public Parent MyParent { get; set; }\n\n    public ObservableCollection <Child> Children { get; set; } = new ObservableCollection <Child>();\n\n    #endregion\n  }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/TestClasses/TestEnum.cs",
    "content": "﻿namespace ObjectsComparer.Tests.TestClasses\n{\n    internal enum TestEnum\n    {\n        Value1,\n        Value2\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/TestClasses/TestInterfaceImplementation1.cs",
    "content": "﻿namespace ObjectsComparer.Tests.TestClasses\n{\n    internal class TestInterfaceImplementation1: ITestInterface\n    {\n        public string Property { get; set; }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/TestClasses/TestInterfaceImplementation2.cs",
    "content": "﻿namespace ObjectsComparer.Tests.TestClasses\n{\n    internal class TestInterfaceImplementation2: ITestInterface\n    {\n        public string Property { get; set; }\n\n        public int AnotherProperty { get; set; }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/TestClasses/TestStruct.cs",
    "content": "﻿namespace ObjectsComparer.Tests.TestClasses\n{\n    internal struct TestStruct\n    {\n        public string FieldA;\n\n        public string FieldB;\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/Utils/TypeExtensionsTests.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing NUnit.Framework;\nusing ObjectsComparer.Utils;\n\nnamespace ObjectsComparer.Tests.Utils\n{\n    [TestFixture]\n    public class TypeExtensionsTests\n    {\n        [Test]\n        public void InheritsFromFirstTypeNull()\n        {\n            var result = ((Type) null).InheritsFrom(typeof(string));\n\n            Assert.IsFalse(result);\n        }\n\n        [Test]\n        public void InheritsFromSecondTypeNull()\n        {\n            var result = typeof(string).InheritsFrom(null);\n\n            Assert.IsFalse(result);\n        }\n\n        [Test]\n        public void InheritsFromEqualTypes()\n        {\n            var result = typeof(string).InheritsFrom(typeof(string));\n\n            Assert.IsTrue(result);\n        }\n\n        [Test]\n        public void InheritsFromEqualGenericTypes()\n        {\n            var result = typeof(List<string>).InheritsFrom(typeof(List<string>));\n\n            Assert.IsTrue(result);\n        }\n\n        [Test]\n        public void InheritsFromEqualGenericTypeAndGenericDefinition()\n        {\n            var result = typeof(List<string>).InheritsFrom(typeof(List<>));\n\n            Assert.IsTrue(result);\n        }\n\n        [Test]\n        public void InheritsFromEqualGenericTypeAndGenericInterface()\n        {\n            var result = typeof(List<string>).InheritsFrom(typeof(IList<>));\n\n            Assert.IsTrue(result);\n        }\n\n        [Test]\n        public void InheritsFromEqualTypeAndBaseType()\n        {\n            var result = typeof(string).InheritsFrom(typeof(object));\n\n            Assert.IsTrue(result);\n        }\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/ValueComparers/DefaultValueComparerTests.cs",
    "content": "﻿using NUnit.Framework;\n\nnamespace ObjectsComparer.Tests\n{\n    [TestFixture]\n    public class DefaultValueComparerTests\n    {\n        [TestCase(null, null, true)]\n        [TestCase(5, null, false)]\n        [TestCase(null, \"string\", false)]\n        [TestCase(5, \"string\", false)]\n        [TestCase(5, 5, true)]\n        [TestCase(5, 7, false)]\n        [TestCase(\"string1\", \"string1\", true)]\n        [TestCase(\"string1\", \"string2\", false)]\n        public void Compare(object obj1, object obj2, bool expectedResult)\n        {\n            var comparer = new DefaultValueComparer();\n\n            var actualResult = comparer.Compare(obj1, obj2, new ComparisonSettings());\n\n            Assert.AreEqual(expectedResult, actualResult);\n        }\n\n        [TestCase(null, \"\")]\n        [TestCase(5, \"5\")]\n        [TestCase(\"str1\", \"str1\")]\n        [TestCase(true, \"True\")]\n        public void Compare(object value, string expectedResult)\n        {\n            var comparer = new DefaultValueComparer();\n\n            var actualResult = comparer.ToString(value);\n\n            Assert.AreEqual(expectedResult, actualResult);\n        }\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/ValueComparers/DefaultValueValueComparerTests.cs",
    "content": "﻿using NUnit.Framework;\n\nnamespace ObjectsComparer.Tests\n{\n    [TestFixture]\n    public class DefaultValueValueComparerTests\n    {\n        [TestCase(0, 5, true)]\n        [TestCase(null, 5, true)]\n        [TestCase(5, 0, true)]\n        [TestCase(5, null, true)]\n        [TestCase(5, 0, true)]\n        [TestCase(0, 5, true)]\n        [TestCase(0, 0, true)]\n        [TestCase(5, 5, true)]\n        [TestCase(null, 7, false)]\n        [TestCase(0, 7, false)]\n        [TestCase(5, 7, false)]\n        [TestCase(7, null, false)]\n        [TestCase(7, 0, false)]\n        [TestCase(7, 5, false)]\n        [TestCase(7, 8, false)]\n        public void ValueType(object obj1, object obj2, bool expectedResult)\n        {\n            var comparer = new DefaultValueValueComparer<int>(5, new DefaultValueComparer());\n\n            var actualResult = comparer.Compare(obj1, obj2, new ComparisonSettings());\n\n            Assert.AreEqual(expectedResult, actualResult);\n        }\n\n        [TestCase(null, \"none\", true)]\n        [TestCase(\"none\", null, true)]\n        [TestCase(null, null, true)]\n        [TestCase(\"none\", \"none\", true)]\n        [TestCase(null, \"string\", false)]\n        [TestCase(\"string\", null, false)]\n        [TestCase(\"none\", \"string\", false)]\n        [TestCase(\"string\", \"none\", false)]\n        [TestCase(\"string1\", \"string2\", false)]\n        public void ReferenceType(object obj1, object obj2, bool expectedResult)\n        {\n            var comparer = new DefaultValueValueComparer<string>(\"none\", new DefaultValueComparer());\n\n            var actualResult = comparer.Compare(obj1, obj2, new ComparisonSettings());\n\n            Assert.AreEqual(expectedResult, actualResult);\n        }\n    }\n}\n"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/ValueComparers/DoNotCompareValueComparerTests.cs",
    "content": "﻿using NUnit.Framework;\n\nnamespace ObjectsComparer.Tests\n{\n    [TestFixture]\n    public class DoNotCompareValueComparerTests\n    {\n        [Test]\n        public void Instance()\n        {\n            Assert.IsNotNull(DoNotCompareValueComparer.Instance);\n        }\n\n        [Test]\n        public void Compare()\n        {\n            var result = DoNotCompareValueComparer.Instance.Compare(25, \"String\", new ComparisonSettings());\n\n            Assert.IsTrue(result);\n        }\n\n        [TestCase(25)]\n        [TestCase(\"String\")]\n        [TestCase(12.5)]\n        [TestCase(null)]\n        public void ToString(object value)\n        {\n            var result = DoNotCompareValueComparer.Instance.ToString(value);\n\n            Assert.AreEqual(string.Empty, result);\n        }\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/ValueComparers/IgnoreCaseStringsValueComparerTests.cs",
    "content": "﻿using NUnit.Framework;\n\nnamespace ObjectsComparer.Tests\n{\n    [TestFixture]\n    public class IgnoreCaseStringsValueComparerTests\n    {\n        [TestCase(null, null, true)]\n        [TestCase(\"\", \"\", true)]\n        [TestCase(\"str\", \"str\", true)]\n        [TestCase(null, \"s\", false)]\n        [TestCase(\"hhh\", null, false)]\n        [TestCase(\"hhh\", \"s\", false)]\n        [TestCase(\"hhh\", \"HHH\", true)]\n        [TestCase(\"hhh\", \"HHH\", true)]\n        [TestCase(\"Go\", \"go\", true)]\n        public void Compare(string s1, string s2, bool expectedResult)\n        {\n            var comparer = new IgnoreCaseStringsValueComparer();\n\n            var result = comparer.Compare(s1, s2, new ComparisonSettings());\n\n            Assert.AreEqual(expectedResult, result);\n        }\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.Tests/ValueComparers/NulableStringsValueComparerTests.cs",
    "content": "﻿using NUnit.Framework;\n\nnamespace ObjectsComparer.Tests\n{\n    [TestFixture]\n    public class NulableStringsValueComparerTests\n    {\n        [TestCase(null, null, true)]\n        [TestCase(null, \"\", true)]\n        [TestCase(\"\", null, true)]\n        [TestCase(\"\", \"\", true)]\n        [TestCase(\"str\", \"str\", true)]\n        [TestCase(null, \"s\", false)]\n        [TestCase(\"hhh\", null, false)]\n        [TestCase(\"hhh\", \"s\", false)]\n        public void Compare(string s1, string s2, bool expectedResult)\n        {\n            var comparer = new NulableStringsValueComparer();\n\n            var result = comparer.Compare(s1, s2, new ComparisonSettings());\n\n            Assert.AreEqual(expectedResult, result);\n        }\n\n        [TestCase(null, \"\")]\n        [TestCase(\"\", \"\")]\n        [TestCase(\"ss\", \"ss\")]\n        public void ToString(string s, string expectedToString)\n        {\n            var comparer = new NulableStringsValueComparer();\n\n            var result = comparer.ToString(s);\n\n            Assert.AreEqual(expectedToString, result);\n        }\n    }\n}"
  },
  {
    "path": "ObjectsComparer/ObjectsComparer.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio 15\nVisualStudioVersion = 15.0.27428.2011\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"ObjectsComparer\", \"ObjectsComparer\\ObjectsComparer.csproj\", \"{1EE94257-5FF4-4453-BF88-BCA30AFCA126}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"ObjectsComparer.Tests\", \"ObjectsComparer.Tests\\ObjectsComparer.Tests.csproj\", \"{73D87409-FEBB-4CFF-90EE-B1F0F8BAFDF7}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"ObjectsComparer.Examples\", \"ObjectsComparer.Examples\\ObjectsComparer.Examples.csproj\", \"{EC727489-24B0-4654-B454-E58698DF384A}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|Any CPU = Debug|Any CPU\n\t\tRelease|Any CPU = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{1EE94257-5FF4-4453-BF88-BCA30AFCA126}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{1EE94257-5FF4-4453-BF88-BCA30AFCA126}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{1EE94257-5FF4-4453-BF88-BCA30AFCA126}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{1EE94257-5FF4-4453-BF88-BCA30AFCA126}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{73D87409-FEBB-4CFF-90EE-B1F0F8BAFDF7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{73D87409-FEBB-4CFF-90EE-B1F0F8BAFDF7}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{73D87409-FEBB-4CFF-90EE-B1F0F8BAFDF7}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{73D87409-FEBB-4CFF-90EE-B1F0F8BAFDF7}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{EC727489-24B0-4654-B454-E58698DF384A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{EC727489-24B0-4654-B454-E58698DF384A}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{EC727489-24B0-4654-B454-E58698DF384A}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{EC727489-24B0-4654-B454-E58698DF384A}.Release|Any CPU.Build.0 = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\n\tGlobalSection(ExtensibilityGlobals) = postSolution\n\t\tSolutionGuid = {3DFB6A53-C530-438E-BB1A-A29CE93CC23F}\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "README.md",
    "content": "![Logo](https://github.com/ValeraT1982/ObjectsComparer/blob/master/ObjectsDataComparer.png)\n\n# Objects Comparer\n## Introduction\nIt is quite common situation when complex objects should be compared. Sometimes objects can contain nested elements, or some members should be excluded from comparison (auto generated identifiers, create/update date etc.), or some members can have custom comparison rules (same data in different formats, like phone numbers). This small framework was developed to solve such kind of problems.\n\nBriefly, Objects Comparer is an object-to-object comparer, which allows to compare objects recursively member by member and define custom comparison rules for certain properties, fields or types.\n\nObjects comparer can be considered as ready to use framework or as a starting point for similar solutions.\n## Installation\n>Install-Package ObjectsComparer\n## Basic Examples\nLet's suppose that we have 2 classes\n```csharp\npublic class ClassA\n{\n    public string StringProperty { get; set; }\n\n    public int IntProperty { get; set; }\n\n    public SubClassA SubClass { get; set; }\n}\n\npublic class SubClassA\n{\n    public bool BoolProperty { get; set; }\n}\n```\n\nThere are some examples below how Objects Comparer can be used to compare instances of these classes.\n\n```csharp\n//Initialize objects and comparer\nvar a1 = new ClassA { StringProperty = \"String\", IntProperty = 1 };\nvar a2 = new ClassA { StringProperty = \"String\", IntProperty = 1 };\nvar comparer = new Comparer<ClassA>();\n\n//Compare objects\nIEnumerable<Difference> differences;\nvar isEqual = comparer.Compare(a1, a2, out differences);\n\n//Print results\nDebug.WriteLine(isEqual ? \"Objects are equal\" : string.Join(Environment.NewLine, differenses));\n```\n>Objects are equal\n\nIn examples below **Compare objects** and **Print results** blocks will be skipped for brevity except some cases.\n\n```csharp\nvar a1 = new ClassA { StringProperty = \"String\", IntProperty = 1 };\nvar a2 = new ClassA { StringProperty = \"String\", IntProperty = 2 };\nvar comparer = new Comparer<ClassA>();\n```\n>Difference: DifferenceType=ValueMismatch, MemberPath='IntProperty', Value1='1', Value2='2'.\n\n```csharp\nvar a1 = new ClassA { SubClass = new SubClassA { BoolProperty = true } };\nvar a2 = new ClassA { SubClass = new SubClassA { BoolProperty = false } };\nvar comparer = new Comparer<ClassA>();\n```\n>Difference: DifferenceType=ValueMismatch, MemberPath='SubClass.BoolProperty', Value1='True', Value2='False'.\n\n```csharp\nvar a1 = new StringBuilder(\"abc\");\nvar a2 = new StringBuilder(\"abd\");\nvar comparer = new Comparer<StringBuilder>();\n```\n>Difference: DifferenceType=ValueMismatch, MemberPath='', Value1='abc', Value2='abd'.\n\n## Enumerables (arrays)\n```csharp\nvar a1 = new[] { 1, 2, 3 };\nvar a2 = new[] { 1, 2, 3 };\nvar comparer = new Comparer<int[]>();\n```\n>Objects are equal\n\n```csharp\nvar a1 = new[] { 1, 2 };\nvar a2 = new[] { 1, 2, 3 };\nvar comparer = new Comparer<int[]>();\n```\n>Difference: DifferenceType=ValueMismatch, MemberPath='Length', Value1='2', Value2='3'.\n\n```csharp\nvar a1 = new[] { 1, 2, 3 };\nvar a2 = new[] { 1, 4, 3 };\nvar comparer = new Comparer<int[]>();\n```\n>Difference: DifferenceType=ValueMismatch, MemberPath='[1]', Value1='2', Value2='4'.\n\n```csharp\nvar a1 = new ArrayList { \"Str1\", \"Str2\" };\nvar a2 = new ArrayList { \"Str1\", 5 };\nvar comparer = new Comparer<ArrayList>();\n```\n>Difference: DifferenceType=TypeMismatch, MemberPath='[1]', Value1='Str2', Value2='5'.\n\n## Sets\n\n```csharp\nvar a1 = new[] { 1, 2, 3 };\nvar a2 = new[] { 1, 2, 3 };\nvar comparer = new Comparer<int[]>();\n```\n>Objects are equal\n\n```csharp\nvar a1 = new HashSet<int> { 1, 2, 3 };\nvar a2 = new HashSet<int> { 2, 1, 4 };\nvar comparer = new Comparer<HashSet<int>>();\n```\n>Difference: DifferenceType=MissedElementInSecondObject, MemberPath='', Value1='3', Value2=''.\n>Difference: DifferenceType=MissedElementInFirstObject, MemberPath='', Value1='', Value2='4'.\n\n## Multidimensional arrays\n\n```csharp\nvar a1 = new[] { new[] { 1, 2 } };\nvar a2 = new[] { new[] { 1, 3 } };\nvar comparer = new Comparer<int[][]>();\n```\n\n>Difference: DifferenceType=ValueMismatch, MemberPath='[0][1]', Value1='2', Value2='3'.\n\n```csharp\nvar a1 = new[] { new[] { 1, 2 } };\nvar a2 = new[] { new[] { 2, 2 }, new[] { 3, 5 } };\nvar comparer = new Comparer<int[][]>();\n```\n>Difference: DifferenceType=ValueMismatch, MemberPath='Length', Value1='1', Value2='2'.\n\n```csharp\nvar a1 = new[] { new[] { 1, 2 }, new[] { 3, 5 } };\nvar a2 = new[] { new[] { 1, 2 }, new[] { 3, 5, 6 } };\nvar comparer = new Comparer<int[][]>();\n```\n\n>Difference: DifferenceType=ValueMismatch, MemberPath='[1].Length', Value1='2', Value2='3'.\n\n```csharp\nvar a1 = new[,] { { 1, 2 }, { 1, 3 } };\nvar a2 = new[,] { { 1, 3, 4 }, { 1, 3, 8 } };\nvar comparer = new Comparer<int[,]>();\n```\n\n>Difference: DifferenceType=ValueMismatch, MemberPath='Dimension1', Value1='2', Value2='3'.\n\n```csharp\nvar a1 = new[,] { { 1, 2 } };\nvar a2 = new[,] { { 1, 3 } };\nvar comparer = new Comparer<int[,]>();\n```\n\n>Difference: DifferenceType=ValueMismatch, MemberPath='[0,1]', Value1='2', Value2='3'.\n\n## Dynamic objects\n\nC# supports several types of dynamic objects. \n\n### ExpandoObject\n\n```csharp\ndynamic a1 = new ExpandoObject();\na1.Field1 = \"A\";\na1.Field2 = 5;\na1.Field4 = 4;\ndynamic a2 = new ExpandoObject();\na2.Field1 = \"B\";\na2.Field3 = false;\na2.Field4 = \"C\";\nvar comparer = new Comparer();\n```\n\n>Difference: DifferenceType=ValueMismatch, MemberPath='Field1', Value1='A', Value2='B'.\n\n>Difference: DifferenceType=MissedMemberInSecondObject, MemberPath='Field2', Value1='5', Value2=''.\n\n>Difference: DifferenceType=TypeMismatch, MemberPath='Field4', Value1='4', Value2='C'.\n\n>Difference: DifferenceType=MissedMemberInFirstObject, MemberPath='Field3', Value1='', Value2='False'.\n\n```csharp\ndynamic a1 = new ExpandoObject();\na1.Field1 = \"A\";\na1.Field2 = 5;\ndynamic a2 = new ExpandoObject();\na2.Field1 = \"B\";\na2.Field3 = false;\nvar comparer = new Comparer();\n```\n\n>Difference: DifferenceType=ValueMismatch, MemberPath='Field1', Value1='A', Value2='B'.\n\n>Difference: DifferenceType=MissedMemberInSecondObject, MemberPath='Field2', Value1='5', Value2=''.\n\n>Difference: DifferenceType=MissedMemberInFirstObject, MemberPath='Field3', Value1='', Value2='False'.\n\nBehavior if member not exists could be changed by providing custom ComparisonSettings (see Comparison Settings below).\n\n```csharp\ndynamic a1 = new ExpandoObject();\na1.Field1 = \"A\";\na1.Field2 = 0;\ndynamic a2 = new ExpandoObject();\na2.Field1 = \"B\";\na2.Field3 = false;\na2.Field4 = \"S\";\nvar comparer = new Comparer(new ComparisonSettings { UseDefaultIfMemberNotExist = true });\n```\n\n>Difference: DifferenceType=ValueMismatch, MemberPath='Field1', Value1='A', Value2='B'.\n\n>Difference: DifferenceType=ValueMismatch, MemberPath='Field4', Value1='', Value2='S'.\n\n### DynamicObject\nLet’s assume that we have such implementation of the **DynamicObject** class. It is necessary to have a correct implementation of the method **GetDynamicMemberNames**, otherwise Objects Comparer wouldn't work in a right way.\n\n```csharp\nprivate class DynamicDictionary : DynamicObject\n{\n    public int IntProperty { get; set; }\n\n    private readonly Dictionary<string, object> _dictionary = new Dictionary<string, object>();\n\n    public override bool TryGetMember(GetMemberBinder binder, out object result)\n    {\n        var name = binder.Name;\n\n        return _dictionary.TryGetValue(name, out result);\n    }\n\n    public override bool TrySetMember(SetMemberBinder binder, object value)\n    {\n        _dictionary[binder.Name] = value;\n\n        return true;\n    }\n\n    public override IEnumerable<string> GetDynamicMemberNames()\n    {\n        return _dictionary.Keys;\n    }\n}\n```\n\n```csharp\ndynamic a1 = new DynamicDictionary();\na1.Field1 = \"A\";\na1.Field3 = true;\ndynamic a2 = new DynamicDictionary();\na2.Field1 = \"B\";\na2.Field2 = 8;\na2.Field3 = 1;\nvar comparer = new Comparer();\n```\n\n>Difference: DifferenceType=ValueMismatch, MemberPath='Field1', Value1='A', Value2='B'.\n\n>Difference: DifferenceType=TypeMismatch, MemberPath='Field3', Value1='True', Value2='1'.\n\n>Difference: DifferenceType=MissedMemberInFirstObject, MemberPath='Field2', Value1='', Value2='8'.\n\n### Compiler generated objects\n\n```csharp\ndynamic a1 = new\n{\n    Field1 = \"A\",\n    Field2 = 5,\n    Field3 = true\n};\ndynamic a2 = new\n{\n    Field1 = \"B\",\n    Field2 = 8\n};\nvar comparer = new Comparer();\n\nIEnumerable<Difference> differences;\nvar isEqual = comparer.Compare((object)a1, (object)a2, out differences);\n```\n\n>Difference: DifferenceType=ValueMismatch, MemberPath='Field1', Value1='A', Value2='B'.\n\n>Difference: DifferenceType=TypeMismatch, MemberPath='Field2', Value1='5', Value2='8'.\n\n>Difference: DifferenceType=MissedMemberInSecondObject, MemberPath='Field3', Value1='True', Value2=''.\n\nThis example requires some additional explanations. Types of the objects a1 and a2 were generated by compiler and are considered as the same type if and only if objects a1 and a2 have same set of members (same name and same type). If casting to **(object)** is skipped in case of different set of members **RuntimeBinderException** will be thrown.\n\n## Overriding Comparison Rules\nTo override comparison rule we need to create custom value comparer or provide function how to compare objects and how to convert these objects to string(optional) and filter function(optional).\nValue Comparer  should be inherited from **AbstractValueComparer<T>** or should implement **IValueComparer<T>**.\n```csharp\npublic class MyValueComparer: AbstractValueComparer<string>\n{\n    public override bool Compare(string obj1, string obj2, ComparisonSettings settings)\n    {\n        return obj1 == obj2; //Implement comparison logic here\n    }\n}\n```\nOverride comparison rule for objects of particular type.\n```csharp\n//Use MyComparer to compare all members of type string \ncomparer.AddComparerOverride<string>(new MyValueComparer());\ncomparer.AddComparerOverride(typeof(string), new MyValueComparer());\n//Use MyComparer to compare all members of type string except members which name starts with \"Xyz\"\ncomparer.AddComparerOverride(typeof(string), new MyValueComparer(), member => !member.Name.StartsWith(\"Xyz\"));\ncomparer.AddComparerOverride<string>(new MyValueComparer(), member => !member.Name.StartsWith(\"Xyz\"));\n```\nOverride comparison rule for particular member (Field or Property).\n```csharp\n//Use MyValueComparer to compare StringProperty of ClassA\ncomparer.AddComparerOverride(() => new ClassA().StringProperty, new MyValueComparer());\ncomparer.AddComparerOverride(\n    typeof(ClassA).GetTypeInfo().GetMember(\"StringProperty\").First(),\n    new MyValueComparer());\n//Compare StringProperty of ClassA by length. If length equal consider that values are equal\ncomparer.AddComparerOverride(\n    () => new ClassA().StringProperty,\n    (s1, s2, parentSettings) => s1?.Length == s2?.Length,\n    s => s.ToString());\ncomparer.AddComparerOverride(\n    () => new ClassA().StringProperty,\n    (s1, s2, parentSettings) => s1?.Length == s2?.Length);\n```\nOverride comparison rule for particular member(s) (Field or Property) by name.\n```csharp\n//Use MyValueComparer to compare all members with name equal to \"StringProperty\"\ncomparer.AddComparerOverride(\"StringProperty\", new MyValueComparer());\n```\nOverride comparison rule by member filter.\n```csharp\ncomparer.AddComparerOverride(new MyValueComparer(), m => m.Name == \"StringProperty\");\n``` \nOverrides by type have highest priority, then overrides by member/member filter and overrides by member name have lowest priority.\nIf more than one value comparers of the same type (by type/by name/by member name) could be applied to the same member, exception **AmbiguousComparerOverrideResolutionException** will be thrown during comparison.\n\nExample:\n```csharp\nvar a1 = new ClassA();\nvar a2 = new ClassA();\ncomparer.AddComparerOverride<string>(valueComparer1, member => member.Name.StartsWith(\"String\"));\ncomparer.AddComparerOverride<string>(valueComparer2, member => member.Name.EndsWith(\"Property\"));\n\nvar result = comparer.Compare(a1, a2);//Exception here\n```\n\n## Ignoring members\nIgnores members of particular type.\n```csharp\ncomparer.IgnoreMember<string>();\n```\n\nIgnores particular member.\n```csharp\ncomparer.IgnoreMember(() => new A().TestProperty1);\n```\n\nIgnores by member filter.\n```csharp\ncomparer.IgnoreMember(member => member.Name == \"TestProperty1\");\n```\n\nIgnores particular member by name.\n```csharp\ncomparer.IgnoreMember(\"TestProperty1\");\n```\n\n## Comparison Settings\nComparer constructor has an optional **settings** parameter to configure some aspects of comparison.\n\n**RecursiveComparison**\n\nTrue by default. If true, all members which are not primitive types, do not have custom comparison rule and do not implement **ICompareble** will be compared using the same rules as root objects.\n\n**EmptyAndNullEnumerablesEqual**\n\nFalse by default. If true, empty enumerables (arrays, collections, lists etc.) and null values will be considered as equal values.\n\n**UseDefaultIfMemberNotExist**\n\nIf true and member does not exist, objects comparer will consider that this member is equal to default value of opposite member type. Applicable for dynamic types comparison only. False by default.\n\nComparison Settings class allows to store custom values that can be used in custom comparers.\n```csharp\nSetCustomSetting<T>(T value, string key = null)\nGetCustomSetting<T>(string key = null)\n```\n\n## Factory\nFactory provides a way to encapsulate comparers creation and configuration. Factory should implement **IComparersFactory** or should be inherited from **ComparersFactory**.\n```csharp\npublic class MyComparersFactory: ComparersFactory\n{\n    public override IComparer<T> GetObjectsComparer<T>(ComparisonSettings settings = null, IBaseComparer parentComparer = null)\n    {\n        if (typeof(T) == typeof(ClassA))\n        {\n            var comparer = new Comparer<ClassA>(settings, parentComparer, this);\n            comparer.AddComparerOverride<Guid>(new MyCustomGuidComparer());\n\n            return (IComparer<T>)comparer;\n        }\n\n        return base.GetObjectsComparer<T>(settings, parentComparer);\n    }\n}\n```\n\n## Non-generic comparer\n```csharp\nvar comparer = new Comparer();\nvar isEqual = comparer.Compare(a1, a2);\n```\nThis comparer creates generic implementation of comparer for each comparison.\n\n## Useful Value Comparers\nFramework contains several custom comparers that can be useful in many cases.\n\n**DoNotCompareValueComparer**\n\nAllows to skip some fields/types. Has singleton implementation (**DoNotCompareValueComparer.Instance**).\n\nP.S. **IgnoreMember** methods is more convenient way to skip fields/types. This comparer is needed if override require custom filter and for backward compatibility.\n\n**DynamicValueComparer<T>**\n\nReceives comparison rule as a function.\n\n**NulableStringsValueComparer**\n\nNull and empty strings are considered as equal values. Has singleton implementation (**NulableStringsValueComparer.Instance**).\n\n**DefaultValueValueComparer**\n\nAllows to consider provided value and default value of specified type as equal values (see Example 3 below).\n\n**IgnoreCaseStringsValueComparer**\n\nAllows to compare string ignoring case. Has singleton implementation (**IgnoreCaseStringsValueComparer.Instance**).\n\n**UriComparer**\n\nAllows to compare Uri objects.\n\n## Examples\nThere are some more complex examples how Objects Comparer can be used.\n\n### Example 1: Expected Message\n#### Challenge\n\nCheck if received message equal to the expected message.\n\n#### Problems\n\n* **DateCreated**, **DateSent** and **DateReceived** properties need to be skipped\n* Auto generated **Id** property need to be skipped\n* **Message** property of **Error** class need to be skipped\n\n#### Solution\n```csharp\npublic class Error\n{\n    public int Id { get; set; }\n\n    public string Messgae { get; set; }\n}\n```\n```csharp\npublic class Message\n{\n    public string Id { get; set; }\n\n    public DateTime DateCreated { get; set; }\n    \n    public DateTime DateSent { get; set; }\n\n    public DateTime DateReceived { get; set; }\n\n    public int MessageType { get; set; }\n\n    public int Status { get; set; }\n\n    public List<Error> Errors { get; set; }\n\n    public override string ToString()\n    {\n        return $\"Id:{Id}, Date:{DateCreated}, Type:{MessageType}, Status:{Status}\";\n    }\n}\n```\nConfiguring comparer.\n```csharp\n_comparer = new Comparer<Message>(\n    new ComparisonSettings\n    {\n        //Null and empty error lists are equal\n        EmptyAndNullEnumerablesEqual = true\n    });\n//Do not compare DateCreated \n_comparer.AddComparerOverride<DateTime>(DoNotCompareValueComparer.Instance);\n//Do not compare Id\n_comparer.AddComparerOverride(() => new Message().Id, DoNotCompareValueComparer.Instance);\n//Do not compare Message Text\n_comparer.AddComparerOverride(() => new Error().Messgae, DoNotCompareValueComparer.Instance);\n```\n\n```csharp\nvar expectedMessage = new Message\n{\n    MessageType = 1,\n    Status = 0\n};\n\nvar actualMessage = new Message\n{\n    Id = \"M12345\",\n    DateCreated = DateTime.Now,\n    DateSent = DateTime.Now,\n    DateReceived = DateTime.Now,\n    MessageType = 1,\n    Status = 0\n};\n\nIEnumerable<Difference> differences;\nvar isEqual = _comparer.Compare(expectedMessage, actualMessage, out differences);\n```\n> Objects are equal\n\n```csharp\nvar expectedMessage = new Message\n{\n    MessageType = 1,\n    Status = 1,\n    Errors = new List<Error>\n    {\n        new Error { Id = 2 },\n        new Error { Id = 7 }\n    }\n};\n\nvar actualMessage = new Message\n{\n    Id = \"M12345\",\n    DateCreated = DateTime.Now,\n    DateSent = DateTime.Now,\n    DateReceived = DateTime.Now,\n    MessageType = 1,\n    Status = 1,\n    Errors = new List<Error>\n    {\n        new Error { Id = 2, Messgae = \"Some error #2\" },\n        new Error { Id = 7, Messgae = \"Some error #7\" },\n    }\n};\n\nIEnumerable<Difference> differences;\nvar isEqual = _comparer.Compare(expectedMessage, actualMessage, out differences);\n```\n> Objects are equal\n\n```csharp\nvar expectedMessage = new Message\n{\n    MessageType = 1,\n    Status = 1,\n    Errors = new List<Error>\n    {\n        new Error { Id = 2, Messgae = \"Some error #2\" },\n        new Error { Id = 8, Messgae = \"Some error #8\" }\n    }\n};\n\nvar actualMessage = new Message\n{\n    Id = \"M12345\",\n    DateCreated = DateTime.Now,\n    DateSent = DateTime.Now,\n    DateReceived = DateTime.Now,\n    MessageType = 1,\n    Status = 2,\n    Errors = new List<Error>\n    {\n        new Error { Id = 2, Messgae = \"Some error #2\" },\n        new Error { Id = 7, Messgae = \"Some error #7\" }\n    }\n};\n\nIEnumerable<Difference> differences;\nvar isEqual = _comparer.Compare(expectedMessage, actualMessage, out differences);\n```\n> Difference: DifferenceType=ValueMismatch, MemberPath='Status', Value1='1', Value2='2'.\n\n> Difference: DifferenceType=ValueMismatch, MemberPath='Errors[1].Id', Value1='8', Value2='7'.\n\n### Example 2: Persons comparison\n#### Challenge\n\nCompare persons from different sources.\n\n#### Problems\n\n* **PhoneNumber** format can be in different. Example: \"111-555-8888\" and \"(111) 555 8888\"\n* **MiddleName** can exist in one source but does not exist in another source. It makes a sense to compare **MiddleName** only if it has value in both sources.\n* **PersonId** property need to be skipped\n#### Solution\n```csharp\npublic class Person\n{\n    public Guid PersonId { get; set; }\n\n    public string FirstName { get; set; }\n\n    public string LastName { get; set; }\n\n    public string MiddleName { get; set; }\n\n    public string PhoneNumber { get; set; }\n\n    public override string ToString()\n    {\n        return $\"{FirstName} {MiddleName} {LastName} ({PhoneNumber})\";\n    }\n}\n```\nPhone number can have different formats. Let’s compare only digits.\n```csharp\npublic class PhoneNumberComparer: AbstractValueComparer<string>\n{\n    public override bool Compare(string obj1, string obj2, ComparisonSettings settings)\n    {\n        return ExtractDigits(obj1) == ExtractDigits(obj2);\n    }\n\n    private string ExtractDigits(string str)\n    {\n        return string.Join(\n            string.Empty, \n            (str ?? string.Empty)\n                .ToCharArray()\n                .Where(char.IsDigit));\n    }\n}\n```\nFactory allows not to configure comparer every time we need to create it.\n```csharp\npublic class MyComparersFactory: ComparersFactory\n{\n    public override IComparer<T> GetObjectsComparer<T>(ComparisonSettings settings = null, IBaseComparer parentComparer = null)\n    {\n        if (typeof(T) == typeof(Person))\n        {\n            var comparer = new Comparer<Person>(settings, parentComparer, this);\n            //Do not compare PersonId\n            comparer.AddComparerOverride<Guid>(DoNotCompareValueComparer.Instance);\n            //Sometimes MiddleName can be skipped. Compare only if property has value.\n            comparer.AddComparerOverride(\n                () => new Person().MiddleName,\n                (s1, s2, parentSettings) => string.IsNullOrWhiteSpace(s1) || string.IsNullOrWhiteSpace(s2) || s1 == s2);\n            comparer.AddComparerOverride(\n                () => new Person().PhoneNumber,\n                new PhoneNumberComparer());\n\n            return (IComparer<T>)comparer;\n        }\n\n        return base.GetObjectsComparer<T>(settings, parentComparer);\n    }\n}\n```\nConfiguring comparer.\n```csharp\n_factory = new MyComparersFactory();\n_comparer = _factory.GetObjectsComparer<Person>();\n```\n\n```csharp\nvar person1 = new Person\n{\n    PersonId = Guid.NewGuid(),\n    FirstName = \"John\",\n    LastName = \"Doe\",\n    MiddleName = \"F\",\n    PhoneNumber = \"111-555-8888\"\n};\nvar person2 = new Person\n{\n    PersonId = Guid.NewGuid(),\n    FirstName = \"John\",\n    LastName = \"Doe\",\n    PhoneNumber = \"(111) 555 8888\"\n};\n\nIEnumerable<Difference> differences;\nvar isEqual = _comparer.Compare(person1, person2, out differences);\n```\n> Objects are equal\n\n```csharp\nvar person1 = new Person\n{\n    PersonId = Guid.NewGuid(),\n    FirstName = \"Jack\",\n    LastName = \"Doe\",\n    MiddleName = \"F\",\n    PhoneNumber = \"111-555-8888\"\n};\nvar person2 = new Person\n{\n    PersonId = Guid.NewGuid(),\n    FirstName = \"John\",\n    LastName = \"Doe\",\n    MiddleName = \"L\",\n    PhoneNumber = \"222-555-9999\"\n};\n\nIEnumerable<Difference> differences;\nvar isEqual = _comparer.Compare(person1, person2, out differences);\n```\n> Difference: DifferenceType=ValueMismatch, MemberPath='FirstName', Value1='Jack', Value2='John'.\n\n> Difference: DifferenceType=ValueMismatch, MemberPath='MiddleName', Value1='F', Value2='L'.\n\n> Difference: DifferenceType=ValueMismatch, MemberPath='PhoneNumber', Value1='111-555-8888', Value2='222-555-9999'.\n\n### Example 3: Comparing JSON configuration files\n#### Challenge\n\nThere are files with settings with some differences that need to be found. Json.NET is used to deserialize JSON data.\n\n#### Problems\n\n* URLs can be with or without http prefix. \n* **DataCompression** is Off by default\n* **SmartMode1...3** disabled by default \n* **ConnectionString**,  **Email** and **Notifications** need to be skipped\n* If **ProcessTaskTimeout** or **TotalProcessTimeout** settings skipped default values will be used, so if in one file setting does not exists and in another file this setting has default value it is actually the same.\n#### Files\n##### Settings0\n```json\n{\n  \"ConnectionString\": \"USER ID=superuser;PASSWORD=superpassword;DATA SOURCE=localhost:1111\",\n  \"Email\": {\n    \"Port\": 25,\n    \"Host\": \"MyHost.com\",\n    \"EmailAddress\": \"test@MyHost.com\"\n  },\n  \"Settings\": {\n    \"DataCompression\": \"On\",\n    \"DataSourceType\": \"MultiDataSource\",\n    \"SomeUrl\": \"http://MyHost.com/VeryImportantData\",\n    \"SomeOtherUrl\": \"http://MyHost.com/NotSoImportantData/\",\n    \"CacheMode\": \"Memory\",\n    \"MaxCacheSize\": \"1GB\",\n    \"SuperModes\": {\n      \"SmartMode1\": \"Enabled\",\n      \"SmartMode2\": \"Disabled\",\n      \"SmartMode3\": \"Enabled\"\n    }\n  },\n  \"Timeouts\": {\n    \"TotalProcessTimeout\": 500,\n    \"ProcessTaskTimeout\": 100\n  },\n  \"BackupSettings\": {\n    \"BackupIntervalUnit\": \"Day\",\n    \"BackupInterval\": 100\n  },\n  \"Notifications\": [\n    {\n      \"Phone\": \"111-222-3333\"\n    },\n    {\n      \"Phone\": \"111-222-4444\"\n    },\n    {\n      \"EMail\": \"support@MyHost.com\"\n    }\n  ],\n  \"Logging\": {\n    \"Enabled\": true,\n    \"Pattern\": \"Logs\\\\MyApplication.%data{yyyyMMdd}.log\",\n    \"MaximumFileSize\": \"20MB\",\n    \"Level\": \"ALL\"\n  }\n}\n```\n##### Settings1\n```json\n{\n  \"ConnectionString\": \"USER ID=admin;PASSWORD=*****;DATA SOURCE=localhost:22222\",\n  \"Email\": {\n    \"Port\": 25,\n    \"Host\": \"MyHost.com\",\n    \"EmailAddress\": \"test@MyHost.com\"\n  },\n  \"Settings\": {\n    \"DataCompression\": \"On\",\n    \"DataSourceType\": \"MultiDataSource\",\n    \"SomeUrl\": \"MyHost.com/VeryImportantData\",\n    \"SomeOtherUrl\": \"MyHost.com/NotSoImportantData/\",\n    \"CacheMode\": \"Memory\",\n    \"MaxCacheSize\": \"1GB\",\n    \"SuperModes\": {\n      \"SmartMode1\": \"enabled\",\n      \"SmartMode3\": \"enabled\"\n    }\n  },\n  \"BackupSettings\": {\n    \"BackupIntervalUnit\": \"Day\",\n    \"BackupInterval\": 100\n  },\n  \"Notifications\": [\n    {\n      \"Phone\": \"111-222-3333\"\n    },\n    {\n      \"EMail\": \"support@MyHost.com\"\n    }\n  ],\n  \"Logging\": {\n    \"Enabled\": true,\n    \"Pattern\": \"Logs\\\\MyApplication.%data{yyyyMMdd}.log\",\n    \"MaximumFileSize\": \"20MB\",\n    \"Level\": \"ALL\"\n  }\n}\n```\n##### Settings2\n```json\n{\n  \"ConnectionString\": \"USER ID=superuser;PASSWORD=superpassword;DATA SOURCE=localhost:1111\",\n  \"Email\": {\n    \"Port\": 25,\n    \"Host\": \"MyHost.com\",\n    \"EmailAddress\": \"test@MyHost.com\"\n  },\n  \"Settings\": {\n    \"DataSourceType\": \"MultiDataSource\",\n    \"SomeUrl\": \"http://MyHost.com/VeryImportantData\",\n    \"SomeOtherUrl\": \"http://MyHost.com/NotSoImportantData/\",\n    \"CacheMode\": \"Memory\",\n    \"MaxCacheSize\": \"1GB\",\n    \"SuperModes\": {\n      \"SmartMode3\": \"Enabled\"\n    }\n  },\n  \"Timeouts\": {\n    \"TotalProcessTimeout\": 500,\n    \"ProcessTaskTimeout\": 200\n  },\n  \"BackupSettings\": {\n    \"BackupIntervalUnit\": \"Week\",\n    \"BackupInterval\": 2\n  },\n  \"Notifications\": [\n    {\n      \"EMail\": \"support@MyHost.com\"\n    }\n  ],\n  \"Logging\": {\n    \"Enabled\": false,\n    \"Pattern\": \"Logs\\\\MyApplication.%data{yyyyMMdd}.log\",\n    \"MaximumFileSize\": \"40MB\",\n    \"Level\": \"ERROR\"\n  }\n}\n```\n#### Solution\nConfiguring comparer.\n```csharp\n_comparer = new Comparer(new ComparisonSettings { UseDefaultIfMemberNotExist = true });\n//Some fields should be ignored\n_comparer.AddComparerOverride(\"ConnectionString\", DoNotCompareValueComparer.Instance);\n_comparer.AddComparerOverride(\"Email\", DoNotCompareValueComparer.Instance);\n_comparer.AddComparerOverride(\"Notifications\", DoNotCompareValueComparer.Instance);\n//Smart Modes are disabled by default. These fields are not case sensitive\nvar disabledByDefaultComparer = new DefaultValueValueComparer<string>(\"Disabled\", IgnoreCaseStringsValueComparer.Instance);\n_comparer.AddComparerOverride(\"SmartMode1\", disabledByDefaultComparer);\n_comparer.AddComparerOverride(\"SmartMode2\", disabledByDefaultComparer);\n_comparer.AddComparerOverride(\"SmartMode3\", disabledByDefaultComparer);\n//http prefix in URLs should be ignored\nvar urlComparer = new DynamicValueComparer<string>(\n    (url1, url2, settings) => url1.Trim('/').Replace(@\"http://\", string.Empty) == url2.Trim('/').Replace(@\"http://\", string.Empty));\n_comparer.AddComparerOverride(\"SomeUrl\", urlComparer);\n_comparer.AddComparerOverride(\"SomeOtherUrl\", urlComparer);\n//DataCompression is Off by default.\n_comparer.AddComparerOverride(\"DataCompression\", new DefaultValueValueComparer<string>(\"Off\", NulableStringsValueComparer.Instance));\n//ProcessTaskTimeout and TotalProcessTimeout fields have default values.\n_comparer.AddComparerOverride(\"ProcessTaskTimeout\", new DefaultValueValueComparer<long>(100, DefaultValueComparer.Instance));\n_comparer.AddComparerOverride(\"TotalProcessTimeout\", new DefaultValueValueComparer<long>(500, DefaultValueComparer.Instance));\n```\n\n```csharp\nvar settings0Json = LoadJson(\"Settings0.json\");\nvar settings0 = JsonConvert.DeserializeObject<ExpandoObject>(settings0Json);\nvar settings1Json = LoadJson(\"Settings1.json\");\nvar settings1 = JsonConvert.DeserializeObject<ExpandoObject>(settings1Json);\n\nIEnumerable<Difference> differences;\nvar isEqual = _comparer.Compare(settings0, settings1, out differences);\n```\n> Objects are equal\n\n```csharp\nvar settings0Json = LoadJson(\"Settings0.json\");\nvar settings0 = JsonConvert.DeserializeObject<ExpandoObject>(settings0Json);\nvar settings2Json = LoadJson(\"Settings2.json\");\nvar settings2 = JsonConvert.DeserializeObject<ExpandoObject>(settings2Json);\n\nIEnumerable<Difference> differences;\nvar isEqual = _comparer.Compare(settings0, settings2, out differences);\n```\n> Difference: DifferenceType=ValueMismatch, MemberPath='Settings.DataCompression', Value1='On', Value2='Off'.\n\n> Difference: DifferenceType=ValueMismatch, MemberPath='Settings.SuperModes.SmartMode1', Value1='Enabled', Value2='Disabled'.\n\n> Difference: DifferenceType=ValueMismatch, MemberPath='Timeouts.ProcessTaskTimeout', Value1='100', Value2='200'.\n\n> Difference: DifferenceType=ValueMismatch, MemberPath='BackupSettings.BackupIntervalUnit', Value1='Day', Value2='Week'.\n\n> Difference: DifferenceType=ValueMismatch, MemberPath='BackupSettings.BackupInterval', Value1='100', Value2='2'.\n\n> Difference: DifferenceType=ValueMismatch, MemberPath='Logging.Enabled', Value1='True', Value2='False'.\n\n> Difference: DifferenceType=ValueMismatch, MemberPath='Logging.MaximumFileSize', Value1='20MB', Value2='40MB'.\n\n> Difference: DifferenceType=ValueMismatch, MemberPath='Logging.Level', Value1='ALL', Value2='ERROR'.\n\n### Example 4: Custom comparer for list\n#### Challenge\n\nThis example came from one of the framework users, so it's 100% real life scenario.\nCompare list of items by content even if counts of items in the lists are different. Use Id property as an identifier.\n\n#### Problems\n\nBy default if counts of items in the lists are different Comparer consider these lists as different and doesn't compere items.\n\n#### Solution\nSolution for this problem is to implement custom Comparer (it should be inherited from AbstractComparer&lt;TypeOfTheList&gt;) and implement comparison logic of comparing lists inside this Comparer. Then implement ComparersFactory (like in Example 2) to return custom Comparer if type equal to IList and use this factory.\n\nClasses to compare.\n```csharp\npublic class FormulaItem\n{\n    public long Id { get; set; }\n    public int Delay { get; set; }\n    public string Name { get; set; }\n    public string Instruction { get; set; }\n}\n```\n```csharp\npublic class Formula\n{\n    public long Id { get; set; }\n    public string Name { get; set; }\n    public IList<FormulaItem> Items { get; set; }\n}\n```\n\nCustom Comparer implementation\n```csharp\npublic class CustomFormulaItemsComparer: AbstractComparer<IList<FormulaItem>>\n{\n    public CustomFormulaItemsComparer(ComparisonSettings settings, BaseComparer parentComparer, IComparersFactory factory) : base(settings, parentComparer, factory)\n    {\n    }\n\n    public override IEnumerable<Difference> CalculateDifferences(IList<FormulaItem> obj1, IList<FormulaItem> obj2)\n    {\n        if (obj1 == null && obj2 == null)\n        {\n            yield break;\n        }\n\n        if (obj1 == null || obj2 == null)\n        {\n            yield return new Difference(\"\", DefaultValueComparer.ToString(obj1) , DefaultValueComparer.ToString(obj2));\n            yield break;\n        }\n\n        if (obj1.Count != obj2.Count)\n        {\n            yield return new Difference(\"Count\", obj1.Count.ToString(), obj2.Count.ToString(),\n                    DifferenceTypes.NumberOfElementsMismatch);\n        }\n\n        foreach (var formulaItem in obj1)\n        {\n            var formulaItem2 = obj2.FirstOrDefault(fi => fi.Id == formulaItem.Id);\n\n            if (formulaItem2 != null)\n            {\n                var comparer = Factory.GetObjectsComparer<FormulaItem>();\n\n                foreach (var difference in comparer.CalculateDifferences(formulaItem, formulaItem2))\n                {\n                    yield return difference.InsertPath($\"[Id={formulaItem.Id}]\");\n                }\n            }\n        }\n    }\n}\n```\nFactory\n```csharp\npublic class MyComparersFactory : ComparersFactory\n{\n    public override IComparer<T> GetObjectsComparer<T>(ComparisonSettings settings = null,\n        BaseComparer parentComparer = null)\n    {\n        if (typeof(T) != typeof(IList<FormulaItem>))\n        {\n            return base.GetObjectsComparer<T>(settings, parentComparer);\n        }\n\n        var comparer = new CustomFormulaItemsComparer(settings, parentComparer, this);\n\n        return (IComparer<T>) comparer;\n\n    }\n}\n```\n\nConfiguring Comparer.\n```csharp\n_factory = new MyComparersFactory();\n_comparer = _factory.GetObjectsComparer<Formula>();\n```\n\n```\nvar formula1 = new Formula\n{\n    Id = 1,\n    Name = \"Formula 1\",\n    Items = new List<FormulaItem>\n    {\n        new FormulaItem\n        {\n            Id = 1,\n            Delay = 60,\n            Name = \"Item 1\",\n            Instruction = \"Instruction 1\"\n        }\n    }\n};\n\nvar formula2 = new Formula\n{\n    Id = 1,\n    Name = \"Formula 1\",\n    Items = new List<FormulaItem>\n    {\n        new FormulaItem\n        {\n            Id = 1,\n            Delay = 80,\n            Name = \"Item One\",\n            Instruction = \"Instruction One\"\n        }\n    }\n};\n\nvar isEqual = _comparer.Compare(formula1, formula2, out var differences);\n```\n\n> Difference: DifferenceType=ValueMismatch, MemberPath='Items[Id=1].Delay', Value1='60', Value2='80'.\n> Difference: DifferenceType=ValueMismatch, MemberPath='Items[Id=1].Name', Value1='Item 1', Value2='Item One'.\n> Difference: DifferenceType=ValueMismatch, MemberPath='Items[Id=1].Instruction', Value1='Instruction 1', Value2='Instruction One'.\n\n```csharp\nvar formula1 = new Formula\n{\n    Id = 1,\n    Name = \"Formula 1\",\n    Items = new List<FormulaItem>\n    {\n        new FormulaItem\n        {\n            Id = 1,\n            Delay = 60,\n            Name = \"Item 1\",\n            Instruction = \"Instruction 1\"\n        }\n    }\n};\n\nvar formula2 = new Formula\n{\n    Id = 1,\n    Name = \"Formula 1\",\n    Items = new List<FormulaItem>\n    {\n        new FormulaItem\n        {\n            Id = 1,\n            Delay = 80,\n            Name = \"Item One\",\n            Instruction = \"Instruction One\"\n        },\n        new FormulaItem\n        {\n            Id = 2,\n            Delay = 30,\n            Name = \"Item Two\",\n            Instruction = \"Instruction Two\"\n        }\n    }\n};\n\nvar isEqual = _comparer.Compare(formula1, formula2, out var differences);\n```\n\n> Difference: DifferenceType=NumberOfElementsMismatch, MemberPath='Items.Count', Value1='1', Value2='2'.\n> Difference: DifferenceType=ValueMismatch, MemberPath='Items[Id=1].Delay', Value1='60', Value2='80'.\n> Difference: DifferenceType=ValueMismatch, MemberPath='Items[Id=1].Name', Value1='Item 1', Value2='Item One'.\n> Difference: DifferenceType=ValueMismatch, MemberPath='Items[Id=1].Instruction', Value1='Instruction 1', Value2='Instruction One'.\n\n## Contributing\nAny useful changes are welcomed.\n\nFeel free to report any defects or ideas how this framework can be improved. \n\nCreate an issue, contact me directly or fork the code and submit a pull request!\n\n\n"
  }
]