Skip to content

Commit

Permalink
Merge branch 'ShouldContainMatchingCount'
Browse files Browse the repository at this point in the history
  • Loading branch information
JakeGinnivan committed Jan 30, 2016
2 parents 054c70a + 1957252 commit 900072b
Show file tree
Hide file tree
Showing 9 changed files with 181 additions and 3 deletions.
1 change: 1 addition & 0 deletions src/Shouldly.Shared/Internals/IShouldlyAssertionContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,6 @@ internal interface IShouldlyAssertionContext
Case? CaseSensitivity { get; set; }
bool CodePartMatchesActual { get; }
Expression Filter { get; set; }
int? MatchCount { get; set; }
}
}
1 change: 1 addition & 0 deletions src/Shouldly.Shared/Internals/ShouldlyAssertionContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ internal class ShouldlyAssertionContext : IShouldlyAssertionContext
public bool IsNegatedAssertion { get { return ShouldMethod.Contains("Not"); } }
public string CustomMessage { get; set; }
public Expression Filter { get; set; }
public int? MatchCount { get; set; }

#if PORTABLE
internal ShouldlyAssertionContext(string shouldlyMethod, object expected = null, object actual = null)
Expand Down
2 changes: 1 addition & 1 deletion src/Shouldly.Shared/Internals/StringHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ internal static string ToStringAwesomely(this object value)
return info.GetValue(constant.Value).ToStringAwesomely();
}

#if net40
#if !net35
if (value is BinaryExpression)
{
return ExpressionToString.ExpressionStringBuilder.ToString(value.As<BinaryExpression>());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,23 @@ public override bool CanProcess(IShouldlyAssertionContext context)
public override string GenerateErrorMessage(IShouldlyAssertionContext context)
{
var codePart = context.CodePart == "null" ? context.Actual.ToStringAwesomely() : context.CodePart;

var elementPhrase = context.MatchCount.HasValue
? context.MatchCount.Value + " element(s)"
: "an element";

if (context.IsNegatedAssertion)
{
return
$@"{codePart}
{context.ShouldMethod.PascalToSpaced()} an element satisfying the condition
{context.ShouldMethod.PascalToSpaced()} {elementPhrase} satisfying the condition
{context.Expected.ToStringAwesomely()}
but does{""}";
}

return
$@"{codePart}
{context.ShouldMethod.PascalToSpaced()} an element satisfying the condition
{context.ShouldMethod.PascalToSpaced()} {elementPhrase} satisfying the condition
{context.Expected.ToStringAwesomely()}
but does not";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,26 @@ public static void ShouldContain<T>(this IEnumerable<T> actual, [InstantHandle]
ShouldContain(actual, elementPredicate, () => null);
}

public static void ShouldContain<T>(this IEnumerable<T> actual, [InstantHandle] Expression<Func<T, bool>> elementPredicate, int expectedCount)
{
ShouldContain(actual, elementPredicate, expectedCount, () => null);
}

public static void ShouldContain<T>(this IEnumerable<T> actual, [InstantHandle] Expression<Func<T, bool>> elementPredicate, int expectedCount, string customMessage)
{
ShouldContain(actual, elementPredicate, expectedCount, () => customMessage);
}

public static void ShouldContain<T>(this IEnumerable<T> actual, [InstantHandle] Expression<Func<T, bool>> elementPredicate, int expectedCount, Func<string> customMessage)
{
var condition = elementPredicate.Compile();
var actualCount = actual.Count(condition);
if (actualCount != expectedCount)
{
throw new ShouldAssertException(new ShouldContainWithCountShouldlyMessage(elementPredicate.Body, actual, expectedCount, customMessage).ToString());
}
}

public static void ShouldContain<T>(this IEnumerable<T> actual, [InstantHandle] Expression<Func<T, bool>> elementPredicate, string customMessage)
{
ShouldContain(actual, elementPredicate, () => customMessage);
Expand Down
13 changes: 13 additions & 0 deletions src/Shouldly.Shared/ShouldlyMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,19 @@ internal class ShouldlyThrowMessage : ShouldlyMessage
}
}

internal class ShouldContainWithCountShouldlyMessage : ShouldlyMessage
{
public ShouldContainWithCountShouldlyMessage(object expected, object actual, int matchCount, [InstantHandle] Func<string> customMessage, [CallerMemberName] string shouldlyMethod = null)
{
ShouldlyAssertionContext = new ShouldlyAssertionContext(shouldlyMethod, expected, actual)
{
HasRelevantActual = true,
MatchCount = matchCount
};
if (customMessage != null) ShouldlyAssertionContext.CustomMessage = customMessage();
}
}

#if net40

internal class TaskShouldlyThrowMessage : ShouldlyMessage
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
using Shouldly.Tests.Strings;
using Xunit;

