Skip to content

Commit

Permalink
Merge 7d4896c into 418a405
Browse files Browse the repository at this point in the history
  • Loading branch information
logiclrd committed Feb 21, 2022
2 parents 418a405 + 7d4896c commit 8903457
Show file tree
Hide file tree
Showing 15 changed files with 2,128 additions and 0 deletions.
31 changes: 31 additions & 0 deletions Src/FluentAssertions/AssertionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Net.Http;
using System.Reflection;
Expand Down Expand Up @@ -384,6 +385,36 @@ public static StringCollectionAssertions Should(this IEnumerable<string> @this)
return new GenericDictionaryAssertions<TCollection, TKey, TValue>(actualValue);
}

/// <summary>
/// Returns an assertions object that provides methods for asserting the state of a <see cref="DataTableCollection"/>.
/// </summary>
[Pure]
public static GenericCollectionAssertions<DataTable> Should(this DataTableCollection actualValue)
{
return new GenericCollectionAssertions<DataTable>(
NonGenericCollectionWrapper.Create(actualValue));
}

/// <summary>
/// Returns an assertions object that provides methods for asserting the state of a <see cref="DataColumnCollection"/>.
/// </summary>
[Pure]
public static GenericCollectionAssertions<DataColumn> Should(this DataColumnCollection actualValue)
{
return new GenericCollectionAssertions<DataColumn>(
NonGenericCollectionWrapper.Create(actualValue));
}

/// <summary>
/// Returns an assertions object that provides methods for asserting the state of a <see cref="DataRowCollection"/>.
/// </summary>
[Pure]
public static GenericCollectionAssertions<DataRow> Should(this DataRowCollection actualValue)
{
return new GenericCollectionAssertions<DataRow>(
NonGenericCollectionWrapper.Create(actualValue));
}

/// <summary>
/// Returns a <see cref="DataColumnAssertions"/> object that can be used to assert the
/// current <see cref="DataColumn"/>.
Expand Down
71 changes: 71 additions & 0 deletions Src/FluentAssertions/Common/NonGenericCollectionWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Linq;

namespace FluentAssertions.Common
{
internal static class NonGenericCollectionWrapper
{
public static NonGenericCollectionWrapper<DataTableCollection, DataTable> Create(DataTableCollection collection)
{
return
(collection != null)
? new NonGenericCollectionWrapper<DataTableCollection, DataTable>(collection)
: null;
}

public static NonGenericCollectionWrapper<DataColumnCollection, DataColumn> Create(DataColumnCollection collection)
{
return
(collection != null)
? new NonGenericCollectionWrapper<DataColumnCollection, DataColumn>(collection)
: null;
}

public static NonGenericCollectionWrapper<DataRowCollection, DataRow> Create(DataRowCollection collection)
{
return
(collection != null)
? new NonGenericCollectionWrapper<DataRowCollection, DataRow>(collection)
: null;
}
}

internal class NonGenericCollectionWrapper<TCollection, TItem> : ICollection<TItem>, IEnumerable<TItem>
where TCollection : ICollection, IEnumerable
{
public TCollection UnderlyingCollection { get; private set; }

public NonGenericCollectionWrapper(TCollection collection)
{
Guard.ThrowIfArgumentIsNull(collection, nameof(collection));

UnderlyingCollection = collection;
}

public int Count => UnderlyingCollection.Count;

public bool IsReadOnly => true;

public IEnumerator<TItem> GetEnumerator() => UnderlyingCollection.Cast<TItem>().GetEnumerator();

IEnumerator IEnumerable.GetEnumerator() => UnderlyingCollection.GetEnumerator();

public bool Contains(TItem item) => UnderlyingCollection.Cast<TItem>().Contains(item);

public void CopyTo(TItem[] array, int arrayIndex) => UnderlyingCollection.CopyTo(array, arrayIndex);

// Mutation is not supported, but these methods must be implemented to satisfy ICollection<T>:
// * Add
// * Clear
// * Remove

public void Add(TItem item) => throw new NotSupportedException();

public void Clear() => throw new NotSupportedException();

public bool Remove(TItem item) => throw new NotSupportedException();
}
}
152 changes: 152 additions & 0 deletions Src/FluentAssertions/DataColumnCollectionAssertionExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
using System.Collections.Generic;
using System.Data;
using System.Linq;

using FluentAssertions.Collections;
using FluentAssertions.Common;
using FluentAssertions.Execution;

