diff --git a/src/TestFramework/TestFramework/Assertions/Assert.AreAllDistinct.cs b/src/TestFramework/TestFramework/Assertions/Assert.AreAllDistinct.cs
new file mode 100644
index 0000000000..0929bfe3dc
--- /dev/null
+++ b/src/TestFramework/TestFramework/Assertions/Assert.AreAllDistinct.cs
@@ -0,0 +1,234 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace Microsoft.VisualStudio.TestTools.UnitTesting;
+
+///
+/// A collection of helper classes to test various conditions within
+/// unit tests. If the condition being tested is not met, an exception
+/// is thrown.
+///
+#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
+#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+public sealed partial class Assert
+{
+ #region AreAllDistinct
+
+ ///
+ /// Tests whether all items in the specified collection are distinct (no two
+ /// elements are equal) and throws an exception if any two elements in the
+ /// collection are equal.
+ ///
+ /// The type of the collection items.
+ ///
+ /// The collection in which to search for duplicate elements.
+ ///
+ ///
+ /// The message to include in the exception when contains
+ /// at least one duplicate element. The message is shown in test results.
+ ///
+ ///
+ /// The syntactic expression of collection as given by the compiler via caller argument expression.
+ /// Users shouldn't pass a value for this parameter.
+ ///
+ ///
+ /// Thrown if is null or contains at least one duplicate element.
+ ///
+ public static void AreAllDistinct([NotNull] IEnumerable? collection, string? message = "", [CallerArgumentExpression(nameof(collection))] string collectionExpression = "")
+ {
+ CheckParameterNotNull(collection, "Assert.AreAllDistinct", "collection");
+ AreAllDistinctImpl(collection, EqualityComparer.Default, comparerTypeName: null, message, collectionExpression);
+ }
+
+ ///
+ /// Tests whether all items in the specified collection are distinct (no two
+ /// elements are equal) using the supplied and throws
+ /// an exception if any two elements in the collection are equal.
+ ///
+ /// The type of the collection items.
+ ///
+ /// The collection in which to search for duplicate elements.
+ ///
+ ///
+ /// The equality comparer to use when comparing elements.
+ ///
+ ///
+ /// The message to include in the exception when contains
+ /// at least one duplicate element. The message is shown in test results.
+ ///
+ ///
+ /// The syntactic expression of collection as given by the compiler via caller argument expression.
+ /// Users shouldn't pass a value for this parameter.
+ ///
+ ///
+ /// Thrown if is null or contains at least one duplicate element.
+ ///
+ public static void AreAllDistinct([NotNull] IEnumerable? collection, [NotNull] IEqualityComparer? comparer, string? message = "", [CallerArgumentExpression(nameof(collection))] string collectionExpression = "")
+ {
+ CheckParameterNotNull(collection, "Assert.AreAllDistinct", "collection");
+ CheckParameterNotNull(comparer, "Assert.AreAllDistinct", "comparer");
+ AreAllDistinctImpl(collection, comparer, comparerTypeName: comparer.GetType().ToString(), message, collectionExpression);
+ }
+
+ ///
+ /// Tests whether all items in the specified collection are distinct (no two
+ /// elements are equal) and throws an exception if any two elements in the
+ /// collection are equal.
+ ///
+ ///
+ /// The collection in which to search for duplicate elements.
+ ///
+ ///
+ /// The message to include in the exception when contains
+ /// at least one duplicate element. The message is shown in test results.
+ ///
+ ///
+ /// The syntactic expression of collection as given by the compiler via caller argument expression.
+ /// Users shouldn't pass a value for this parameter.
+ ///
+ ///
+ /// Thrown if is null or contains at least one duplicate element.
+ ///
+ public static void AreAllDistinct([NotNull] IEnumerable? collection, string? message = "", [CallerArgumentExpression(nameof(collection))] string collectionExpression = "")
+ {
+ CheckParameterNotNull(collection, "Assert.AreAllDistinct", "collection");
+ AreAllDistinctImpl(collection.Cast(), EqualityComparer.Default, comparerTypeName: null, message, collectionExpression);
+ }
+
+ ///
+ /// Tests whether all items in the specified collection are distinct (no two
+ /// elements are equal) using the supplied and throws
+ /// an exception if any two elements in the collection are equal.
+ ///
+ ///
+ /// The collection in which to search for duplicate elements.
+ ///
+ ///
+ /// The equality comparer to use when comparing elements.
+ ///
+ ///
+ /// The message to include in the exception when contains
+ /// at least one duplicate element. The message is shown in test results.
+ ///
+ ///
+ /// The syntactic expression of collection as given by the compiler via caller argument expression.
+ /// Users shouldn't pass a value for this parameter.
+ ///
+ ///
+ /// Thrown if is null or contains at least one duplicate element.
+ ///
+ public static void AreAllDistinct([NotNull] IEnumerable? collection, [NotNull] IEqualityComparer? comparer, string? message = "", [CallerArgumentExpression(nameof(collection))] string collectionExpression = "")
+ {
+ CheckParameterNotNull(collection, "Assert.AreAllDistinct", "collection");
+ CheckParameterNotNull(comparer, "Assert.AreAllDistinct", "comparer");
+ AreAllDistinctImpl(collection.Cast(), new NonGenericEqualityComparerAdapter(comparer), comparerTypeName: comparer.GetType().ToString(), message, collectionExpression);
+ }
+
+#pragma warning disable CS8714 // The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match 'notnull' constraint.
+ private static void AreAllDistinctImpl(IEnumerable collection, IEqualityComparer comparer, string? comparerTypeName, string? message, string collectionExpression)
+ {
+ List snapshot = collection is List list ? list : [.. collection];
+
+#pragma warning disable IDE0028 // Collection initialization can be simplified - target-typed `new` cannot pass the comparer in the same syntactic form expected.
+ var seen = new HashSet(comparer);
+#pragma warning restore IDE0028
+
+ bool seenNull = false;
+ List? duplicates = null;
+ HashSet? duplicatesSeen = null;
+ bool nullDuplicateRecorded = false;
+
+ foreach (T item in snapshot)
+ {
+ if (item is null)
+ {
+ if (!seenNull)
+ {
+ seenNull = true;
+ continue;
+ }
+
+ if (!nullDuplicateRecorded)
+ {
+ duplicates ??= [];
+ duplicates.Add(default!);
+ nullDuplicateRecorded = true;
+ }
+
+ continue;
+ }
+
+ if (!seen.Add(item))
+ {
+#pragma warning disable IDE0028 // Collection initialization can be simplified - target-typed `new` cannot pass the comparer in the same syntactic form expected.
+ duplicatesSeen ??= new(comparer);
+#pragma warning restore IDE0028
+ if (duplicatesSeen.Add(item))
+ {
+ duplicates ??= [];
+ duplicates.Add(item);
+ }
+ }
+ }
+
+ if (duplicates is not null)
+ {
+ ReportAssertAreAllDistinctFailed(snapshot, duplicates, comparerTypeName, message, collectionExpression);
+ }
+ }
+#pragma warning restore CS8714
+
+ [DoesNotReturn]
+ private static void ReportAssertAreAllDistinctFailed(IEnumerable collection, List duplicates, string? comparerTypeName, string? message, string collectionExpression)
+ {
+ string collectionText = AssertionValueRenderer.RenderValue(collection);
+ string duplicatesText = AssertionValueRenderer.RenderValue(duplicates);
+
+ EvidenceBlock evidence = EvidenceBlock.Create()
+ .AddLine("duplicates:", duplicatesText)
+ .AddLine("collection:", collectionText);
+
+ if (comparerTypeName is not null)
+ {
+ evidence.AddLine("comparer:", comparerTypeName);
+ }
+
+ StructuredAssertionMessage structured = new(FrameworkMessages.AreAllDistinctFailedSummary);
+ structured.WithUserMessage(message);
+ structured.WithEvidence(evidence);
+ structured.WithExpectedAndActual(expectedText: null, actualText: collectionText);
+ structured.WithCallSiteExpression(BuildCallSiteWithComparerForCollection("Assert.AreAllDistinct", collectionExpression, comparerTypeName is not null));
+
+ ReportAssertFailed(structured);
+ }
+
+ private static string? BuildCallSiteWithComparerForCollection(string assertionMethodName, string collectionExpression, bool hasComparer)
+ {
+ string? callSite = FormatCallSiteExpression(assertionMethodName, collectionExpression, "");
+ if (callSite is null || !hasComparer)
+ {
+ return callSite;
+ }
+
+ // FormatCallSiteExpression has no overload accepting a third argument expression; insert
+ // the placeholder so the rendered call-site reflects the overload that was actually invoked.
+ Debug.Assert(callSite.Length > 0 && callSite[callSite.Length - 1] == ')', "FormatCallSiteExpression contract: rendered call-site must end with ')'.");
+ return string.Concat(callSite.Remove(callSite.Length - 1), ", )");
+ }
+
+ #endregion // AreAllDistinct
+
+ // TODO: Deduplicate with the same adapter in Assert.CollectionEquivalence.cs (introduced by PR #8234)
+ // once both PRs have landed.
+ private sealed class NonGenericEqualityComparerAdapter : IEqualityComparer
+ {
+ private readonly IEqualityComparer _comparer;
+
+ public NonGenericEqualityComparerAdapter(IEqualityComparer comparer)
+ => _comparer = comparer;
+
+ bool IEqualityComparer.Equals(object? x, object? y) => _comparer.Equals(x, y);
+
+ int IEqualityComparer.GetHashCode(object? obj) => obj is null ? 0 : _comparer.GetHashCode(obj);
+ }
+}
diff --git a/src/TestFramework/TestFramework/Assertions/Assert.AreAllNotNull.cs b/src/TestFramework/TestFramework/Assertions/Assert.AreAllNotNull.cs
new file mode 100644
index 0000000000..88b0f15899
--- /dev/null
+++ b/src/TestFramework/TestFramework/Assertions/Assert.AreAllNotNull.cs
@@ -0,0 +1,105 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace Microsoft.VisualStudio.TestTools.UnitTesting;
+
+///
+/// A collection of helper classes to test various conditions within
+/// unit tests. If the condition being tested is not met, an exception
+/// is thrown.
+///
+#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
+#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+public sealed partial class Assert
+{
+ #region AreAllNotNull
+
+ ///
+ /// Tests whether all items in the specified collection are non-null and throws
+ /// an exception if any element is null.
+ ///
+ ///
+ /// The collection in which to search for null elements.
+ ///
+ ///
+ /// The message to include in the exception when contains
+ /// a null element. The message is shown in test results.
+ ///
+ ///
+ /// The syntactic expression of collection as given by the compiler via caller argument expression.
+ /// Users shouldn't pass a value for this parameter.
+ ///
+ ///
+ /// Thrown if is null or contains at least one null element.
+ ///
+ public static void AreAllNotNull([NotNull] IEnumerable? collection, string? message = "", [CallerArgumentExpression(nameof(collection))] string collectionExpression = "")
+ {
+ CheckParameterNotNull(collection, "Assert.AreAllNotNull", "collection");
+ AreAllNotNullImpl(collection.Cast(), message, collectionExpression);
+ }
+
+ ///
+ /// Tests whether all items in the specified collection are non-null and throws
+ /// an exception if any element is null.
+ ///
+ /// The type of the collection items.
+ ///
+ /// The collection in which to search for null elements.
+ ///
+ ///
+ /// The message to include in the exception when contains
+ /// a null element. The message is shown in test results.
+ ///
+ ///
+ /// The syntactic expression of collection as given by the compiler via caller argument expression.
+ /// Users shouldn't pass a value for this parameter.
+ ///
+ ///
+ /// Thrown if is null or contains at least one null element.
+ ///
+ public static void AreAllNotNull([NotNull] IEnumerable? collection, string? message = "", [CallerArgumentExpression(nameof(collection))] string collectionExpression = "")
+ {
+ CheckParameterNotNull(collection, "Assert.AreAllNotNull", "collection");
+ AreAllNotNullImpl(collection, message, collectionExpression);
+ }
+
+ private static void AreAllNotNullImpl(IEnumerable collection, string? message, string collectionExpression)
+ {
+ List snapshot = collection is List list ? list : [.. collection];
+ List? nullIndices = null;
+ for (int i = 0; i < snapshot.Count; i++)
+ {
+ if (snapshot[i] is null)
+ {
+ nullIndices ??= [];
+ nullIndices.Add(i);
+ }
+ }
+
+ if (nullIndices is not null)
+ {
+ ReportAssertAreAllNotNullFailed(snapshot, nullIndices, message, collectionExpression);
+ }
+ }
+
+ [DoesNotReturn]
+ private static void ReportAssertAreAllNotNullFailed(IEnumerable collection, List nullIndices, string? message, string collectionExpression)
+ {
+ string collectionText = AssertionValueRenderer.RenderValue(collection);
+ string nullIndicesText = AssertionValueRenderer.RenderValue(nullIndices);
+
+ EvidenceBlock evidence = EvidenceBlock.Create()
+ .AddLine("null indices:", nullIndicesText)
+ .AddLine("collection:", collectionText);
+
+ StructuredAssertionMessage structured = new(FrameworkMessages.AreAllNotNullFailedSummary);
+ structured.WithUserMessage(message);
+ structured.WithEvidence(evidence);
+ structured.WithExpectedAndActual(expectedText: null, actualText: collectionText);
+ structured.WithCallSiteExpression(FormatCallSiteExpression("Assert.AreAllNotNull", collectionExpression, ""));
+
+ ReportAssertFailed(structured);
+ }
+
+ #endregion // AreAllNotNull
+}
diff --git a/src/TestFramework/TestFramework/Assertions/Assert.AreAllOfType.cs b/src/TestFramework/TestFramework/Assertions/Assert.AreAllOfType.cs
new file mode 100644
index 0000000000..5ae37365b4
--- /dev/null
+++ b/src/TestFramework/TestFramework/Assertions/Assert.AreAllOfType.cs
@@ -0,0 +1,174 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace Microsoft.VisualStudio.TestTools.UnitTesting;
+
+///
+/// A collection of helper classes to test various conditions within
+/// unit tests. If the condition being tested is not met, an exception
+/// is thrown.
+///
+#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
+#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+public sealed partial class Assert
+{
+ #region AreAllOfType
+
+ ///
+ /// Tests whether all elements in the specified collection are instances of (or
+ /// derived from) the expected type and throws an exception if the expected type is
+ /// not in the inheritance hierarchy of one or more of the elements, or if any
+ /// element is null.
+ ///
+ ///
+ /// The expected type of each element of .
+ ///
+ ///
+ /// The collection containing elements the test expects to be of the specified type.
+ ///
+ ///
+ /// The message to include in the exception when an element in
+ /// is null or not an instance of . The message is shown in
+ /// test results.
+ ///
+ ///
+ /// The syntactic expression of expectedType as given by the compiler via caller argument expression.
+ /// Users shouldn't pass a value for this parameter.
+ ///
+ ///
+ /// The syntactic expression of collection as given by the compiler via caller argument expression.
+ /// Users shouldn't pass a value for this parameter.
+ ///
+ ///
+ /// Thrown if or is null,
+ /// or some elements of are null or do not inherit from /
+ /// implement .
+ ///
+ public static void AreAllOfType([NotNull] Type? expectedType, [NotNull] IEnumerable? collection, string? message = "", [CallerArgumentExpression(nameof(expectedType))] string expectedTypeExpression = "", [CallerArgumentExpression(nameof(collection))] string collectionExpression = "")
+ {
+ CheckParameterNotNull(expectedType, "Assert.AreAllOfType", "expectedType");
+ CheckParameterNotNull(collection, "Assert.AreAllOfType", "collection");
+ AreAllOfTypeImpl(collection, expectedType, genericTypeArgumentName: null, message, collectionExpression, expectedTypeExpression);
+ }
+
+ ///
+ /// Tests whether all elements in the specified collection are instances of (or
+ /// derived from) the expected type and throws an exception if the expected type is
+ /// not in the inheritance hierarchy of one or more of the elements, or if any
+ /// element is null.
+ ///
+ /// The type each element of is expected to be.
+ ///
+ /// The collection containing elements the test expects to be of the specified type.
+ ///
+ ///
+ /// The message to include in the exception when an element in
+ /// is null or not an instance of . The message is shown in
+ /// test results.
+ ///
+ ///
+ /// The syntactic expression of collection as given by the compiler via caller argument expression.
+ /// Users shouldn't pass a value for this parameter.
+ ///
+ ///
+ /// Thrown if is null or some elements of
+ /// are null or do not inherit from / implement
+ /// .
+ ///
+ public static void AreAllOfType([NotNull] IEnumerable? collection, string? message = "", [CallerArgumentExpression(nameof(collection))] string collectionExpression = "")
+ {
+ CheckParameterNotNull(collection, "Assert.AreAllOfType", "collection");
+ AreAllOfTypeImpl(collection, typeof(TExpected), genericTypeArgumentName: "TExpected", message, collectionExpression, expectedTypeExpression: null);
+ }
+
+ private static void AreAllOfTypeImpl(IEnumerable collection, Type expectedType, string? genericTypeArgumentName, string? message, string collectionExpression, string? expectedTypeExpression)
+ {
+ List snapshot = [.. collection.Cast()];
+ List? mismatches = null;
+ for (int i = 0; i < snapshot.Count; i++)
+ {
+ object? element = snapshot[i];
+ if (element is null)
+ {
+ mismatches ??= [];
+ mismatches.Add(new TypeMismatch(i, actualType: null));
+ }
+ else if (!expectedType.IsInstanceOfType(element))
+ {
+ mismatches ??= [];
+ mismatches.Add(new TypeMismatch(i, element.GetType()));
+ }
+ }
+
+ if (mismatches is not null)
+ {
+ ReportAssertAreAllOfTypeFailed(snapshot, expectedType, mismatches, genericTypeArgumentName, message, collectionExpression, expectedTypeExpression);
+ }
+ }
+
+ [DoesNotReturn]
+ private static void ReportAssertAreAllOfTypeFailed(IEnumerable collection, Type expectedType, List mismatches, string? genericTypeArgumentName, string? message, string collectionExpression, string? expectedTypeExpression)
+ {
+ string collectionText = AssertionValueRenderer.RenderValue(collection);
+ string expectedTypeText = $"{expectedType} (or derived)";
+
+ StringBuilder mismatchesBuilder = new();
+ mismatchesBuilder.Append('[');
+ for (int i = 0; i < mismatches.Count; i++)
+ {
+ if (i > 0)
+ {
+ mismatchesBuilder.Append(", ");
+ }
+
+ TypeMismatch mismatch = mismatches[i];
+ mismatchesBuilder.Append("index ").Append(mismatch.Index).Append(": ");
+ if (mismatch.ActualType is null)
+ {
+ mismatchesBuilder.Append("");
+ }
+ else
+ {
+ mismatchesBuilder.Append(mismatch.ActualType);
+ }
+ }
+
+ mismatchesBuilder.Append(']');
+
+ EvidenceBlock evidence = EvidenceBlock.Create()
+ .AddLine("expected type:", expectedTypeText)
+ .AddLine("mismatches:", mismatchesBuilder.ToString())
+ .AddLine("collection:", collectionText);
+
+ string assertionMethodName = genericTypeArgumentName is null
+ ? "Assert.AreAllOfType"
+ : $"Assert.AreAllOfType<{genericTypeArgumentName}>";
+
+ string? callSite = genericTypeArgumentName is null
+ ? FormatCallSiteExpression(assertionMethodName, expectedTypeExpression!, collectionExpression, "", "")
+ : FormatCallSiteExpression(assertionMethodName, collectionExpression, "");
+
+ StructuredAssertionMessage structured = new(FrameworkMessages.AreAllOfTypeFailedSummary);
+ structured.WithUserMessage(message);
+ structured.WithEvidence(evidence);
+ structured.WithExpectedAndActual(expectedTypeText, collectionText);
+ structured.WithCallSiteExpression(callSite);
+
+ ReportAssertFailed(structured);
+ }
+
+ private readonly struct TypeMismatch
+ {
+ public TypeMismatch(int index, Type? actualType)
+ {
+ Index = index;
+ ActualType = actualType;
+ }
+
+ public int Index { get; }
+
+ public Type? ActualType { get; }
+ }
+
+ #endregion // AreAllOfType
+}
diff --git a/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt b/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt
index b6e5dde3df..b33be34749 100644
--- a/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt
+++ b/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt
@@ -2,6 +2,14 @@
[MSTESTEXP]static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Scope() -> System.IDisposable!
Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException.ActualText.get -> string?
Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException.ExpectedText.get -> string?
+static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreAllDistinct(System.Collections.IEnumerable? collection, System.Collections.IEqualityComparer? comparer, string? message = "", string! collectionExpression = "") -> void
+static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreAllDistinct(System.Collections.IEnumerable? collection, string? message = "", string! collectionExpression = "") -> void
+static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreAllDistinct(System.Collections.Generic.IEnumerable? collection, System.Collections.Generic.IEqualityComparer? comparer, string? message = "", string! collectionExpression = "") -> void
+static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreAllDistinct(System.Collections.Generic.IEnumerable? collection, string? message = "", string! collectionExpression = "") -> void
+static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreAllNotNull(System.Collections.IEnumerable? collection, string? message = "", string! collectionExpression = "") -> void
+static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreAllNotNull(System.Collections.Generic.IEnumerable? collection, string? message = "", string! collectionExpression = "") -> void
+static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreAllOfType(System.Type? expectedType, System.Collections.IEnumerable? collection, string? message = "", string! expectedTypeExpression = "", string! collectionExpression = "") -> void
+static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreAllOfType(System.Collections.IEnumerable? collection, string? message = "", string! collectionExpression = "") -> void
static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEquivalent(T? expected, T? actual, bool strict, string? message = "", string! expectedExpression = "", string! actualExpression = "") -> void
static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEquivalent(T? expected, T? actual, string? message = "", string! expectedExpression = "", string! actualExpression = "") -> void
static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEquivalent(T? notExpected, T? actual, bool strict, string? message = "", string! notExpectedExpression = "", string! actualExpression = "") -> void
diff --git a/src/TestFramework/TestFramework/Resources/FrameworkMessages.resx b/src/TestFramework/TestFramework/Resources/FrameworkMessages.resx
index dd9fc1b561..30692162b0 100644
--- a/src/TestFramework/TestFramework/Resources/FrameworkMessages.resx
+++ b/src/TestFramework/TestFramework/Resources/FrameworkMessages.resx
@@ -479,6 +479,15 @@ Actual: {2}
Expected value to not be null.
+
+ Expected all items in collection to be non-null.
+
+
+ Expected all items in collection to be distinct.
+
+
+ Expected all items in collection to be of the specified type.
+
Expected string to match the specified regular expression.
diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.cs.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.cs.xlf
index 59ccc4447e..fcdb0c36a4 100644
--- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.cs.xlf
+++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.cs.xlf
@@ -17,6 +17,21 @@
Byla nalezena duplicitní položka:<{1}>. {0}
+
+ Expected all items in collection to be distinct.
+ Expected all items in collection to be distinct.
+
+
+
+ Expected all items in collection to be non-null.
+ Expected all items in collection to be non-null.
+
+
+
+ Expected all items in collection to be of the specified type.
+ Expected all items in collection to be of the specified type.
+
+
Expected:<{1}>. Actual:<{2}>. {0}
Očekáváno:<{1}>. Aktuálně:<{2}>. {0}
diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.de.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.de.xlf
index 512defa5c8..5e7f1c48d2 100644
--- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.de.xlf
+++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.de.xlf
@@ -17,6 +17,21 @@
Doppeltes Element gefunden: <{1}>. {0}
+
+ Expected all items in collection to be distinct.
+ Expected all items in collection to be distinct.
+
+
+
+ Expected all items in collection to be non-null.
+ Expected all items in collection to be non-null.
+
+
+
+ Expected all items in collection to be of the specified type.
+ Expected all items in collection to be of the specified type.
+
+
Expected:<{1}>. Actual:<{2}>. {0}
Erwartet:<{1}>. Tatsächlich:<{2}>. {0}
diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.es.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.es.xlf
index 0e8a5863fd..e82d1f0364 100644
--- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.es.xlf
+++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.es.xlf
@@ -17,6 +17,21 @@
Se encontró un elemento duplicado:<{1}>. {0}
+
+ Expected all items in collection to be distinct.
+ Expected all items in collection to be distinct.
+
+
+
+ Expected all items in collection to be non-null.
+ Expected all items in collection to be non-null.
+
+
+
+ Expected all items in collection to be of the specified type.
+ Expected all items in collection to be of the specified type.
+
+
Expected:<{1}>. Actual:<{2}>. {0}
Se esperaba <{1}>, pero es <{2}>. {0}
diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.fr.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.fr.xlf
index f2393eb9c6..1338340da3 100644
--- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.fr.xlf
+++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.fr.xlf
@@ -17,6 +17,21 @@
Un élément dupliqué a été trouvé : <{1}>. {0}
+
+ Expected all items in collection to be distinct.
+ Expected all items in collection to be distinct.
+
+
+
+ Expected all items in collection to be non-null.
+ Expected all items in collection to be non-null.
+
+
+
+ Expected all items in collection to be of the specified type.
+ Expected all items in collection to be of the specified type.
+
+
Expected:<{1}>. Actual:<{2}>. {0}
Attendu : <{1}>, Réel : <{2}>. {0}
diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.it.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.it.xlf
index 561a73665e..9f26017606 100644
--- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.it.xlf
+++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.it.xlf
@@ -17,6 +17,21 @@
Rilevato elemento duplicato:<{1}>. {0}
+
+ Expected all items in collection to be distinct.
+ Expected all items in collection to be distinct.
+
+
+
+ Expected all items in collection to be non-null.
+ Expected all items in collection to be non-null.
+
+
+
+ Expected all items in collection to be of the specified type.
+ Expected all items in collection to be of the specified type.
+
+
Expected:<{1}>. Actual:<{2}>. {0}
Previsto:<{1}>. Effettivo:<{2}>. {0}
diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ja.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ja.xlf
index 16e474fec9..430fa3d6fb 100644
--- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ja.xlf
+++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ja.xlf
@@ -17,6 +17,21 @@
重複する項目が見つかりました:<{1}>。{0}
+
+ Expected all items in collection to be distinct.
+ Expected all items in collection to be distinct.
+
+
+
+ Expected all items in collection to be non-null.
+ Expected all items in collection to be non-null.
+
+
+
+ Expected all items in collection to be of the specified type.
+ Expected all items in collection to be of the specified type.
+
+
Expected:<{1}>. Actual:<{2}>. {0}
<{1}> が必要ですが、<{2}> が指定されました。{0}
diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ko.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ko.xlf
index af329e6b4f..c9f2dd212a 100644
--- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ko.xlf
+++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ko.xlf
@@ -17,6 +17,21 @@
중복된 항목이 있습니다. <{1}>. {0}
+
+ Expected all items in collection to be distinct.
+ Expected all items in collection to be distinct.
+
+
+
+ Expected all items in collection to be non-null.
+ Expected all items in collection to be non-null.
+
+
+
+ Expected all items in collection to be of the specified type.
+ Expected all items in collection to be of the specified type.
+
+
Expected:<{1}>. Actual:<{2}>. {0}
예상 값: <{1}>. 실제 값: <{2}>. {0}
diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.pl.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.pl.xlf
index 2400f343a5..c9b1425b60 100644
--- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.pl.xlf
+++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.pl.xlf
@@ -17,6 +17,21 @@
Znaleziono duplikat:<{1}>. {0}
+
+ Expected all items in collection to be distinct.
+ Expected all items in collection to be distinct.
+
+
+
+ Expected all items in collection to be non-null.
+ Expected all items in collection to be non-null.
+
+
+
+ Expected all items in collection to be of the specified type.
+ Expected all items in collection to be of the specified type.
+
+
Expected:<{1}>. Actual:<{2}>. {0}
Oczekiwana:<{1}>. Rzeczywista:<{2}>. {0}
diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.pt-BR.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.pt-BR.xlf
index f154240002..dd34fd89e8 100644
--- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.pt-BR.xlf
+++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.pt-BR.xlf
@@ -17,6 +17,21 @@
Item duplicado encontrado:<{1}>. {0}
+
+ Expected all items in collection to be distinct.
+ Expected all items in collection to be distinct.
+
+
+
+ Expected all items in collection to be non-null.
+ Expected all items in collection to be non-null.
+
+
+
+ Expected all items in collection to be of the specified type.
+ Expected all items in collection to be of the specified type.
+
+
Expected:<{1}>. Actual:<{2}>. {0}
Esperado:<{1}>. Real:<{2}>. {0}
diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ru.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ru.xlf
index 723aab1646..4fa3d74ff0 100644
--- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ru.xlf
+++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ru.xlf
@@ -17,6 +17,21 @@
Обнаружен совпадающий элемент: <{1}>. {0}
+
+ Expected all items in collection to be distinct.
+ Expected all items in collection to be distinct.
+
+
+
+ Expected all items in collection to be non-null.
+ Expected all items in collection to be non-null.
+
+
+
+ Expected all items in collection to be of the specified type.
+ Expected all items in collection to be of the specified type.
+
+
Expected:<{1}>. Actual:<{2}>. {0}
Ожидается: <{1}>. Фактически: <{2}>. {0}
diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.tr.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.tr.xlf
index 207520dbec..17a10138eb 100644
--- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.tr.xlf
+++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.tr.xlf
@@ -17,6 +17,21 @@
Yinelenen öğe bulundu:<{1}>. {0}
+
+ Expected all items in collection to be distinct.
+ Expected all items in collection to be distinct.
+
+
+
+ Expected all items in collection to be non-null.
+ Expected all items in collection to be non-null.
+
+
+
+ Expected all items in collection to be of the specified type.
+ Expected all items in collection to be of the specified type.
+
+
Expected:<{1}>. Actual:<{2}>. {0}
Beklenen:<{1}>. Gerçek:<{2}>. {0}
diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.zh-Hans.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.zh-Hans.xlf
index 89894d5145..667f1f5ba8 100644
--- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.zh-Hans.xlf
+++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.zh-Hans.xlf
@@ -17,6 +17,21 @@
找到了重复项: <{1}>。{0}
+
+ Expected all items in collection to be distinct.
+ Expected all items in collection to be distinct.
+
+
+
+ Expected all items in collection to be non-null.
+ Expected all items in collection to be non-null.
+
+
+
+ Expected all items in collection to be of the specified type.
+ Expected all items in collection to be of the specified type.
+
+
Expected:<{1}>. Actual:<{2}>. {0}
应为: <{1}>,实际为: <{2}>。{0}
diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.zh-Hant.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.zh-Hant.xlf
index ae7770aaeb..3aeee9b380 100644
--- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.zh-Hant.xlf
+++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.zh-Hant.xlf
@@ -17,6 +17,21 @@
找到重複的項目: <{1}>。{0}
+
+ Expected all items in collection to be distinct.
+ Expected all items in collection to be distinct.
+
+
+
+ Expected all items in collection to be non-null.
+ Expected all items in collection to be non-null.
+
+
+
+ Expected all items in collection to be of the specified type.
+ Expected all items in collection to be of the specified type.
+
+
Expected:<{1}>. Actual:<{2}>. {0}
預期: <{1}>。實際: <{2}>。{0}
diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreAll.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreAll.cs
new file mode 100644
index 0000000000..6ae04d2944
--- /dev/null
+++ b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreAll.cs
@@ -0,0 +1,665 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Collections;
+
+using AwesomeAssertions;
+
+using TestFramework.ForTestingMSTest;
+
+namespace Microsoft.VisualStudio.TestPlatform.TestFramework.UnitTests;
+
+public partial class AssertTests : TestContainer
+{
+ #region AreAllNotNull
+
+ public void AreAllNotNull_Generic_NoNulls_ShouldPass()
+ => Assert.AreAllNotNull(new[] { "a", "b", "c" });
+
+ public void AreAllNotNull_Generic_Empty_ShouldPass()
+ => Assert.AreAllNotNull(Array.Empty());
+
+ public void AreAllNotNull_Generic_ValueTypes_ShouldPass()
+ => Assert.AreAllNotNull(new[] { 1, 2, 3 });
+
+ public void AreAllNotNull_Generic_HasNull_ShouldFail()
+ {
+ Action action = () => Assert.AreAllNotNull(new[] { "a", null, "b", null });
+ action.Should().Throw()
+ .WithMessage(
+ """
+ Assertion failed. Expected all items in collection to be non-null.
+
+ null indices: [1, 3]
+ collection: ["a", null, "b", null]
+
+ Assert.AreAllNotNull(new[] { "a", null, "b", null })
+ """);
+ }
+
+ public void AreAllNotNull_Generic_WithUserMessage_ShouldFail()
+ {
+ Action action = () => Assert.AreAllNotNull(new[] { "a", null }, "User-provided message");
+ action.Should().Throw()
+ .WithMessage(
+ """
+ Assertion failed. Expected all items in collection to be non-null.
+ User-provided message
+
+ null indices: [1]
+ collection: ["a", null]
+
+ Assert.AreAllNotNull(new[] { "a", null })
+ """);
+ }
+
+ public void AreAllNotNull_Generic_NullCollection_ShouldFail()
+ {
+ Action action = () => Assert.AreAllNotNull((IEnumerable?)null);
+ action.Should().Throw()
+ .WithMessage("Assert.AreAllNotNull failed. The parameter 'collection' is invalid. The value cannot be null.");
+ }
+
+ public void AreAllNotNull_NonGeneric_NoNulls_ShouldPass()
+ {
+ ArrayList list = ["a", "b", "c"];
+ Assert.AreAllNotNull(list);
+ }
+
+ public void AreAllNotNull_NonGeneric_HasNull_ShouldFail()
+ {
+ ArrayList list = ["a", null, "b"];
+ Action action = () => Assert.AreAllNotNull(list);
+ action.Should().Throw()
+ .WithMessage(
+ """
+ Assertion failed. Expected all items in collection to be non-null.
+
+ null indices: [1]
+ collection: ["a", null, "b"]
+
+ Assert.AreAllNotNull(list)
+ """);
+ }
+
+ public void AreAllNotNull_NonGeneric_NullCollection_ShouldFail()
+ {
+ Action action = () => Assert.AreAllNotNull(null);
+ action.Should().Throw()
+ .WithMessage("Assert.AreAllNotNull failed. The parameter 'collection' is invalid. The value cannot be null.");
+ }
+
+ public void AreAllNotNull_NonGeneric_WithUserMessage_ShouldFail()
+ {
+ ArrayList list = ["a", null];
+ Action action = () => Assert.AreAllNotNull(list, "User-provided message");
+ action.Should().Throw()
+ .WithMessage(
+ """
+ Assertion failed. Expected all items in collection to be non-null.
+ User-provided message
+
+ null indices: [1]
+ collection: ["a", null]
+
+ Assert.AreAllNotNull(list)
+ """);
+ }
+
+ public void AreAllNotNull_Generic_NullableValueType_HasNull_ShouldFail()
+ {
+ Action action = () => Assert.AreAllNotNull(new int?[] { 1, null, 3 });
+ action.Should().Throw()
+ .WithMessage(
+ """
+ Assertion failed. Expected all items in collection to be non-null.
+
+ null indices: [1]
+ collection: [1, null, 3]
+
+ Assert.AreAllNotNull(new int?[] { 1, null, 3 })
+ """);
+ }
+
+ public void AreAllNotNull_Generic_LazyEnumerable_HasNull_ShouldFail()
+ {
+ static IEnumerable Lazy()
+ {
+ yield return "a";
+ yield return null;
+ yield return "b";
+ }
+
+ Action action = () => Assert.AreAllNotNull(Lazy());
+ action.Should().Throw()
+ .WithMessage("*null indices: [1]*collection:*");
+ }
+
+ #endregion // AreAllNotNull
+
+ #region AreAllDistinct
+
+ public void AreAllDistinct_Generic_AllDistinct_ShouldPass()
+ => Assert.AreAllDistinct(new[] { 1, 2, 3 });
+
+ public void AreAllDistinct_Generic_Empty_ShouldPass()
+ => Assert.AreAllDistinct(Array.Empty());
+
+ public void AreAllDistinct_Generic_SingleNull_ShouldPass()
+ => Assert.AreAllDistinct(new string?[] { "a", null, "b" });
+
+ public void AreAllDistinct_Generic_HasDuplicate_ShouldFail()
+ {
+ Action action = () => Assert.AreAllDistinct(new[] { 1, 2, 3, 4, 3 });
+ action.Should().Throw()
+ .WithMessage(
+ """
+ Assertion failed. Expected all items in collection to be distinct.
+
+ duplicates: [3]
+ collection: [1, 2, 3, 4, 3]
+
+ Assert.AreAllDistinct(new[] { 1, 2, 3, 4, 3 })
+ """);
+ }
+
+ public void AreAllDistinct_Generic_HasMultipleDuplicates_ShouldFail()
+ {
+ Action action = () => Assert.AreAllDistinct(new[] { 1, 2, 3, 2, 1, 1 });
+ action.Should().Throw()
+ .WithMessage(
+ """
+ Assertion failed. Expected all items in collection to be distinct.
+
+ duplicates: [2, 1]
+ collection: [1, 2, 3, 2, 1, 1]
+
+ Assert.AreAllDistinct(new[] { 1, 2, 3, 2, 1, 1 })
+ """);
+ }
+
+ public void AreAllDistinct_Generic_NullDuplicate_ShouldFail()
+ {
+ Action action = () => Assert.AreAllDistinct(new string?[] { "a", null, "b", null });
+ action.Should().Throw()
+ .WithMessage(
+ """
+ Assertion failed. Expected all items in collection to be distinct.
+
+ duplicates: [null]
+ collection: ["a", null, "b", null]
+
+ Assert.AreAllDistinct(new string?[] { "a", null, "b", null })
+ """);
+ }
+
+ public void AreAllDistinct_Generic_WithUserMessage_ShouldFail()
+ {
+ Action action = () => Assert.AreAllDistinct(new[] { 1, 1 }, "User-provided message");
+ action.Should().Throw()
+ .WithMessage(
+ """
+ Assertion failed. Expected all items in collection to be distinct.
+ User-provided message
+
+ duplicates: [1]
+ collection: [1, 1]
+
+ Assert.AreAllDistinct(new[] { 1, 1 })
+ """);
+ }
+
+ public void AreAllDistinct_Generic_WithComparer_AllDistinct_ShouldPass()
+ => Assert.AreAllDistinct(new[] { "a", "B", "c" }, new CaseInsensitiveStringComparer());
+
+ public void AreAllDistinct_Generic_WithComparer_HasDuplicate_ShouldFail()
+ {
+ Action action = () => Assert.AreAllDistinct(new[] { "A", "B", "a" }, new CaseInsensitiveStringComparer());
+ action.Should().Throw()
+ .WithMessage(
+ """
+ Assertion failed. Expected all items in collection to be distinct.
+
+ duplicates: ["a"]
+ collection: ["A", "B", "a"]
+ comparer: CaseInsensitiveStringComparer
+
+ Assert.AreAllDistinct(new[] { "A", "B", "a" }, )
+ """);
+ }
+
+ public void AreAllDistinct_Generic_NullCollection_ShouldFail()
+ {
+ Action action = () => Assert.AreAllDistinct((IEnumerable?)null);
+ action.Should().Throw()
+ .WithMessage("Assert.AreAllDistinct failed. The parameter 'collection' is invalid. The value cannot be null.");
+ }
+
+ public void AreAllDistinct_Generic_NullComparer_ShouldFail()
+ {
+ Action action = () => Assert.AreAllDistinct(new[] { 1, 2 }, (IEqualityComparer?)null);
+ action.Should().Throw()
+ .WithMessage("Assert.AreAllDistinct failed. The parameter 'comparer' is invalid. The value cannot be null.");
+ }
+
+ public void AreAllDistinct_NonGeneric_AllDistinct_ShouldPass()
+ {
+ ArrayList list = [1, "a", 3.5];
+ Assert.AreAllDistinct(list);
+ }
+
+ public void AreAllDistinct_NonGeneric_HasDuplicate_ShouldFail()
+ {
+ ArrayList list = [1, 2, 3, 2];
+ Action action = () => Assert.AreAllDistinct(list);
+ action.Should().Throw()
+ .WithMessage(
+ """
+ Assertion failed. Expected all items in collection to be distinct.
+
+ duplicates: [2]
+ collection: [1, 2, 3, 2]
+
+ Assert.AreAllDistinct(list)
+ """);
+ }
+
+ public void AreAllDistinct_NonGeneric_WithComparer_HasDuplicate_ShouldFail()
+ {
+ ArrayList list = ["A", "B", "a"];
+ Action action = () => Assert.AreAllDistinct(list, new CaseInsensitiveStringComparer());
+ action.Should().Throw()
+ .WithMessage(
+ """
+ Assertion failed. Expected all items in collection to be distinct.
+
+ duplicates: ["a"]
+ collection: ["A", "B", "a"]
+ comparer: CaseInsensitiveStringComparer
+
+ Assert.AreAllDistinct(list, )
+ """);
+ }
+
+ public void AreAllDistinct_NonGeneric_NullCollection_ShouldFail()
+ {
+ Action action = () => Assert.AreAllDistinct(null);
+ action.Should().Throw()
+ .WithMessage("Assert.AreAllDistinct failed. The parameter 'collection' is invalid. The value cannot be null.");
+ }
+
+ public void AreAllDistinct_NonGeneric_NullComparer_ShouldFail()
+ {
+ ArrayList list = [1, 2];
+ Action action = () => Assert.AreAllDistinct(list, (IEqualityComparer?)null);
+ action.Should().Throw()
+ .WithMessage("Assert.AreAllDistinct failed. The parameter 'comparer' is invalid. The value cannot be null.");
+ }
+
+ public void AreAllDistinct_NonGeneric_WithUserMessage_ShouldFail()
+ {
+ ArrayList list = [1, 1];
+ Action action = () => Assert.AreAllDistinct(list, "User-provided message");
+ action.Should().Throw()
+ .WithMessage(
+ """
+ Assertion failed. Expected all items in collection to be distinct.
+ User-provided message
+
+ duplicates: [1]
+ collection: [1, 1]
+
+ Assert.AreAllDistinct(list)
+ """);
+ }
+
+ public void AreAllDistinct_NonGeneric_SingleNull_ShouldPass()
+ {
+ ArrayList list = ["a", null, "b"];
+ Assert.AreAllDistinct(list);
+ }
+
+ public void AreAllDistinct_NonGeneric_NullDuplicate_ShouldFail()
+ {
+ ArrayList list = ["a", null, null];
+ Action action = () => Assert.AreAllDistinct(list);
+ action.Should().Throw()
+ .WithMessage(
+ """
+ Assertion failed. Expected all items in collection to be distinct.
+
+ duplicates: [null]
+ collection: ["a", null, null]
+
+ Assert.AreAllDistinct(list)
+ """);
+ }
+
+ public void AreAllDistinct_Generic_ManyNulls_ReportsNullOnce_ShouldFail()
+ {
+ Action action = () => Assert.AreAllDistinct(new string?[] { null, "a", null, "b", null });
+ action.Should().Throw()
+ .WithMessage(
+ """
+ Assertion failed. Expected all items in collection to be distinct.
+
+ duplicates: [null]
+ collection: [null, "a", null, "b", null]
+
+ Assert.AreAllDistinct(new string?[] { null, "a", null, "b", null })
+ """);
+ }
+
+ public void AreAllDistinct_Generic_ManyDuplicatesOfSameValue_ReportsValueOnce_ShouldFail()
+ {
+ Action action = () => Assert.AreAllDistinct(new[] { 5, 5, 5, 5 });
+ action.Should().Throw()
+ .WithMessage(
+ """
+ Assertion failed. Expected all items in collection to be distinct.
+
+ duplicates: [5]
+ collection: [5, 5, 5, 5]
+
+ Assert.AreAllDistinct(new[] { 5, 5, 5, 5 })
+ """);
+ }
+
+ // Pins the current behavior: null elements are short-circuited and never passed to the user-provided comparer.
+ // A comparer that treats null as equal to "" is therefore not consulted, and [null, ""] is considered distinct.
+ public void AreAllDistinct_Generic_ComparerTreatsNullAsEmpty_NullAndEmpty_ShouldPass()
+ => Assert.AreAllDistinct(new string?[] { null, string.Empty }, new NullEqualsEmptyStringComparer());
+
+ // Pins the current behavior: a comparer that throws on null is never invoked with a null argument because
+ // null elements are short-circuited before the comparer is consulted.
+ public void AreAllDistinct_Generic_ComparerThrowsOnNull_WithNulls_ShouldNotInvokeComparerForNull()
+ => Assert.AreAllDistinct(new string?[] { null, "a", "b" }, new ThrowOnNullStringComparer());
+
+ private sealed class CaseInsensitiveStringComparer : IEqualityComparer, IEqualityComparer
+ {
+ public bool Equals(string? x, string? y) => StringComparer.OrdinalIgnoreCase.Equals(x, y);
+
+ public int GetHashCode(string? obj) => obj is null ? 0 : StringComparer.OrdinalIgnoreCase.GetHashCode(obj);
+
+ bool IEqualityComparer.Equals(object? x, object? y) => Equals(x as string, y as string);
+
+ int IEqualityComparer.GetHashCode(object obj) => obj is string value ? GetHashCode(value) : 0;
+ }
+
+ private sealed class NullEqualsEmptyStringComparer : IEqualityComparer
+ {
+ public bool Equals(string? x, string? y) => (x ?? string.Empty) == (y ?? string.Empty);
+
+ public int GetHashCode(string? obj) => (obj ?? string.Empty).GetHashCode();
+ }
+
+ private sealed class ThrowOnNullStringComparer : IEqualityComparer
+ {
+ public bool Equals(string? x, string? y)
+ => x is null || y is null
+ ? throw new InvalidOperationException("Comparer should not be invoked with null.")
+ : x == y;
+
+ public int GetHashCode(string? obj)
+ => obj is null
+ ? throw new InvalidOperationException("Comparer should not be invoked with null.")
+ : obj.GetHashCode();
+ }
+
+ #endregion // AreAllDistinct
+
+ #region AreAllOfType
+
+ public void AreAllOfType_Generic_AllMatch_ShouldPass()
+ => Assert.AreAllOfType(new object[] { "a", "b", "c" });
+
+ public void AreAllOfType_Generic_DerivedTypes_ShouldPass()
+ => Assert.AreAllOfType(new object[] { new DerivedAllItemsA(), new DerivedAllItemsB() });
+
+ public void AreAllOfType_Generic_BaseTypeAcceptsDerived_ShouldPass()
+ => Assert.AreAllOfType(new object[] { 1, "two", 3.0 });
+
+ public void AreAllOfType_Generic_Empty_ShouldPass()
+ => Assert.AreAllOfType(Array.Empty());
+
+ public void AreAllOfType_Generic_NullElement_ShouldFail()
+ {
+ Action action = () => Assert.AreAllOfType(new object?[] { "a", null, "b" });
+ action.Should().Throw()
+ .WithMessage(
+ """
+ Assertion failed. Expected all items in collection to be of the specified type.
+
+ expected type: System.String (or derived)
+ mismatches: [index 1: ]
+ collection: ["a", null, "b"]
+
+ Assert.AreAllOfType(new object?[] { "a", null, "b" })
+ """);
+ }
+
+ public void AreAllOfType_Generic_HasMismatch_ShouldFail()
+ {
+ Action action = () => Assert.AreAllOfType(new object[] { "a", 42, "b", true });
+ action.Should().Throw()
+ .WithMessage(
+ """
+ Assertion failed. Expected all items in collection to be of the specified type.
+
+ expected type: System.String (or derived)
+ mismatches: [index 1: System.Int32, index 3: System.Boolean]
+ collection: ["a", 42, "b", true]
+
+ Assert.AreAllOfType(new object[] { "a", 42, "b", true })
+ """);
+ }
+
+ public void AreAllOfType_Generic_WithUserMessage_ShouldFail()
+ {
+ Action action = () => Assert.AreAllOfType(new object[] { 1 }, "User-provided message");
+ action.Should().Throw()
+ .WithMessage(
+ """
+ Assertion failed. Expected all items in collection to be of the specified type.
+ User-provided message
+
+ expected type: System.String (or derived)
+ mismatches: [index 0: System.Int32]
+ collection: [1]
+
+ Assert.AreAllOfType(new object[] { 1 })
+ """);
+ }
+
+ public void AreAllOfType_Generic_HasMismatch_ShouldPopulateExpectedAndActualPayload()
+ {
+ try
+ {
+ Assert.AreAllOfType(new object[] { 1 });
+ }
+ catch (AssertFailedException ex)
+ {
+ ex.ExpectedText.Should().Be("System.String (or derived)");
+ ex.ActualText.Should().Be("[1]");
+ ex.Data["assert.expected"].Should().Be("System.String (or derived)");
+ ex.Data["assert.actual"].Should().Be("[1]");
+ return;
+ }
+
+ throw new AssertFailedException("Expected AssertFailedException was not thrown.");
+ }
+
+ public void AreAllOfType_Generic_NullCollection_ShouldFail()
+ {
+ Action action = () => Assert.AreAllOfType(null);
+ action.Should().Throw()
+ .WithMessage("Assert.AreAllOfType failed. The parameter 'collection' is invalid. The value cannot be null.");
+ }
+
+ public void AreAllOfType_NonGeneric_AllMatch_ShouldPass()
+ {
+ ArrayList list = ["a", "b"];
+ Assert.AreAllOfType(typeof(string), list);
+ }
+
+ public void AreAllOfType_NonGeneric_HasMismatch_ShouldFail()
+ {
+ ArrayList list = ["a", 42];
+ Action action = () => Assert.AreAllOfType(typeof(string), list);
+ action.Should().Throw()
+ .WithMessage(
+ """
+ Assertion failed. Expected all items in collection to be of the specified type.
+
+ expected type: System.String (or derived)
+ mismatches: [index 1: System.Int32]
+ collection: ["a", 42]
+
+ Assert.AreAllOfType(typeof(string), list)
+ """);
+ }
+
+ public void AreAllOfType_NonGeneric_NullCollection_ShouldFail()
+ {
+ Action action = () => Assert.AreAllOfType(typeof(string), null);
+ action.Should().Throw()
+ .WithMessage("Assert.AreAllOfType failed. The parameter 'collection' is invalid. The value cannot be null.");
+ }
+
+ public void AreAllOfType_NonGeneric_NullExpectedType_ShouldFail()
+ {
+ ArrayList list = ["a"];
+ Action action = () => Assert.AreAllOfType(null, list);
+ action.Should().Throw()
+ .WithMessage("Assert.AreAllOfType failed. The parameter 'expectedType' is invalid. The value cannot be null.");
+ }
+
+ public void AreAllOfType_NonGeneric_WithUserMessage_ShouldFail()
+ {
+ ArrayList list = [1];
+ Action action = () => Assert.AreAllOfType(typeof(string), list, "User-provided message");
+ action.Should().Throw()
+ .WithMessage(
+ """
+ Assertion failed. Expected all items in collection to be of the specified type.
+ User-provided message
+
+ expected type: System.String (or derived)
+ mismatches: [index 0: System.Int32]
+ collection: [1]
+
+ Assert.AreAllOfType(typeof(string), list)
+ """);
+ }
+
+ public void AreAllOfType_NonGeneric_DerivedTypes_ShouldPass()
+ {
+ ArrayList list = [new DerivedAllItemsA(), new DerivedAllItemsB()];
+ Assert.AreAllOfType(typeof(DerivedAllItemsBase), list);
+ }
+
+ public void AreAllOfType_NonGeneric_NullElement_ShouldFail()
+ {
+ ArrayList list = ["a", null, "b"];
+ Action action = () => Assert.AreAllOfType(typeof(string), list);
+ action.Should().Throw()
+ .WithMessage(
+ """
+ Assertion failed. Expected all items in collection to be of the specified type.
+
+ expected type: System.String (or derived)
+ mismatches: [index 1: ]
+ collection: ["a", null, "b"]
+
+ Assert.AreAllOfType(typeof(string), list)
+ """);
+ }
+
+ public void AreAllOfType_NonGeneric_Empty_ShouldPass()
+ {
+ ArrayList list = [];
+ Assert.AreAllOfType(typeof(string), list);
+ }
+
+ public void AreAllOfType_Generic_BoxedValueType_AllMatch_ShouldPass()
+ => Assert.AreAllOfType(new object[] { 1, 2, 3 });
+
+ public void AreAllOfType_Generic_BoxedValueType_HasMismatch_ShouldFail()
+ {
+ Action action = () => Assert.AreAllOfType(new object[] { 1, "x" });
+ action.Should().Throw()
+ .WithMessage(
+ """
+ Assertion failed. Expected all items in collection to be of the specified type.
+
+ expected type: System.Int32 (or derived)
+ mismatches: [index 1: System.String]
+ collection: [1, "x"]
+
+ Assert.AreAllOfType(new object[] { 1, "x" })
+ """);
+ }
+
+ // Pins the current behavior of `AreAllOfType` for nullable value types: a `null` element fails because
+ // `typeof(int?).IsInstanceOfType(null)` returns false. Boxed non-null nullable values are accepted because
+ // boxing erases the `Nullable` wrapper.
+ public void AreAllOfType_Generic_NullableValueType_NullElement_ShouldFail()
+ {
+ Action action = () => Assert.AreAllOfType(new object?[] { 1, null, 3 });
+ action.Should().Throw()
+ .WithMessage("*mismatches: [index 1: ]*");
+ }
+
+ public void AreAllOfType_Generic_StructMismatch_ShouldFail()
+ {
+ Action action = () => Assert.AreAllOfType(new object[] { DateTime.UtcNow, "x" });
+ action.Should().Throw()
+ .WithMessage(
+ """
+ *expected type: System.DateTime (or derived)
+ mismatches: [index 1: System.String]*
+ """);
+ }
+
+ public void AreAllOfType_NonGeneric_BoxedValueType_AllMatch_ShouldPass()
+ {
+ ArrayList list = [1, 2, 3];
+ Assert.AreAllOfType(typeof(int), list);
+ }
+
+ public void AreAllOfType_NonGeneric_BoxedValueType_HasMismatch_ShouldFail()
+ {
+ ArrayList list = [1, "x"];
+ Action action = () => Assert.AreAllOfType(typeof(int), list);
+ action.Should().Throw()
+ .WithMessage(
+ """
+ Assertion failed. Expected all items in collection to be of the specified type.
+
+ expected type: System.Int32 (or derived)
+ mismatches: [index 1: System.String]
+ collection: [1, "x"]
+
+ Assert.AreAllOfType(typeof(int), list)
+ """);
+ }
+
+ public void AreAllOfType_Generic_Interface_AllImplement_ShouldPass()
+ => Assert.AreAllOfType(new object[] { new System.IO.MemoryStream(), new System.IO.MemoryStream() });
+
+ public void AreAllOfType_NonGeneric_Interface_HasMismatch_ShouldFail()
+ {
+ ArrayList list = [new System.IO.MemoryStream(), "not-disposable-string"];
+ Action action = () => Assert.AreAllOfType(typeof(IDisposable), list);
+ action.Should().Throw()
+ .WithMessage("*expected type: System.IDisposable (or derived)*mismatches: [index 1: System.String]*");
+ }
+
+ private class DerivedAllItemsBase;
+
+ private sealed class DerivedAllItemsA : DerivedAllItemsBase;
+
+ private sealed class DerivedAllItemsB : DerivedAllItemsBase;
+
+ #endregion // AreAllOfType
+}