Skip to content

Commit

Permalink
Preliminary work on adding a table helpers class that can be resolved…
Browse files Browse the repository at this point in the history
… from the DI. This enables per-scenario differing value retrievers.
  • Loading branch information
ajeckmans committed May 14, 2024
1 parent c9b362f commit 25f2f47
Show file tree
Hide file tree
Showing 52 changed files with 1,273 additions and 236 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public static void CompareToInstance<T>(this Table table, T instance)
/// Indicates whether the table is equivalent to the specified instance by comparing the values of all
/// columns against the properties of the instance. Will return false after finding the first difference.
/// </summary>
[Obsolete("Use TableHelpers instead")]
public static bool IsEquivalentToInstance<T>(this Table table, T instance)
{
return Reqnroll.InstanceComparisonExtensionMethods.IsEquivalentToInstance(table, instance);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using System;
using System.Collections.Generic;
using Reqnroll;

// ReSharper disable once CheckNamespace
namespace TechTalk.SpecFlow.Assist;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,61 +10,61 @@ public static class TableHelperExtensionMethods
{
public static T CreateInstance<T>(this Table table)
{
return Reqnroll.TableHelperExtensionMethods.CreateInstance<T>(table);
return Reqnroll.TableExtensionMethods.CreateInstance<T>(table);
}

public static T CreateInstance<T>(this Table table, InstanceCreationOptions creationOptions)
{
return Reqnroll.TableHelperExtensionMethods.CreateInstance<T>(table, creationOptions);
return Reqnroll.TableExtensionMethods.CreateInstance<T>(table, creationOptions);
}

public static T CreateInstance<T>(this Table table, Func<T> methodToCreateTheInstance)
{
return Reqnroll.TableHelperExtensionMethods.CreateInstance(table, methodToCreateTheInstance);
return Reqnroll.TableExtensionMethods.CreateInstance(table, methodToCreateTheInstance);
}

public static T CreateInstance<T>(this Table table, Func<T> methodToCreateTheInstance, InstanceCreationOptions creationOptions)
{
return Reqnroll.TableHelperExtensionMethods.CreateInstance(table, methodToCreateTheInstance, creationOptions);
return Reqnroll.TableExtensionMethods.CreateInstance(table, methodToCreateTheInstance, creationOptions);
}

public static void FillInstance(this Table table, object instance)
{
Reqnroll.TableHelperExtensionMethods.FillInstance(table, instance);
Reqnroll.TableExtensionMethods.FillInstance(table, instance);
}

public static void FillInstance(this Table table, object instance, InstanceCreationOptions creationOptions)
{
Reqnroll.TableHelperExtensionMethods.FillInstance(table, instance, creationOptions);
Reqnroll.TableExtensionMethods.FillInstance(table, instance, creationOptions);
}

public static IEnumerable<T> CreateSet<T>(this Table table)
{
return Reqnroll.TableHelperExtensionMethods.CreateSet<T>(table);
return Reqnroll.TableExtensionMethods.CreateSet<T>(table);
}

public static IEnumerable<T> CreateSet<T>(this Table table, InstanceCreationOptions creationOptions)
{
return Reqnroll.TableHelperExtensionMethods.CreateSet<T>(table, creationOptions);
return Reqnroll.TableExtensionMethods.CreateSet<T>(table, creationOptions);
}

public static IEnumerable<T> CreateSet<T>(this Table table, Func<T> methodToCreateEachInstance)
{
return Reqnroll.TableHelperExtensionMethods.CreateSet<T>(table, methodToCreateEachInstance);
return Reqnroll.TableExtensionMethods.CreateSet<T>(table, methodToCreateEachInstance);
}

public static IEnumerable<T> CreateSet<T>(this Table table, Func<T> methodToCreateEachInstance, InstanceCreationOptions creationOptions)
{
return Reqnroll.TableHelperExtensionMethods.CreateSet<T>(table, methodToCreateEachInstance, creationOptions);
return Reqnroll.TableExtensionMethods.CreateSet<T>(table, methodToCreateEachInstance, creationOptions);
}

public static IEnumerable<T> CreateSet<T>(this Table table, Func<DataTableRow, T> methodToCreateEachInstance)
{
return Reqnroll.TableHelperExtensionMethods.CreateSet<T>(table, methodToCreateEachInstance);
return Reqnroll.TableExtensionMethods.CreateSet<T>(table, methodToCreateEachInstance);
}

public static IEnumerable<T> CreateSet<T>(this Table table, Func<DataTableRow, T> methodToCreateEachInstance, InstanceCreationOptions creationOptions)
{
return Reqnroll.TableHelperExtensionMethods.CreateSet<T>(table, methodToCreateEachInstance, creationOptions);
return Reqnroll.TableExtensionMethods.CreateSet<T>(table, methodToCreateEachInstance, creationOptions);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ public void CreateEmptyCSharpCore3_1ProjectInNewFormat()
string projectFileContent = GetProjectFileContent(solutionFolder, project);

projectFileContent.Should()
.Contain("<Project Sdk=\"Microsoft.NET.Sdk\">\r\n <PropertyGroup>\r\n <TargetFramework>netcoreapp3.1</TargetFramework>\r\n </PropertyGroup>\r\n</Project>");
.Contain("<Project Sdk=\"Microsoft.NET.Sdk\">\r\n <PropertyGroup>\r\n <TargetFramework>net6.0</TargetFramework>\r\n </PropertyGroup>\r\n</Project>");
}

[Fact]
Expand Down
4 changes: 2 additions & 2 deletions Reqnroll/Assist/EnumerableProjection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ public class EnumerableProjection<T> : IEnumerable<Projection<T>>
private readonly IEnumerable<T> collection;
private readonly IEnumerable<string> properties;

public EnumerableProjection(Table table, IEnumerable<T> collection = null)
public EnumerableProjection(TableHelpers tableHelpers, Table table, IEnumerable<T> collection = null)
{
if (table == null && collection == null)
throw new ArgumentNullException(nameof(table), "Either table or projectCollection must be specified");

if (table != null)
properties = table.Header;
this.collection = collection ?? table.CreateSet<T>();
this.collection = collection ?? tableHelpers.CreateSet<T>(table);
}

public IEnumerator<Projection<T>> GetEnumerator()
Expand Down
18 changes: 9 additions & 9 deletions Reqnroll/Assist/FindInSetExtensionMethods.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;

Expand All @@ -7,26 +8,25 @@ public static class FindInSetExtensionMethods
{
public static T FindInSet<T>(this Table table, IEnumerable<T> set)
{
var instanceTable = TEHelpers.GetTheProperInstanceTable(table, typeof (T));
var instanceTable = TEHelpers.GetTheProperInstanceTable(table, typeof(T));

var matches = set.Where(instance => InstanceMatchesTable(instance, instanceTable)).ToArray();
var matches = set.Where(instance => InstanceMatchesTable(Service.Instance, instance, instanceTable)).ToArray();

if (matches.Length > 1)
throw new ComparisonException("Multiple instances match the table");
if (matches.Length > 1) throw new ComparisonException("Multiple instances match the table");

return matches.FirstOrDefault();
}

public static IEnumerable<T> FindAllInSet<T>(this Table table, IEnumerable<T> set)
{
var instanceTable = TEHelpers.GetTheProperInstanceTable(table, typeof (T));
var instanceTable = TEHelpers.GetTheProperInstanceTable(table, typeof(T));

return set.Where(instance => InstanceMatchesTable(instance, instanceTable)).ToArray();
return set.Where(instance => InstanceMatchesTable(Service.Instance, instance, instanceTable)).ToArray();
}

private static bool InstanceMatchesTable<T>(T instance, Table table)
private static bool InstanceMatchesTable<T>(Service service, T instance, Table table)
{
return table.Rows.All(row => !InstanceComparisonExtensionMethods.ThereIsADifference(instance, row));
return table.Rows.All(row => !InstanceComparisonExtensionMethods.ThereIsADifference(service, instance, row));
}
}
}
}
67 changes: 33 additions & 34 deletions Reqnroll/Assist/InstanceComparisonExtensionMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,15 @@ namespace Reqnroll
{
public static class InstanceComparisonExtensionMethods
{

public static void CompareToInstance<T>(this Table table, T instance)
{
AssertThatTheInstanceExists(instance);

var instanceTable = TEHelpers.GetTheProperInstanceTable(table, typeof(T));

var differences = FindAnyDifferences(instanceTable, instance);
var differences = FindAnyDifferences(Service.Instance, instanceTable, instance);

if (ThereAreAnyDifferences(differences))
ThrowAnExceptionThatDescribesThoseDifferences(differences);
if (ThereAreAnyDifferences(differences)) ThrowAnExceptionThatDescribesThoseDifferences(differences);
}

/// <summary>
Expand All @@ -31,13 +29,12 @@ public static bool IsEquivalentToInstance<T>(this Table table, T instance)

var instanceTable = TEHelpers.GetTheProperInstanceTable(table, typeof(T));

return HasDifference(instanceTable, instance) == false;
return HasDifference(Service.Instance, instanceTable, instance) == false;
}

private static void AssertThatTheInstanceExists<T>(T instance)
{
if (instance == null)
throw new ComparisonException("The item to compare was null.");
if (instance == null) throw new ComparisonException("The item to compare was null.");
}

private static void ThrowAnExceptionThatDescribesThoseDifferences(IEnumerable<Difference> differences)
Expand All @@ -47,23 +44,25 @@ private static void ThrowAnExceptionThatDescribesThoseDifferences(IEnumerable<Di

private static string CreateDescriptiveErrorMessage(IEnumerable<Difference> differences)
{
return differences.Aggregate(@"The following fields did not match:",
(sum, next) => sum + (Environment.NewLine + next.Description));
return differences.Aggregate(
@"The following fields did not match:",
(sum, next) => sum + (Environment.NewLine + next.Description));
}

private static Difference[] FindAnyDifferences<T>(Table table, T instance)
private static Difference[] FindAnyDifferences<T>(Service service, Table table, T instance)
{
return (from row in table.Rows
where ThePropertyDoesNotExist(instance, row) || TheValuesDoNotMatch(instance, row)
select CreateDifferenceForThisRow(instance, row)).ToArray();
where ThePropertyDoesNotExist(instance, row) || TheValuesDoNotMatch(service, instance, row)
select CreateDifferenceForThisRow(service, instance, row)).ToArray();
}

private static bool HasDifference<T>(Table table, T instance)
private static bool HasDifference<T>(Service service, Table table, T instance)
{
// This method exists so it will stop evaluating the instance (hence stop using Reflection)
// after the first difference is found.
return (from row in table.Rows select row)
.Any(row => ThePropertyDoesNotExist(instance, row) || TheValuesDoNotMatch(instance, row));
return (from row in table.Rows
select row)
.Any(row => ThePropertyDoesNotExist(instance, row) || TheValuesDoNotMatch(service, instance, row));
}

private static bool ThereAreAnyDifferences(IEnumerable<Difference> differences)
Expand All @@ -73,50 +72,50 @@ private static bool ThereAreAnyDifferences(IEnumerable<Difference> differences)

internal static bool ThePropertyDoesNotExist<T>(T instance, DataTableRow row)
{
return instance.GetType().GetProperties()
.Any(property => TEHelpers.IsMemberMatchingToColumnName(property, row.Id())) == false;
return instance.GetType()
.GetProperties()
.Any(property => TEHelpers.IsMemberMatchingToColumnName(property, row.Id()))
== false;
}

internal static bool ThereIsADifference<T>(T instance, DataTableRow row)
internal static bool ThereIsADifference<T>(Service service, T instance, DataTableRow row)
{
return ThePropertyDoesNotExist(instance, row) || TheValuesDoNotMatch(instance, row);
return ThePropertyDoesNotExist(instance, row) || TheValuesDoNotMatch(service, instance, row);
}

private static bool TheValuesDoNotMatch<T>(T instance, DataTableRow row)
private static bool TheValuesDoNotMatch<T>(Service service, T instance, DataTableRow row)
{
var expected = GetTheExpectedValue(row);
var propertyValue = instance.GetPropertyValue(row.Id());
var comparer = FindValueComparerForValue(propertyValue);
var comparer = FindValueComparerForValue(service, propertyValue);

return comparer
.Compare(expected, propertyValue) == false;
.Compare(expected, propertyValue)
== false;
}

private static string GetTheExpectedValue(DataTableRow row)
{
return row.Value();
}

private static Difference CreateDifferenceForThisRow<T>(T instance, DataTableRow row)
private static Difference CreateDifferenceForThisRow<T>(Service service, T instance, DataTableRow row)
{
var propertyName = row.Id();

if (ThePropertyDoesNotExist(instance, row))
return new PropertyDoesNotExist(propertyName);
if (ThePropertyDoesNotExist(instance, row)) return new PropertyDoesNotExist(propertyName);

var expected = row.Value();
var actual = instance.GetPropertyValue(propertyName);
var comparer = FindValueComparerForProperty(instance, propertyName);
var comparer = FindValueComparerForProperty(service, instance, propertyName);
return new PropertyDiffers(propertyName, expected, actual, comparer);
}

private static IValueComparer FindValueComparerForProperty<T>(T instance, string propertyName) =>
FindValueComparerForValue(
instance.GetPropertyValue(propertyName));
private static IValueComparer FindValueComparerForProperty<T>(Service service, T instance, string propertyName) =>
FindValueComparerForValue(service, instance.GetPropertyValue(propertyName));

private static IValueComparer FindValueComparerForValue(object propertyValue) =>
Service.Instance.ValueComparers
.FirstOrDefault(x => x.CanCompare(propertyValue));
private static IValueComparer FindValueComparerForValue(Service service, object propertyValue) =>
service.ValueComparers.FirstOrDefault(x => x.CanCompare(propertyValue));

private abstract class Difference
{
Expand Down Expand Up @@ -156,7 +155,7 @@ public PropertyDiffers(string propertyName, object expected, object actual, IVal
}
}

public static class TableHelpers
public static class SetComparerTableHelpers
{
public static string Id(this DataTableRow row)
{
Expand All @@ -176,4 +175,4 @@ public ComparisonException(string message)
{
}
}
}
}
13 changes: 9 additions & 4 deletions Reqnroll/Assist/ProjectionExtensionMethods.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;