namespace FluentAssertions
{
public static class DataColumnCollectionAssertionExtensions
{
/// <summary>
/// Asserts that an object reference refers to the exact same object as another object reference.
/// </summary>
/// <param name="expected">The expected object</param>
/// <param name="because">
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
/// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
/// </param>
/// <param name="becauseArgs">
/// Zero or more objects to format using the placeholders in <paramref name="because"/>.
/// </param>
public static AndConstraint<GenericCollectionAssertions<DataColumn>> BeSameAs(
this GenericCollectionAssertions<DataColumn> assertion, DataColumnCollection expected, string because = "",
params object[] becauseArgs)
{
if (assertion.Subject is NonGenericCollectionWrapper<DataColumnCollection, DataColumn> wrapper)
{
var actualSubject = wrapper.UnderlyingCollection;

Execute.Assertion
.UsingLineBreaks
.ForCondition(ReferenceEquals(actualSubject, expected))
.BecauseOf(because, becauseArgs)
.FailWith(
"Expected {context:column collection} to refer to {0}{reason}, but found {1} (different underlying object).",
expected, actualSubject);
}
else
{
Execute.Assertion
.UsingLineBreaks
.BecauseOf(because, becauseArgs)
.FailWith(
"Expected {context:column collection} to refer to DataColumnCollection{reason}, but found {0} (different type).",
assertion.Subject);
}

return new AndConstraint<GenericCollectionAssertions<DataColumn>>(assertion);
}

/// <summary>
/// Asserts that an object reference refers to a different object than another object reference refers to.
/// </summary>
/// <param name="unexpected">The unexpected object</param>
/// <param name="because">
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
/// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
/// </param>
/// <param name="becauseArgs">
/// Zero or more objects to format using the placeholders in <paramref name="because"/>.
/// </param>
public static AndConstraint<GenericCollectionAssertions<DataColumn>> NotBeSameAs(
this GenericCollectionAssertions<DataColumn> assertion, DataColumnCollection unexpected, string because = "",
params object[] becauseArgs)
{
if (assertion.Subject is NonGenericCollectionWrapper<DataColumnCollection, DataColumn> wrapper)
{
var actualSubject = wrapper.UnderlyingCollection;

Execute.Assertion
.UsingLineBreaks
.ForCondition(!ReferenceEquals(actualSubject, unexpected))
.BecauseOf(because, becauseArgs)
.FailWith("Did not expect {context:column collection} to refer to {0}{reason}.", unexpected);
}

return new AndConstraint<GenericCollectionAssertions<DataColumn>>(assertion);
}

/// <summary>
/// Assert that the current collection has the same number of elements as <paramref name="otherCollection" />.
/// </summary>
/// <param name="otherCollection">The other collection with the same expected number of elements</param>
/// <param name="because">
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
/// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
/// </param>
/// <param name="becauseArgs">
/// Zero or more objects to format using the placeholders in <paramref name="because" />.
/// </param>
public static AndConstraint<GenericCollectionAssertions<DataColumn>> HaveSameCount(
this GenericCollectionAssertions<DataColumn> assertion, DataColumnCollection otherCollection, string because = "",
params object[] becauseArgs)
{
Guard.ThrowIfArgumentIsNull(
otherCollection, nameof(otherCollection), "Cannot verify count against a <null> collection.");

Execute.Assertion
.BecauseOf(because, becauseArgs)
.WithExpectation("Expected {context:collection} to have ")
.Given(() => assertion.Subject)
.ForCondition(subject => subject is not null)
.FailWith("the same count as {0}{reason}, but found <null>.", otherCollection)
.Then
.Given((subject) => (actual: subject.Count(), expected: otherCollection.Count))
.ForCondition(count => count.actual == count.expected)
.FailWith("{0} column(s){reason}, but found {1}.", count => count.expected, count => count.actual)
.Then
.ClearExpectation();

return new AndConstraint<GenericCollectionAssertions<DataColumn>>(assertion);
}

/// <summary>
/// Assert that the current collection of <see cref="DataColumn"/>s does not have the same number of columns as
/// <paramref name="otherCollection" />.
/// </summary>
/// <param name="otherCollection">The other <see cref="DataColumnCollection"/> with the unexpected number of
/// elements</param>
/// <param name="because">
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
/// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
/// </param>
/// <param name="becauseArgs">
/// Zero or more objects to format using the placeholders in <paramref name="because" />.
/// </param>
public static AndConstraint<GenericCollectionAssertions<DataColumn>> NotHaveSameCount(
this GenericCollectionAssertions<DataColumn> assertion, DataColumnCollection otherCollection, string because = "",
params object[] becauseArgs)
{
Guard.ThrowIfArgumentIsNull(
otherCollection, nameof(otherCollection), "Cannot verify count against a <null> collection.");

Execute.Assertion
.BecauseOf(because, becauseArgs)
.WithExpectation("Expected {context:collection} to not have ")
.Given(() => assertion.Subject)
.ForCondition(subject => subject is not null)
.FailWith("the same count as {0}{reason}, but found <null>.", otherCollection)
.Then
.Given((subject) => (actual: subject.Count(), expected: otherCollection.Count))
.ForCondition(count => count.actual != count.expected)
.FailWith("{0} column(s){reason}, but found {1}.", count => count.expected, count => count.actual)
.Then
.ClearExpectation();

return new AndConstraint<GenericCollectionAssertions<DataColumn>>(assertion);
}
}
}
Loading

0 comments on commit 8903457

Please sign in to comment.