namespace Shouldly.Tests.Shared.ShouldContain
{
public class CollectionPredicateWithCountScenario
{
[Fact]
public void CollectionWithACountOtherThanTheSpecifiedCountOfMatchingPredicatesFails()
{
var collection = new[] { "a", "b", "c", "c" };
Verify.ShouldFail(() =>
collection.ShouldContain(x => x == "c", 5),
#if net35
errorWithSource:
@"collection
should contain 5 element(s) satisfying the condition
(x = ""c"")
but does not",
errorWithoutSource:
@"[""a"", ""b"", ""c"", ""c""]
should contain 5 element(s) satisfying the condition
(x = ""c"")
but does not");
#else

errorWithSource:
@"collection
should contain 5 element(s) satisfying the condition
(x == ""c"")
but does not",
errorWithoutSource:
@"[""a"", ""b"", ""c"", ""c""]
should contain 5 element(s) satisfying the condition
(x == ""c"")
but does not");
#endif
}

[Fact]
public void CollectionWithACountOtherThanTheSpecifiedCountOfMatchingPredicatesFailsWithCustomMessage()
{
var collection = new[] { "a", "b", "c", "c" };
Verify.ShouldFail(() =>
collection.ShouldContain(x => x == "c", 5, "custom message"),
#if net35
errorWithSource:
@"collection
should contain 5 element(s) satisfying the condition
(x = ""c"")
but does not
Additional Info:
custom message",
errorWithoutSource:
@"[""a"", ""b"", ""c"", ""c""]
should contain 5 element(s) satisfying the condition
(x = ""c"")
but does not
Additional Info:
custom message");
#else
errorWithSource:
@"collection
should contain 5 element(s) satisfying the condition
(x == ""c"")
but does not
Additional Info:
custom message",
errorWithoutSource:
@"[""a"", ""b"", ""c"", ""c""]
should contain 5 element(s) satisfying the condition
(x == ""c"")
but does not
Additional Info:
custom message");
#endif
}

[Fact]
public void CollectionWithACountOtherThanTheSpecifiedCountOfMatchingPredicatesFailsWithCustomMessageFunc()
{
var collection = new[] { "a", "b", "c", "c" };
Verify.ShouldFail(() =>
collection.ShouldContain(x => x == "c", 5, () => "custom message"),
#if net35
errorWithSource:
@"collection
should contain 5 element(s) satisfying the condition
(x = ""c"")
but does not
Additional Info:
custom message",
errorWithoutSource:
@"[""a"", ""b"", ""c"", ""c""]
should contain 5 element(s) satisfying the condition
(x = ""c"")
but does not
Additional Info:
custom message");
#else

errorWithSource:
@"collection
should contain 5 element(s) satisfying the condition
(x == ""c"")
but does not
Additional Info:
custom message",
errorWithoutSource:
@"[""a"", ""b"", ""c"", ""c""]
should contain 5 element(s) satisfying the condition
(x == ""c"")
but does not
Additional Info:
custom message");
#endif
}

[Fact]
public void CollectionWithTheSpecifiedCountOfMatchingPredicatesSucceeds()
{
new[] { "a","b","c","c"}.ShouldContain(x => x == "c", 2); // collection has exactly two items that are "c"
new[] { 1, 2, 3, 4 }.ShouldContain(i => (i % 2) == 0, 2); // collection has exactly two items that are even
}
}
}
1 change: 1 addition & 0 deletions src/Shouldly.Tests.Shared/Shouldly.Tests.Shared.projitems
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@
<Compile Include="$(MSBuildThisFileDirectory)ShouldBe\WithTolerance\FloatScenario.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ShouldBe\WithTolerance\TimeSpanScenario.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ShouldCompleteInTests.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ShouldContain\CollectionPredicateWithCountScenario.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ShouldContain\DoubleWithToleranceScenario.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ShouldContain\EmptyArrayScenario.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ShouldContain\FloatWithToleranceScenario.cs" />
Expand Down
3 changes: 3 additions & 0 deletions src/ShouldlyConvention.Tests/ShouldlyApi.approved.cs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,9 @@ public class static ShouldBeEnumerableTestExtensions
public static void ShouldContain<T>(this System.Collections.Generic.IEnumerable<T> actual, T expected, string customMessage) { }
public static void ShouldContain<T>(this System.Collections.Generic.IEnumerable<T> actual, T expected, [JetBrains.Annotations.InstantHandleAttribute()] System.Func<string> customMessage) { }
public static void ShouldContain<T>(this System.Collections.Generic.IEnumerable<T> actual, [JetBrains.Annotations.InstantHandleAttribute()] System.Linq.Expressions.Expression<System.Func<T, bool>> elementPredicate) { }
public static void ShouldContain<T>(this System.Collections.Generic.IEnumerable<T> actual, [JetBrains.Annotations.InstantHandleAttribute()] System.Linq.Expressions.Expression<System.Func<T, bool>> elementPredicate, int expectedCount) { }
public static void ShouldContain<T>(this System.Collections.Generic.IEnumerable<T> actual, [JetBrains.Annotations.InstantHandleAttribute()] System.Linq.Expressions.Expression<System.Func<T, bool>> elementPredicate, int expectedCount, string customMessage) { }
public static void ShouldContain<T>(this System.Collections.Generic.IEnumerable<T> actual, [JetBrains.Annotations.InstantHandleAttribute()] System.Linq.Expressions.Expression<System.Func<T, bool>> elementPredicate, int expectedCount, System.Func<string> customMessage) { }
public static void ShouldContain<T>(this System.Collections.Generic.IEnumerable<T> actual, [JetBrains.Annotations.InstantHandleAttribute()] System.Linq.Expressions.Expression<System.Func<T, bool>> elementPredicate, string customMessage) { }
public static void ShouldContain<T>(this System.Collections.Generic.IEnumerable<T> actual, [JetBrains.Annotations.InstantHandleAttribute()] System.Linq.Expressions.Expression<System.Func<T, bool>> elementPredicate, [JetBrains.Annotations.InstantHandleAttribute()] System.Func<string> customMessage) { }
public static void ShouldContain(this System.Collections.Generic.IEnumerable<float> actual, float expected, double tolerance) { }
Expand Down

0 comments on commit 900072b

Please sign in to comment.