namespace Reqnroll.Assist
Expand All @@ -6,22 +7,26 @@ public static class ProjectionExtensionMethods
{
public static IEnumerable<Projection<T>> ToProjection<T>(this IEnumerable<T> collection, Table table = null)
{
return new EnumerableProjection<T>(table, collection);
var tableHelpers = new TableHelpers(Service.Instance);
return new EnumerableProjection<T>(tableHelpers, table, collection);
}

public static IEnumerable<Projection<T>> ToProjection<T>(this Table table)
{
return new EnumerableProjection<T>(table);
var tableHelpers = new TableHelpers(Service.Instance);
return new EnumerableProjection<T>(tableHelpers, table);
}

public static IEnumerable<Projection<T>> ToProjectionOfSet<T>(this Table table, IEnumerable<T> collection)
{
return new EnumerableProjection<T>(table);
var tableHelpers = new TableHelpers(Service.Instance);
return new EnumerableProjection<T>(tableHelpers, table);
}

public static IEnumerable<Projection<T>> ToProjectionOfInstance<T>(this Table table, T instance)
{
return new EnumerableProjection<T>(table);
var tableHelpers = new TableHelpers(Service.Instance);
return new EnumerableProjection<T>(tableHelpers, table);
}
}
}
6 changes: 3 additions & 3 deletions Reqnroll/Assist/ReqnrollDefaultValueRetrieverList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Reqnroll.Assist
#nullable enable
internal sealed class ReqnrollDefaultValueRetrieverList : ServiceComponentList<IValueRetriever>
{
public ReqnrollDefaultValueRetrieverList()
public ReqnrollDefaultValueRetrieverList(Service service)
: base(new List<IValueRetriever> {
// Sorted by likelihood
new StringValueRetriever(),
Expand All @@ -19,8 +19,8 @@ public ReqnrollDefaultValueRetrieverList()
new TimeSpanValueRetriever(),
new GuidValueRetriever(),
new EnumValueRetriever(),
new ListValueRetriever(),
new ArrayValueRetriever(),
new ListValueRetriever(service),
new ArrayValueRetriever(service),
new ByteValueRetriever(),
new SByteValueRetriever(),
new UIntValueRetriever(),
Expand Down
4 changes: 2 additions & 2 deletions Reqnroll/Assist/Service.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ public class Service
public Service()
{
ValueComparers = new ReqnrollDefaultValueComparerList();
ValueRetrievers = new ReqnrollDefaultValueRetrieverList();
ValueRetrievers = new ReqnrollDefaultValueRetrieverList(this);
}

public void RestoreDefaults()
{
ValueComparers = new ReqnrollDefaultValueComparerList();
ValueRetrievers = new ReqnrollDefaultValueRetrieverList();
ValueRetrievers = new ReqnrollDefaultValueRetrieverList(this);
}

[Obsolete("Use ValueComparers.Register")]
Expand Down
Loading

0 comments on commit 25f2f47

Please sign in to comment.