Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ When making change to resource files, you MUST:

## Public API guidelines

- Treat adding an overload as a potential source-breaking change, even when it is binary-compatible. Existing calls can become ambiguous when an argument converts to multiple parameter types, especially across `Span<T>`, `ReadOnlySpan<T>`, arrays, generic interfaces such as `IEnumerable<T>`, and overloads with optional parameters.
- Before adding or changing overloads, enumerate representative existing call shapes and compare all applicable implicit conversions and generic type-inference paths.
- For `Assert` overload changes, update the manually maintained implicit consumer call shapes in [`AssertSourceCompatibilityTests.cs`](../test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssertSourceCompatibilityTests.cs). The test compiles them against the packed `MSTest.TestFramework` using C# 12 and automatically requires every public `Assert` method family to have at least one representative scenario.
- Add equivalent package-consuming compilation coverage for overload changes in other public API types, using the oldest relevant default C# language version and target framework. Repository projects use `LangVersion=preview`, so an ordinary in-repo unit test does not detect overload-resolution regressions that only affect older compilers.
- During review, do not treat successful compilation under the repository's language version as sufficient evidence of source compatibility.
- Public API for MSTest and Microsoft.Testing.Platform MUST NOT use `init` accessors.
- Exception: Existing APIs in Microsoft.Testing.Platform, because changing them right now would be a breaking change. However, we MUST NOT introduce **new** APIs using `init` accessors.
- IMPORTANT: Make sure to apply this rule strictly both during PR review and when working on code changes.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,37 @@ private static void ReportAssertAreAllDistinctFailed<T>(IEnumerable<T> collectio

#region AreAllDistinct span/memory

/// <summary>
/// Tests whether all elements in the specified array are distinct using the specified equality comparer
/// and throws an exception if any two elements are equal.
/// </summary>
/// <typeparam name="T">The type of the array elements.</typeparam>
/// <param name="collection">The array whose elements must all be distinct.</param>
/// <param name="comparer">The equality comparer to use when comparing elements.</param>
/// <param name="message">The message to include in the exception when two equal elements are found.</param>
/// <param name="collectionExpression">The syntactic expression of collection as given by the compiler via caller argument expression.</param>
/// <exception cref="AssertFailedException">Thrown if any two elements in <paramref name="collection"/> are equal.</exception>
public static void AreAllDistinct<T>([NotNull] T[]? collection, [NotNull] IEqualityComparer<T>? comparer, string? message = "", [CallerArgumentExpression(nameof(collection))] string collectionExpression = "")
=> AreAllDistinct((IEnumerable<T>?)collection, comparer, message, collectionExpression);

/// <summary>
/// Tests whether all elements in the specified collection are distinct using the specified equality comparer
/// and throws an exception if any two elements are equal.
/// </summary>
/// <typeparam name="T">The type of the collection elements.</typeparam>
/// <typeparam name="TCollection">The concrete type of the collection.</typeparam>
/// <param name="collection">The collection whose elements must all be distinct.</param>
/// <param name="comparer">The equality comparer to use when comparing elements.</param>
/// <param name="message">The message to include in the exception when two equal elements are found.</param>
/// <param name="collectionExpression">
/// The syntactic expression of collection as given by the compiler via caller argument expression.
/// Users shouldn't pass a value for this parameter.
/// </param>
/// <exception cref="AssertFailedException">Thrown if any two elements in <paramref name="collection"/> are equal.</exception>
public static void AreAllDistinct<T, TCollection>([NotNull] TCollection? collection, [NotNull] IEqualityComparer<T>? comparer, string? message = "", [CallerArgumentExpression(nameof(collection))] string collectionExpression = "")
where TCollection : IEnumerable<T>
=> AreAllDistinct((IEnumerable<T>?)collection, comparer, message, collectionExpression);

/// <summary>
/// Tests whether all items in the specified span are distinct (no two elements are equal).
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// 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;
Expand Down Expand Up @@ -139,6 +139,78 @@ public static void AreNotSequenceEqual(IEnumerable? notExpected, IEnumerable? ac

#region AreNotSequenceEqual span/memory

/// <summary>
/// Tests whether two arrays differ in the same order using the specified equality comparer.
/// </summary>
/// <typeparam name="T">The type of the array elements.</typeparam>
/// <param name="notExpected">The array not expected to equal <paramref name="actual"/>.</param>
/// <param name="actual">The array produced by the code under test.</param>
/// <param name="comparer">The equality comparer to use when comparing elements, or <see langword="null"/> to use the default comparer.</param>
/// <param name="message">The message to include in the exception when the arrays are equal.</param>
/// <param name="notExpectedExpression">The syntactic expression of notExpected as given by the compiler via caller argument expression.</param>
/// <param name="actualExpression">The syntactic expression of actual as given by the compiler via caller argument expression.</param>
public static void AreNotSequenceEqual<T>(T[]? notExpected, T[]? actual, IEqualityComparer<T>? comparer, string? message = "", [CallerArgumentExpression(nameof(notExpected))] string notExpectedExpression = "", [CallerArgumentExpression(nameof(actual))] string actualExpression = "")
=> AreNotSequenceEqual((IEnumerable<T>?)notExpected, (IEnumerable<T>?)actual, comparer, message, notExpectedExpression, actualExpression);

/// <summary>
/// Tests whether two arrays differ using the specified equality comparer and order semantics.
/// </summary>
/// <typeparam name="T">The type of the array elements.</typeparam>
/// <param name="notExpected">The array not expected to equal <paramref name="actual"/>.</param>
/// <param name="actual">The array produced by the code under test.</param>
/// <param name="comparer">The equality comparer to use when comparing elements, or <see langword="null"/> to use the default comparer.</param>
/// <param name="order">Specifies whether elements must appear in the same order or in any order.</param>
/// <param name="message">The message to include in the exception when the arrays are equal.</param>
/// <param name="notExpectedExpression">The syntactic expression of notExpected as given by the compiler via caller argument expression.</param>
/// <param name="actualExpression">The syntactic expression of actual as given by the compiler via caller argument expression.</param>
public static void AreNotSequenceEqual<T>(T[]? notExpected, T[]? actual, IEqualityComparer<T>? comparer, SequenceOrder order, string? message = "", [CallerArgumentExpression(nameof(notExpected))] string notExpectedExpression = "", [CallerArgumentExpression(nameof(actual))] string actualExpression = "")
=> AreNotSequenceEqual((IEnumerable<T>?)notExpected, (IEnumerable<T>?)actual, comparer, order, message, notExpectedExpression, actualExpression);

/// <summary>
/// Tests whether two collections differ in the same order using the specified equality comparer
/// and throws an exception if they are equal.
/// </summary>
/// <typeparam name="T">The type of the collection elements.</typeparam>
/// <typeparam name="TNotExpectedCollection">The concrete type of the not-expected collection.</typeparam>
/// <typeparam name="TActualCollection">The concrete type of the actual collection.</typeparam>
/// <param name="notExpected">The collection not expected to equal <paramref name="actual"/>.</param>
/// <param name="actual">The collection produced by the code under test.</param>
/// <param name="comparer">The equality comparer to use when comparing elements, or <see langword="null"/> to use the default comparer.</param>
/// <param name="message">The message to include in the exception when the collections are equal.</param>
/// <param name="notExpectedExpression">The syntactic expression of notExpected as given by the compiler via caller argument expression.</param>
/// <param name="actualExpression">The syntactic expression of actual as given by the compiler via caller argument expression.</param>
public static void AreNotSequenceEqual<T, TNotExpectedCollection, TActualCollection>(TNotExpectedCollection? notExpected, TActualCollection? actual, IEqualityComparer<T>? comparer, string? message = "", [CallerArgumentExpression(nameof(notExpected))] string notExpectedExpression = "", [CallerArgumentExpression(nameof(actual))] string actualExpression = "")
where TNotExpectedCollection : IEnumerable<T>
where TActualCollection : IEnumerable<T>
{
IEnumerable<T>? notExpectedEnumerable = notExpected;
IEnumerable<T>? actualEnumerable = actual;
AreNotSequenceEqual(notExpectedEnumerable, actualEnumerable, comparer, message, notExpectedExpression, actualExpression);
}

/// <summary>
/// Tests whether two collections differ using the specified equality comparer and order semantics
/// and throws an exception if they are equal.
/// </summary>
/// <typeparam name="T">The type of the collection elements.</typeparam>
/// <typeparam name="TNotExpectedCollection">The concrete type of the not-expected collection.</typeparam>
/// <typeparam name="TActualCollection">The concrete type of the actual collection.</typeparam>
/// <param name="notExpected">The collection not expected to equal <paramref name="actual"/>.</param>
/// <param name="actual">The collection produced by the code under test.</param>
/// <param name="comparer">The equality comparer to use when comparing elements, or <see langword="null"/> to use the default comparer.</param>
/// <param name="order">Specifies whether elements must appear in the same order or in any order.</param>
/// <param name="message">The message to include in the exception when the collections are equal.</param>
/// <param name="notExpectedExpression">The syntactic expression of notExpected as given by the compiler via caller argument expression.</param>
/// <param name="actualExpression">The syntactic expression of actual as given by the compiler via caller argument expression.</param>
public static void AreNotSequenceEqual<T, TNotExpectedCollection, TActualCollection>(TNotExpectedCollection? notExpected, TActualCollection? actual, IEqualityComparer<T>? comparer, SequenceOrder order, string? message = "", [CallerArgumentExpression(nameof(notExpected))] string notExpectedExpression = "", [CallerArgumentExpression(nameof(actual))] string actualExpression = "")
where TNotExpectedCollection : IEnumerable<T>
where TActualCollection : IEnumerable<T>
{
IEnumerable<T>? notExpectedEnumerable = notExpected;
IEnumerable<T>? actualEnumerable = actual;
AreNotSequenceEqual(notExpectedEnumerable, actualEnumerable, comparer, order, message, notExpectedExpression, actualExpression);
}

/// <summary>
/// Tests whether two spans differ in the same order and throws an exception if they do not.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// 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;
Expand Down Expand Up @@ -139,6 +139,78 @@ public static void AreSequenceEqual(IEnumerable? expected, IEnumerable? actual,

#region AreSequenceEqual span/memory

/// <summary>
/// Tests whether two arrays contain equal elements in the same order using the specified equality comparer.
/// </summary>
/// <typeparam name="T">The type of the array elements.</typeparam>
/// <param name="expected">The array expected to be equal to <paramref name="actual"/>.</param>
/// <param name="actual">The array produced by the code under test.</param>
/// <param name="comparer">The equality comparer to use when comparing elements, or <see langword="null"/> to use the default comparer.</param>
/// <param name="message">The message to include in the exception when the arrays are not equal.</param>
/// <param name="expectedExpression">The syntactic expression of expected as given by the compiler via caller argument expression.</param>
/// <param name="actualExpression">The syntactic expression of actual as given by the compiler via caller argument expression.</param>
public static void AreSequenceEqual<T>(T[]? expected, T[]? actual, IEqualityComparer<T>? comparer, string? message = "", [CallerArgumentExpression(nameof(expected))] string expectedExpression = "", [CallerArgumentExpression(nameof(actual))] string actualExpression = "")
=> AreSequenceEqual((IEnumerable<T>?)expected, (IEnumerable<T>?)actual, comparer, message, expectedExpression, actualExpression);

/// <summary>
/// Tests whether two arrays contain equal elements using the specified equality comparer and order semantics.
/// </summary>
/// <typeparam name="T">The type of the array elements.</typeparam>
/// <param name="expected">The array expected to be equal to <paramref name="actual"/>.</param>
/// <param name="actual">The array produced by the code under test.</param>
/// <param name="comparer">The equality comparer to use when comparing elements, or <see langword="null"/> to use the default comparer.</param>
/// <param name="order">Specifies whether elements must appear in the same order or in any order.</param>
/// <param name="message">The message to include in the exception when the arrays are not equal.</param>
/// <param name="expectedExpression">The syntactic expression of expected as given by the compiler via caller argument expression.</param>
/// <param name="actualExpression">The syntactic expression of actual as given by the compiler via caller argument expression.</param>
public static void AreSequenceEqual<T>(T[]? expected, T[]? actual, IEqualityComparer<T>? comparer, SequenceOrder order, string? message = "", [CallerArgumentExpression(nameof(expected))] string expectedExpression = "", [CallerArgumentExpression(nameof(actual))] string actualExpression = "")
=> AreSequenceEqual((IEnumerable<T>?)expected, (IEnumerable<T>?)actual, comparer, order, message, expectedExpression, actualExpression);

/// <summary>
/// Tests whether two collections contain equal elements in the same order using the specified equality comparer
/// and throws an exception if they do not.
/// </summary>
/// <typeparam name="T">The type of the collection elements.</typeparam>
/// <typeparam name="TExpectedCollection">The concrete type of the expected collection.</typeparam>
/// <typeparam name="TActualCollection">The concrete type of the actual collection.</typeparam>
/// <param name="expected">The collection expected to be equal to <paramref name="actual"/>.</param>
/// <param name="actual">The collection produced by the code under test.</param>
/// <param name="comparer">The equality comparer to use when comparing elements, or <see langword="null"/> to use the default comparer.</param>
/// <param name="message">The message to include in the exception when the collections are not equal.</param>
/// <param name="expectedExpression">The syntactic expression of expected as given by the compiler via caller argument expression.</param>
/// <param name="actualExpression">The syntactic expression of actual as given by the compiler via caller argument expression.</param>
public static void AreSequenceEqual<T, TExpectedCollection, TActualCollection>(TExpectedCollection? expected, TActualCollection? actual, IEqualityComparer<T>? comparer, string? message = "", [CallerArgumentExpression(nameof(expected))] string expectedExpression = "", [CallerArgumentExpression(nameof(actual))] string actualExpression = "")
where TExpectedCollection : IEnumerable<T>
where TActualCollection : IEnumerable<T>
{
IEnumerable<T>? expectedEnumerable = expected;
IEnumerable<T>? actualEnumerable = actual;
AreSequenceEqual(expectedEnumerable, actualEnumerable, comparer, message, expectedExpression, actualExpression);
}

/// <summary>
/// Tests whether two collections contain equal elements using the specified equality comparer and order semantics
/// and throws an exception if they do not.
/// </summary>
/// <typeparam name="T">The type of the collection elements.</typeparam>
/// <typeparam name="TExpectedCollection">The concrete type of the expected collection.</typeparam>
/// <typeparam name="TActualCollection">The concrete type of the actual collection.</typeparam>
/// <param name="expected">The collection expected to be equal to <paramref name="actual"/>.</param>
/// <param name="actual">The collection produced by the code under test.</param>
/// <param name="comparer">The equality comparer to use when comparing elements, or <see langword="null"/> to use the default comparer.</param>
/// <param name="order">Specifies whether elements must appear in the same order or in any order.</param>
/// <param name="message">The message to include in the exception when the collections are not equal.</param>
/// <param name="expectedExpression">The syntactic expression of expected as given by the compiler via caller argument expression.</param>
/// <param name="actualExpression">The syntactic expression of actual as given by the compiler via caller argument expression.</param>
public static void AreSequenceEqual<T, TExpectedCollection, TActualCollection>(TExpectedCollection? expected, TActualCollection? actual, IEqualityComparer<T>? comparer, SequenceOrder order, string? message = "", [CallerArgumentExpression(nameof(expected))] string expectedExpression = "", [CallerArgumentExpression(nameof(actual))] string actualExpression = "")
where TExpectedCollection : IEnumerable<T>
where TActualCollection : IEnumerable<T>
{
IEnumerable<T>? expectedEnumerable = expected;
IEnumerable<T>? actualEnumerable = actual;
AreSequenceEqual(expectedEnumerable, actualEnumerable, comparer, order, message, expectedExpression, actualExpression);
}

/// <summary>
/// Tests whether two spans contain equal elements in the same order and throws an exception if they do not.
/// </summary>
Expand Down
Loading