diff --git a/src/TestFramework/TestFramework/Assertions/Assert.AreEqual.cs b/src/TestFramework/TestFramework/Assertions/Assert.AreEqual.cs
index 2079f60160..274bc26248 100644
--- a/src/TestFramework/TestFramework/Assertions/Assert.AreEqual.cs
+++ b/src/TestFramework/TestFramework/Assertions/Assert.AreEqual.cs
@@ -1,6 +1,8 @@
// 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.ComponentModel;
+
namespace Microsoft.VisualStudio.TestTools.UnitTesting;
///
@@ -10,6 +12,356 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting;
///
public sealed partial class Assert
{
+ [InterpolatedStringHandler]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public readonly struct AssertAreEqualInterpolatedStringHandler
+ {
+ private readonly StringBuilder? _builder;
+ private readonly object? _expected;
+ private readonly object? _actual;
+
+ public AssertAreEqualInterpolatedStringHandler(int literalLength, int formattedCount, TArgument? expected, TArgument? actual, out bool shouldAppend)
+ {
+ _expected = expected!;
+ shouldAppend = AreEqualFailing(expected, actual);
+ if (shouldAppend)
+ {
+ _builder = new StringBuilder(literalLength + formattedCount);
+ _expected = expected;
+ _actual = actual;
+ }
+ }
+
+ public AssertAreEqualInterpolatedStringHandler(int literalLength, int formattedCount, TArgument? expected, TArgument? actual, IEqualityComparer? comparer, out bool shouldAppend)
+ {
+ shouldAppend = AreEqualFailing(expected, actual, comparer);
+ if (shouldAppend)
+ {
+ _builder = new StringBuilder(literalLength + formattedCount);
+ _expected = expected;
+ _actual = actual;
+ }
+ }
+
+ public AssertAreEqualInterpolatedStringHandler(int literalLength, int formattedCount, IEquatable? expected, IEquatable? actual, out bool shouldAppend)
+ {
+ shouldAppend = AreEqualFailing(expected, actual);
+ if (shouldAppend)
+ {
+ _builder = new StringBuilder(literalLength + formattedCount);
+ _expected = expected;
+ _actual = actual;
+ }
+ }
+
+ internal void ComputeAssertion()
+ {
+ if (_builder is not null)
+ {
+ ThrowAssertAreEqualFailed(_expected, _actual, _builder.ToString());
+ }
+ }
+
+ public void AppendLiteral(string? value) => _builder!.Append(value);
+
+ public void AppendFormatted(T value) => AppendFormatted(value, format: null);
+
+#if NETCOREAPP3_1_OR_GREATER
+ public void AppendFormatted(ReadOnlySpan value) => _builder!.Append(value);
+
+#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+ public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) => AppendFormatted(value.ToString(), alignment, format);
+#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+#endif
+
+ // NOTE: All the overloads involving format and/or alignment are not super efficient.
+ // This code path is only for when an assert is failing, so that's not the common scenario
+ // and should be okay if not very optimized.
+ // A more efficient implementation that can be used for .NET 6 and later is to delegate the work to
+ // the BCL's StringBuilder.AppendInterpolatedStringHandler
+ public void AppendFormatted(T value, string? format) => _builder!.AppendFormat(null, $"{{0:{format}}}", value);
+
+ public void AppendFormatted(T value, int alignment) => _builder!.AppendFormat(null, $"{{0,{alignment}}}", value);
+
+ public void AppendFormatted(T value, int alignment, string? format) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+
+ public void AppendFormatted(string? value) => _builder!.Append(value);
+
+#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 void AppendFormatted(string? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+
+ public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
+#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+ }
+
+ [InterpolatedStringHandler]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public readonly struct AssertAreNotEqualInterpolatedStringHandler
+ {
+ private readonly StringBuilder? _builder;
+ private readonly object? _notExpected;
+ private readonly object? _actual;
+
+ public AssertAreNotEqualInterpolatedStringHandler(int literalLength, int formattedCount, TArgument? notExpected, TArgument? actual, out bool shouldAppend)
+ : this(literalLength, formattedCount, notExpected, actual, null, out shouldAppend)
+ {
+ }
+
+ public AssertAreNotEqualInterpolatedStringHandler(int literalLength, int formattedCount, TArgument? notExpected, TArgument? actual, IEqualityComparer? comparer, out bool shouldAppend)
+ {
+ shouldAppend = AreNotEqualFailing(notExpected, actual, comparer);
+ if (shouldAppend)
+ {
+ _builder = new StringBuilder(literalLength + formattedCount);
+ _notExpected = notExpected;
+ _actual = actual;
+ }
+ }
+
+ internal void ComputeAssertion()
+ {
+ if (_builder is not null)
+ {
+ ThrowAssertAreNotEqualFailed(_notExpected, _actual, _builder.ToString());
+ }
+ }
+
+ public void AppendLiteral(string value) => _builder!.Append(value);
+
+ public void AppendFormatted(T value) => AppendFormatted(value, format: null);
+
+#if NETCOREAPP3_1_OR_GREATER
+ public void AppendFormatted(ReadOnlySpan value) => _builder!.Append(value);
+
+#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+ public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) => AppendFormatted(value.ToString(), alignment, format);
+#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+#endif
+
+ // NOTE: All the overloads involving format and/or alignment are not super efficient.
+ // This code path is only for when an assert is failing, so that's not the common scenario
+ // and should be okay if not very optimized.
+ // A more efficient implementation that can be used for .NET 6 and later is to delegate the work to
+ // the BCL's StringBuilder.AppendInterpolatedStringHandler
+ public void AppendFormatted(T value, string? format) => _builder!.AppendFormat(null, $"{{0:{format}}}", value);
+
+ public void AppendFormatted(T value, int alignment) => _builder!.AppendFormat(null, $"{{0,{alignment}}}", value);
+
+ public void AppendFormatted(T value, int alignment, string? format) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+
+ public void AppendFormatted(string? value) => _builder!.Append(value);
+
+#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 void AppendFormatted(string? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+
+ public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
+#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+ }
+
+ [InterpolatedStringHandler]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public readonly struct AssertNonGenericAreEqualInterpolatedStringHandler
+ {
+ private readonly StringBuilder? _builder;
+ private readonly Action? _failAction;
+
+ public AssertNonGenericAreEqualInterpolatedStringHandler(int literalLength, int formattedCount, float expected, float actual, float delta, out bool shouldAppend)
+ {
+ shouldAppend = AreEqualFailing(expected, actual, delta);
+ if (shouldAppend)
+ {
+ _builder = new StringBuilder(literalLength + formattedCount);
+ _failAction = userMessage => ThrowAssertAreEqualFailed(expected, actual, delta, userMessage);
+ }
+ }
+
+ public AssertNonGenericAreEqualInterpolatedStringHandler(int literalLength, int formattedCount, decimal expected, decimal actual, decimal delta, out bool shouldAppend)
+ {
+ shouldAppend = AreEqualFailing(expected, actual, delta);
+ if (shouldAppend)
+ {
+ _builder = new StringBuilder(literalLength + formattedCount);
+ _failAction = userMessage => ThrowAssertAreEqualFailed(expected, actual, delta, userMessage);
+ }
+ }
+
+ public AssertNonGenericAreEqualInterpolatedStringHandler(int literalLength, int formattedCount, long expected, long actual, long delta, out bool shouldAppend)
+ {
+ shouldAppend = AreEqualFailing(expected, actual, delta);
+ if (shouldAppend)
+ {
+ _builder = new StringBuilder(literalLength + formattedCount);
+ _failAction = userMessage => ThrowAssertAreEqualFailed(expected, actual, delta, userMessage);
+ }
+ }
+
+ public AssertNonGenericAreEqualInterpolatedStringHandler(int literalLength, int formattedCount, double expected, double actual, double delta, out bool shouldAppend)
+ {
+ shouldAppend = AreEqualFailing(expected, actual, delta);
+ if (shouldAppend)
+ {
+ _builder = new StringBuilder(literalLength + formattedCount);
+ _failAction = userMessage => ThrowAssertAreEqualFailed(expected, actual, delta, userMessage);
+ }
+ }
+
+ public AssertNonGenericAreEqualInterpolatedStringHandler(int literalLength, int formattedCount, string? expected, string? actual, bool ignoreCase, out bool shouldAppend)
+ : this(literalLength, formattedCount, expected, actual, ignoreCase, CultureInfo.InvariantCulture, out shouldAppend)
+ {
+ }
+
+ public AssertNonGenericAreEqualInterpolatedStringHandler(int literalLength, int formattedCount, string? expected, string? actual, bool ignoreCase, [NotNull] CultureInfo? culture, out bool shouldAppend)
+ {
+ Guard.NotNull(culture);
+ shouldAppend = AreEqualFailing(expected, actual, ignoreCase, culture);
+ if (shouldAppend)
+ {
+ _builder = new StringBuilder(literalLength + formattedCount);
+ _failAction = userMessage => ThrowAssertAreEqualFailed(expected, actual, ignoreCase, culture, userMessage);
+ }
+ }
+
+ internal void ComputeAssertion()
+ => _failAction?.Invoke(_builder!.ToString());
+
+ public void AppendLiteral(string value) => _builder!.Append(value);
+
+ public void AppendFormatted(T value) => AppendFormatted(value, format: null);
+
+#if NETCOREAPP3_1_OR_GREATER
+ public void AppendFormatted(ReadOnlySpan value) => _builder!.Append(value);
+
+#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+ public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) => AppendFormatted(value.ToString(), alignment, format);
+#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+#endif
+
+ // NOTE: All the overloads involving format and/or alignment are not super efficient.
+ // This code path is only for when an assert is failing, so that's not the common scenario
+ // and should be okay if not very optimized.
+ // A more efficient implementation that can be used for .NET 6 and later is to delegate the work to
+ // the BCL's StringBuilder.AppendInterpolatedStringHandler
+ public void AppendFormatted(T value, string? format) => _builder!.AppendFormat(null, $"{{0:{format}}}", value);
+
+ public void AppendFormatted(T value, int alignment) => _builder!.AppendFormat(null, $"{{0,{alignment}}}", value);
+
+ public void AppendFormatted(T value, int alignment, string? format) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+
+ public void AppendFormatted(string? value) => _builder!.Append(value);
+
+#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 void AppendFormatted(string? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+
+ public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
+#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+ }
+
+ [InterpolatedStringHandler]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public readonly struct AssertNonGenericAreNotEqualInterpolatedStringHandler
+ {
+ private readonly StringBuilder? _builder;
+ private readonly Action? _failAction;
+
+ public AssertNonGenericAreNotEqualInterpolatedStringHandler(int literalLength, int formattedCount, float notExpected, float actual, float delta, out bool shouldAppend)
+ {
+ shouldAppend = AreNotEqualFailing(notExpected, actual, delta);
+ if (shouldAppend)
+ {
+ _builder = new StringBuilder(literalLength + formattedCount);
+ _failAction = userMessage => ThrowAssertAreNotEqualFailed(notExpected, actual, delta, userMessage);
+ }
+ }
+
+ public AssertNonGenericAreNotEqualInterpolatedStringHandler(int literalLength, int formattedCount, decimal notExpected, decimal actual, decimal delta, out bool shouldAppend)
+ {
+ shouldAppend = AreNotEqualFailing(notExpected, actual, delta);
+ if (shouldAppend)
+ {
+ _builder = new StringBuilder(literalLength + formattedCount);
+ _failAction = userMessage => ThrowAssertAreNotEqualFailed(notExpected, actual, delta, userMessage);
+ }
+ }
+
+ public AssertNonGenericAreNotEqualInterpolatedStringHandler(int literalLength, int formattedCount, long notExpected, long actual, long delta, out bool shouldAppend)
+ {
+ shouldAppend = AreNotEqualFailing(notExpected, actual, delta);
+ if (shouldAppend)
+ {
+ _builder = new StringBuilder(literalLength + formattedCount);
+ _failAction = userMessage => ThrowAssertAreNotEqualFailed(notExpected, actual, delta, userMessage);
+ }
+ }
+
+ public AssertNonGenericAreNotEqualInterpolatedStringHandler(int literalLength, int formattedCount, double notExpected, double actual, double delta, out bool shouldAppend)
+ {
+ shouldAppend = AreNotEqualFailing(notExpected, actual, delta);
+ if (shouldAppend)
+ {
+ _builder = new StringBuilder(literalLength + formattedCount);
+ _failAction = userMessage => ThrowAssertAreNotEqualFailed(notExpected, actual, delta, userMessage);
+ }
+ }
+
+ public AssertNonGenericAreNotEqualInterpolatedStringHandler(int literalLength, int formattedCount, string? notExpected, string? actual, bool ignoreCase, out bool shouldAppend)
+ : this(literalLength, formattedCount, notExpected, actual, ignoreCase, CultureInfo.InvariantCulture, out shouldAppend)
+ {
+ }
+
+ public AssertNonGenericAreNotEqualInterpolatedStringHandler(int literalLength, int formattedCount, string? notExpected, string? actual, bool ignoreCase, [NotNull] CultureInfo? culture, out bool shouldAppend)
+ {
+ Guard.NotNull(culture);
+ shouldAppend = AreNotEqualFailing(notExpected, actual, ignoreCase, culture);
+ if (shouldAppend)
+ {
+ _builder = new StringBuilder(literalLength + formattedCount);
+ _failAction = userMessage => ThrowAssertAreNotEqualFailed(notExpected, actual, userMessage);
+ }
+ }
+
+ internal void ComputeAssertion()
+ => _failAction?.Invoke(_builder!.ToString());
+
+ public void AppendLiteral(string value) => _builder!.Append(value);
+
+ public void AppendFormatted(T value) => AppendFormatted(value, format: null);
+
+#if NETCOREAPP3_1_OR_GREATER
+ public void AppendFormatted(ReadOnlySpan value) => _builder!.Append(value);
+
+#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+ public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) => AppendFormatted(value.ToString(), alignment, format);
+#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+#endif
+
+ // NOTE: All the overloads involving format and/or alignment are not super efficient.
+ // This code path is only for when an assert is failing, so that's not the common scenario
+ // and should be okay if not very optimized.
+ // A more efficient implementation that can be used for .NET 6 and later is to delegate the work to
+ // the BCL's StringBuilder.AppendInterpolatedStringHandler
+ public void AppendFormatted(T value, string? format) => _builder!.AppendFormat(null, $"{{0:{format}}}", value);
+
+ public void AppendFormatted(T value, int alignment) => _builder!.AppendFormat(null, $"{{0,{alignment}}}", value);
+
+ public void AppendFormatted(T value, int alignment, string? format) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+
+ public void AppendFormatted(string? value) => _builder!.Append(value);
+
+#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 void AppendFormatted(string? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+
+ public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
+#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+ }
+
///
/// Tests whether the specified values are equal and throws an exception
/// if the two values are not equal.
@@ -80,6 +432,12 @@ public static void AreEqual(T? expected, T? actual, IEqualityComparer? com
public static void AreEqual(T? expected, T? actual, string? message)
=> AreEqual(expected, actual, null, message, null);
+ ///
+#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578
+ public static void AreEqual(T? expected, T? actual, [InterpolatedStringHandlerArgument(nameof(expected), nameof(actual))] ref AssertAreEqualInterpolatedStringHandler message)
+#pragma warning restore IDE0060 // Remove unused parameter
+ => message.ComputeAssertion();
+
///
/// Tests whether the specified values are equal and throws an exception
/// if the two values are not equal.
@@ -110,6 +468,12 @@ public static void AreEqual(T? expected, T? actual, string? message)
public static void AreEqual(T? expected, T? actual, IEqualityComparer? comparer, string? message)
=> AreEqual(expected, actual, comparer, message, null);
+ ///
+#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578
+ public static void AreEqual(T? expected, T? actual, IEqualityComparer? comparer, [InterpolatedStringHandlerArgument(nameof(expected), nameof(actual), nameof(comparer))] ref AssertAreEqualInterpolatedStringHandler message)
+#pragma warning restore IDE0060 // Remove unused parameter
+ => message.ComputeAssertion();
+
///
/// Tests whether the specified values are equal and throws an exception
/// if the two values are not equal.
@@ -172,30 +536,13 @@ public static void AreEqual(T? expected, T? actual, [StringSyntax(StringSynta
public static void AreEqual(T? expected, T? actual, IEqualityComparer? comparer,
[StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters)
{
- IEqualityComparer localComparer = comparer ?? EqualityComparer.Default;
- if (localComparer.Equals(expected!, actual!))
+ if (!AreEqualFailing(expected, actual, comparer))
{
return;
}
string userMessage = BuildUserMessage(message, parameters);
- string finalMessage = actual != null && expected != null && !actual.GetType().Equals(expected.GetType())
- ? string.Format(
- CultureInfo.CurrentCulture,
- FrameworkMessages.AreEqualDifferentTypesFailMsg,
- userMessage,
- ReplaceNulls(expected),
- expected.GetType().FullName,
- ReplaceNulls(actual),
- actual.GetType().FullName)
- : string.Format(
- CultureInfo.CurrentCulture,
- FrameworkMessages.AreEqualFailMsg,
- userMessage,
- ReplaceNulls(expected),
- ReplaceNulls(actual));
-
- ThrowAssertFailed("Assert.AreEqual", finalMessage);
+ ThrowAssertAreEqualFailed(expected, actual, userMessage);
}
///
@@ -244,6 +591,12 @@ public static void AreEqual(IEquatable? expected, IEquatable? actual)
public static void AreEqual(IEquatable? expected, IEquatable? actual, string? message)
=> AreEqual(expected, actual, message, null);
+ ///
+#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578
+ public static void AreEqual(IEquatable? expected, IEquatable? actual, [InterpolatedStringHandlerArgument(nameof(expected), nameof(actual))] ref AssertAreEqualInterpolatedStringHandler message)
+#pragma warning restore IDE0060 // Remove unused parameter
+ => message.ComputeAssertion();
+
///
/// Tests whether the specified values are equal and throws an exception
/// if the two values are not equal.
@@ -272,17 +625,44 @@ public static void AreEqual(IEquatable? expected, IEquatable? actual, s
///
public static void AreEqual(IEquatable? expected, IEquatable? actual, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters)
{
- if (actual is null && expected is null)
- {
- return;
- }
-
- if (actual?.Equals(expected) == true)
+ if (!AreEqualFailing(expected, actual))
{
return;
}
string userMessage = BuildUserMessage(message, parameters);
+ ThrowAssertAreEqualFailed(expected, actual, userMessage);
+ }
+
+ private static bool AreEqualFailing(T? expected, T? actual)
+ => AreEqualFailing(expected, actual, null);
+
+ private static bool AreEqualFailing(T? expected, T? actual, IEqualityComparer? comparer)
+ => !(comparer ?? EqualityComparer.Default).Equals(expected!, actual!);
+
+ private static bool AreEqualFailing(IEquatable? expected, IEquatable? actual)
+ => (actual is not null || expected is not null) && actual?.Equals(expected) != true;
+
+ private static bool AreEqualFailing(string? expected, string? actual, bool ignoreCase, CultureInfo culture)
+ => CompareInternal(expected, actual, ignoreCase, culture) != 0;
+
+ private static bool AreEqualFailing(float expected, float actual, float delta)
+ => float.IsNaN(expected) || float.IsNaN(actual) || float.IsNaN(delta) ||
+ Math.Abs(expected - actual) > delta;
+
+ private static bool AreEqualFailing(double expected, double actual, double delta)
+ => double.IsNaN(expected) || double.IsNaN(actual) || double.IsNaN(delta) ||
+ Math.Abs(expected - actual) > delta;
+
+ private static bool AreEqualFailing(decimal expected, decimal actual, decimal delta)
+ => Math.Abs(expected - actual) > delta;
+
+ private static bool AreEqualFailing(long expected, long actual, long delta)
+ => Math.Abs(expected - actual) > delta;
+
+ [DoesNotReturn]
+ private static void ThrowAssertAreEqualFailed(object? expected, object? actual, string userMessage)
+ {
string finalMessage = actual != null && expected != null && !actual.GetType().Equals(expected.GetType())
? string.Format(
CultureInfo.CurrentCulture,
@@ -298,6 +678,39 @@ public static void AreEqual(IEquatable? expected, IEquatable? actual, [
userMessage,
ReplaceNulls(expected),
ReplaceNulls(actual));
+ ThrowAssertFailed("Assert.AreEqual", finalMessage);
+ }
+
+ [DoesNotReturn]
+ private static void ThrowAssertAreEqualFailed(T expected, T actual, T delta, string userMessage)
+ where T : struct, IConvertible
+ {
+ string finalMessage = string.Format(
+ CultureInfo.CurrentCulture,
+ FrameworkMessages.AreEqualDeltaFailMsg,
+ userMessage,
+ expected.ToString(CultureInfo.CurrentCulture.NumberFormat),
+ actual.ToString(CultureInfo.CurrentCulture.NumberFormat),
+ delta.ToString(CultureInfo.CurrentCulture.NumberFormat));
+ ThrowAssertFailed("Assert.AreEqual", finalMessage);
+ }
+
+ [DoesNotReturn]
+ private static void ThrowAssertAreEqualFailed(string? expected, string? actual, bool ignoreCase, CultureInfo culture, string userMessage)
+ {
+ string finalMessage = !ignoreCase && CompareInternal(expected, actual, ignoreCase, culture) == 0
+ ? string.Format(
+ CultureInfo.CurrentCulture,
+ FrameworkMessages.AreEqualCaseFailMsg,
+ userMessage,
+ ReplaceNulls(expected),
+ ReplaceNulls(actual))
+ : string.Format(
+ CultureInfo.CurrentCulture,
+ FrameworkMessages.AreEqualFailMsg,
+ userMessage,
+ ReplaceNulls(expected),
+ ReplaceNulls(actual));
ThrowAssertFailed("Assert.AreEqual", finalMessage);
}
@@ -374,6 +787,12 @@ public static void AreNotEqual(T? notExpected, T? actual, IEqualityComparer(T? notExpected, T? actual, string? message)
=> AreNotEqual(notExpected, actual, null, message, null);
+ ///
+#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578
+ public static void AreNotEqual(T? notExpected, T? actual, [InterpolatedStringHandlerArgument(nameof(notExpected), nameof(actual))] ref AssertAreNotEqualInterpolatedStringHandler message)
+#pragma warning restore IDE0060 // Remove unused parameter
+ => message.ComputeAssertion();
+
///
/// Tests whether the specified values are unequal and throws an exception
/// if the two values are equal.
@@ -404,6 +823,12 @@ public static void AreNotEqual(T? notExpected, T? actual, string? message)
public static void AreNotEqual(T? notExpected, T? actual, IEqualityComparer? comparer, string? message)
=> AreNotEqual(notExpected, actual, comparer, message, null);
+ ///
+#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578
+ public static void AreNotEqual(T? notExpected, T? actual, IEqualityComparer? comparer, [InterpolatedStringHandlerArgument(nameof(notExpected), nameof(actual), nameof(comparer))] ref AssertAreNotEqualInterpolatedStringHandler message)
+#pragma warning restore IDE0060 // Remove unused parameter
+ => message.ComputeAssertion();
+
///
/// Tests whether the specified values are unequal and throws an exception
/// if the two values are equal.
@@ -466,20 +891,13 @@ public static void AreNotEqual(T? notExpected, T? actual, [StringSyntax(Strin
public static void AreNotEqual(T? notExpected, T? actual, IEqualityComparer? comparer,
[StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters)
{
- IEqualityComparer localComparer = comparer ?? EqualityComparer.Default;
- if (!localComparer.Equals(notExpected!, actual!))
+ if (!AreNotEqualFailing(notExpected, actual, comparer))
{
return;
}
string userMessage = BuildUserMessage(message, parameters);
- string finalMessage = string.Format(
- CultureInfo.CurrentCulture,
- FrameworkMessages.AreNotEqualFailMsg,
- userMessage,
- ReplaceNulls(notExpected),
- ReplaceNulls(actual));
- ThrowAssertFailed("Assert.AreNotEqual", finalMessage);
+ ThrowAssertAreNotEqualFailed(notExpected, actual, userMessage);
}
///
@@ -531,6 +949,12 @@ public static void AreEqual(float expected, float actual, float delta)
public static void AreEqual(float expected, float actual, float delta, string? message)
=> AreEqual(expected, actual, delta, message, null);
+ ///
+#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578
+ public static void AreEqual(float expected, float actual, float delta, [InterpolatedStringHandlerArgument(nameof(expected), nameof(actual), nameof(delta))] ref AssertNonGenericAreEqualInterpolatedStringHandler message)
+#pragma warning restore IDE0060 // Remove unused parameter
+ => message.ComputeAssertion();
+
///
/// Tests whether the specified floats are equal and throws an exception
/// if they are not equal.
@@ -561,18 +985,10 @@ public static void AreEqual(float expected, float actual, float delta, string? m
public static void AreEqual(float expected, float actual, float delta, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message,
params object?[]? parameters)
{
- if (float.IsNaN(expected) || float.IsNaN(actual) || float.IsNaN(delta) ||
- Math.Abs(expected - actual) > delta)
+ if (AreEqualFailing(expected, actual, delta))
{
string userMessage = BuildUserMessage(message, parameters);
- string finalMessage = string.Format(
- CultureInfo.CurrentCulture,
- FrameworkMessages.AreEqualDeltaFailMsg,
- userMessage,
- expected.ToString(CultureInfo.CurrentCulture.NumberFormat),
- actual.ToString(CultureInfo.CurrentCulture.NumberFormat),
- delta.ToString(CultureInfo.CurrentCulture.NumberFormat));
- ThrowAssertFailed("Assert.AreEqual", finalMessage);
+ ThrowAssertAreEqualFailed(expected, actual, delta, userMessage);
}
}
@@ -625,6 +1041,12 @@ public static void AreNotEqual(float notExpected, float actual, float delta)
public static void AreNotEqual(float notExpected, float actual, float delta, string? message)
=> AreNotEqual(notExpected, actual, delta, message, null);
+ ///
+#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578
+ public static void AreNotEqual(float notExpected, float actual, float delta, [InterpolatedStringHandlerArgument(nameof(notExpected), nameof(actual), nameof(delta))] ref AssertNonGenericAreNotEqualInterpolatedStringHandler message)
+#pragma warning restore IDE0060 // Remove unused parameter
+ => message.ComputeAssertion();
+
///
/// Tests whether the specified floats are unequal and throws an exception
/// if they are equal.
@@ -655,20 +1077,16 @@ public static void AreNotEqual(float notExpected, float actual, float delta, str
public static void AreNotEqual(float notExpected, float actual, float delta, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message,
params object?[]? parameters)
{
- if (Math.Abs(notExpected - actual) <= delta)
+ if (AreNotEqualFailing(notExpected, actual, delta))
{
string userMessage = BuildUserMessage(message, parameters);
- string finalMessage = string.Format(
- CultureInfo.CurrentCulture,
- FrameworkMessages.AreNotEqualDeltaFailMsg,
- userMessage,
- notExpected.ToString(CultureInfo.CurrentCulture.NumberFormat),
- actual.ToString(CultureInfo.CurrentCulture.NumberFormat),
- delta.ToString(CultureInfo.CurrentCulture.NumberFormat));
- ThrowAssertFailed("Assert.AreNotEqual", finalMessage);
+ ThrowAssertAreNotEqualFailed(notExpected, actual, delta, userMessage);
}
}
+ private static bool AreNotEqualFailing(float notExpected, float actual, float delta)
+ => Math.Abs(notExpected - actual) <= delta;
+
///
/// Tests whether the specified decimals are equal and throws an exception
/// if they are not equal.
@@ -718,6 +1136,12 @@ public static void AreEqual(decimal expected, decimal actual, decimal delta)
public static void AreEqual(decimal expected, decimal actual, decimal delta, string? message)
=> AreEqual(expected, actual, delta, message, null);
+ ///
+#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578
+ public static void AreEqual(decimal expected, decimal actual, decimal delta, [InterpolatedStringHandlerArgument(nameof(expected), nameof(actual), nameof(delta))] ref AssertNonGenericAreEqualInterpolatedStringHandler message)
+#pragma warning restore IDE0060 // Remove unused parameter
+ => message.ComputeAssertion();
+
///
/// Tests whether the specified decimals are equal and throws an exception
/// if they are not equal.
@@ -748,17 +1172,10 @@ public static void AreEqual(decimal expected, decimal actual, decimal delta, str
public static void AreEqual(decimal expected, decimal actual, decimal delta, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message,
params object?[]? parameters)
{
- if (Math.Abs(expected - actual) > delta)
+ if (AreEqualFailing(expected, actual, delta))
{
string userMessage = BuildUserMessage(message, parameters);
- string finalMessage = string.Format(
- CultureInfo.CurrentCulture,
- FrameworkMessages.AreEqualDeltaFailMsg,
- userMessage,
- expected.ToString(CultureInfo.CurrentCulture.NumberFormat),
- actual.ToString(CultureInfo.CurrentCulture.NumberFormat),
- delta.ToString(CultureInfo.CurrentCulture.NumberFormat));
- ThrowAssertFailed("Assert.AreEqual", finalMessage);
+ ThrowAssertAreEqualFailed(expected, actual, delta, userMessage);
}
}
@@ -811,6 +1228,12 @@ public static void AreNotEqual(decimal notExpected, decimal actual, decimal delt
public static void AreNotEqual(decimal notExpected, decimal actual, decimal delta, string? message)
=> AreNotEqual(notExpected, actual, delta, message, null);
+ ///
+#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578
+ public static void AreNotEqual(decimal notExpected, decimal actual, decimal delta, [InterpolatedStringHandlerArgument(nameof(notExpected), nameof(actual), nameof(delta))] ref AssertNonGenericAreNotEqualInterpolatedStringHandler message)
+#pragma warning restore IDE0060 // Remove unused parameter
+ => message.ComputeAssertion();
+
///
/// Tests whether the specified decimals are unequal and throws an exception
/// if they are equal.
@@ -841,20 +1264,16 @@ public static void AreNotEqual(decimal notExpected, decimal actual, decimal delt
public static void AreNotEqual(decimal notExpected, decimal actual, decimal delta, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message,
params object?[]? parameters)
{
- if (Math.Abs(notExpected - actual) <= delta)
+ if (AreNotEqualFailing(notExpected, actual, delta))
{
string userMessage = BuildUserMessage(message, parameters);
- string finalMessage = string.Format(
- CultureInfo.CurrentCulture,
- FrameworkMessages.AreNotEqualDeltaFailMsg,
- userMessage,
- notExpected.ToString(CultureInfo.CurrentCulture.NumberFormat),
- actual.ToString(CultureInfo.CurrentCulture.NumberFormat),
- delta.ToString(CultureInfo.CurrentCulture.NumberFormat));
- ThrowAssertFailed("Assert.AreNotEqual", finalMessage);
+ ThrowAssertAreNotEqualFailed(notExpected, actual, delta, userMessage);
}
}
+ private static bool AreNotEqualFailing(decimal notExpected, decimal actual, decimal delta)
+ => Math.Abs(notExpected - actual) <= delta;
+
///
/// Tests whether the specified longs are equal and throws an exception
/// if they are not equal.
@@ -904,6 +1323,12 @@ public static void AreEqual(long expected, long actual, long delta)
public static void AreEqual(long expected, long actual, long delta, string? message)
=> AreEqual(expected, actual, delta, message, null);
+ ///
+#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578
+ public static void AreEqual(long expected, long actual, long delta, [InterpolatedStringHandlerArgument(nameof(expected), nameof(actual), nameof(delta))] ref AssertNonGenericAreEqualInterpolatedStringHandler message)
+#pragma warning restore IDE0060 // Remove unused parameter
+ => message.ComputeAssertion();
+
///
/// Tests whether the specified longs are equal and throws an exception
/// if they are not equal.
@@ -934,17 +1359,10 @@ public static void AreEqual(long expected, long actual, long delta, string? mess
public static void AreEqual(long expected, long actual, long delta, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message,
params object?[]? parameters)
{
- if (Math.Abs(expected - actual) > delta)
+ if (AreEqualFailing(expected, actual, delta))
{
string userMessage = BuildUserMessage(message, parameters);
- string finalMessage = string.Format(
- CultureInfo.CurrentCulture,
- FrameworkMessages.AreEqualDeltaFailMsg,
- userMessage,
- expected.ToString(CultureInfo.CurrentCulture.NumberFormat),
- actual.ToString(CultureInfo.CurrentCulture.NumberFormat),
- delta.ToString(CultureInfo.CurrentCulture.NumberFormat));
- ThrowAssertFailed("Assert.AreEqual", finalMessage);
+ ThrowAssertAreEqualFailed(expected, actual, delta, userMessage);
}
}
@@ -997,6 +1415,12 @@ public static void AreNotEqual(long notExpected, long actual, long delta)
public static void AreNotEqual(long notExpected, long actual, long delta, string? message)
=> AreNotEqual(notExpected, actual, delta, message, null);
+ ///
+#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578
+ public static void AreNotEqual(long notExpected, long actual, long delta, [InterpolatedStringHandlerArgument(nameof(notExpected), nameof(actual), nameof(delta))] ref AssertNonGenericAreNotEqualInterpolatedStringHandler message)
+#pragma warning restore IDE0060 // Remove unused parameter
+ => message.ComputeAssertion();
+
///
/// Tests whether the specified longs are unequal and throws an exception
/// if they are equal.
@@ -1027,20 +1451,16 @@ public static void AreNotEqual(long notExpected, long actual, long delta, string
public static void AreNotEqual(long notExpected, long actual, long delta, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message,
params object?[]? parameters)
{
- if (Math.Abs(notExpected - actual) <= delta)
+ if (AreNotEqualFailing(notExpected, actual, delta))
{
string userMessage = BuildUserMessage(message, parameters);
- string finalMessage = string.Format(
- CultureInfo.CurrentCulture,
- FrameworkMessages.AreNotEqualDeltaFailMsg,
- userMessage,
- notExpected.ToString(CultureInfo.CurrentCulture.NumberFormat),
- actual.ToString(CultureInfo.CurrentCulture.NumberFormat),
- delta.ToString(CultureInfo.CurrentCulture.NumberFormat));
- ThrowAssertFailed("Assert.AreNotEqual", finalMessage);
+ ThrowAssertAreNotEqualFailed(notExpected, actual, delta, userMessage);
}
}
+ private static bool AreNotEqualFailing(long notExpected, long actual, long delta)
+ => Math.Abs(notExpected - actual) <= delta;
+
///
/// Tests whether the specified doubles are equal and throws an exception
/// if they are not equal.
@@ -1089,6 +1509,12 @@ public static void AreEqual(double expected, double actual, double delta)
public static void AreEqual(double expected, double actual, double delta, string? message)
=> AreEqual(expected, actual, delta, message, null);
+ ///
+#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578
+ public static void AreEqual(double expected, double actual, double delta, [InterpolatedStringHandlerArgument(nameof(expected), nameof(actual), nameof(delta))] ref AssertNonGenericAreEqualInterpolatedStringHandler message)
+#pragma warning restore IDE0060 // Remove unused parameter
+ => message.ComputeAssertion();
+
///
/// Tests whether the specified doubles are equal and throws an exception
/// if they are not equal.
@@ -1118,18 +1544,10 @@ public static void AreEqual(double expected, double actual, double delta, string
public static void AreEqual(double expected, double actual, double delta, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message,
params object?[]? parameters)
{
- if (double.IsNaN(expected) || double.IsNaN(actual) || double.IsNaN(delta) ||
- Math.Abs(expected - actual) > delta)
+ if (AreEqualFailing(expected, actual, delta))
{
string userMessage = BuildUserMessage(message, parameters);
- string finalMessage = string.Format(
- CultureInfo.CurrentCulture,
- FrameworkMessages.AreEqualDeltaFailMsg,
- userMessage,
- expected.ToString(CultureInfo.CurrentCulture.NumberFormat),
- actual.ToString(CultureInfo.CurrentCulture.NumberFormat),
- delta.ToString(CultureInfo.CurrentCulture.NumberFormat));
- ThrowAssertFailed("Assert.AreEqual", finalMessage);
+ ThrowAssertAreEqualFailed(expected, actual, delta, userMessage);
}
}
@@ -1182,6 +1600,12 @@ public static void AreNotEqual(double notExpected, double actual, double delta)
public static void AreNotEqual(double notExpected, double actual, double delta, string? message)
=> AreNotEqual(notExpected, actual, delta, message, null);
+ ///
+#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578
+ public static void AreNotEqual(double notExpected, double actual, double delta, [InterpolatedStringHandlerArgument(nameof(notExpected), nameof(actual), nameof(delta))] ref AssertNonGenericAreNotEqualInterpolatedStringHandler message)
+#pragma warning restore IDE0060 // Remove unused parameter
+ => message.ComputeAssertion();
+
///
/// Tests whether the specified doubles are unequal and throws an exception
/// if they are equal.
@@ -1212,20 +1636,30 @@ public static void AreNotEqual(double notExpected, double actual, double delta,
public static void AreNotEqual(double notExpected, double actual, double delta, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message,
params object?[]? parameters)
{
- if (Math.Abs(notExpected - actual) <= delta)
+ if (AreNotEqualFailing(notExpected, actual, delta))
{
string userMessage = BuildUserMessage(message, parameters);
- string finalMessage = string.Format(
- CultureInfo.CurrentCulture,
- FrameworkMessages.AreNotEqualDeltaFailMsg,
- userMessage,
- notExpected.ToString(CultureInfo.CurrentCulture.NumberFormat),
- actual.ToString(CultureInfo.CurrentCulture.NumberFormat),
- delta.ToString(CultureInfo.CurrentCulture.NumberFormat));
- ThrowAssertFailed("Assert.AreNotEqual", finalMessage);
+ ThrowAssertAreNotEqualFailed(notExpected, actual, delta, userMessage);
}
}
+ private static bool AreNotEqualFailing(double notExpected, double actual, double delta)
+ => Math.Abs(notExpected - actual) <= delta;
+
+ [DoesNotReturn]
+ private static void ThrowAssertAreNotEqualFailed(T notExpected, T actual, T delta, string userMessage)
+ where T : struct, IConvertible
+ {
+ string finalMessage = string.Format(
+ CultureInfo.CurrentCulture,
+ FrameworkMessages.AreNotEqualDeltaFailMsg,
+ userMessage,
+ notExpected.ToString(CultureInfo.CurrentCulture.NumberFormat),
+ actual.ToString(CultureInfo.CurrentCulture.NumberFormat),
+ delta.ToString(CultureInfo.CurrentCulture.NumberFormat));
+ ThrowAssertFailed("Assert.AreNotEqual", finalMessage);
+ }
+
///
/// Tests whether the specified strings are equal and throws an exception
/// if they are not equal. The invariant culture is used for the comparison.
@@ -1271,6 +1705,12 @@ public static void AreEqual(string? expected, string? actual, bool ignoreCase)
public static void AreEqual(string? expected, string? actual, bool ignoreCase, string? message)
=> AreEqual(expected, actual, ignoreCase, message, null);
+ ///
+#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578
+ public static void AreEqual(string? expected, string? actual, bool ignoreCase, [InterpolatedStringHandlerArgument(nameof(expected), nameof(actual), nameof(ignoreCase))] ref AssertNonGenericAreEqualInterpolatedStringHandler message)
+#pragma warning restore IDE0060 // Remove unused parameter
+ => message.ComputeAssertion();
+
///
/// Tests whether the specified strings are equal and throws an exception
/// if they are not equal. The invariant culture is used for the comparison.
@@ -1353,6 +1793,16 @@ public static void AreEqual(string? expected, string? actual, bool ignoreCase,
[NotNull] CultureInfo? culture, string? message)
=> AreEqual(expected, actual, ignoreCase, culture, message, null);
+ ///
+#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578
+ public static void AreEqual(string? expected, string? actual, bool ignoreCase,
+#pragma warning restore IDE0060 // Remove unused parameter
+ [NotNull] CultureInfo? culture, [InterpolatedStringHandlerArgument(nameof(expected), nameof(actual), nameof(ignoreCase), nameof(culture))] ref AssertNonGenericAreEqualInterpolatedStringHandler message)
+ {
+ CheckParameterNotNull(culture, "Assert.AreEqual", nameof(culture), string.Empty);
+ message.ComputeAssertion();
+ }
+
///
/// Tests whether the specified strings are equal and throws an exception
/// if they are not equal.
@@ -1385,28 +1835,13 @@ public static void AreEqual(string? expected, string? actual, bool ignoreCase,
[NotNull] CultureInfo? culture, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters)
{
CheckParameterNotNull(culture, "Assert.AreEqual", "culture", string.Empty);
- if (CompareInternal(expected, actual, ignoreCase, culture) == 0)
+ if (!AreEqualFailing(expected, actual, ignoreCase, culture))
{
return;
}
string userMessage = BuildUserMessage(message, parameters);
- string finalMessage = !ignoreCase && CompareInternal(expected, actual, ignoreCase, culture) == 0
- ? string.Format(
- CultureInfo.CurrentCulture,
- FrameworkMessages.AreEqualCaseFailMsg,
- userMessage,
- ReplaceNulls(expected),
- ReplaceNulls(actual))
- : string.Format(
- CultureInfo.CurrentCulture,
- FrameworkMessages.AreEqualFailMsg,
- userMessage,
- ReplaceNulls(expected),
- ReplaceNulls(actual));
-
- // Comparison failed. Check if it was a case-only failure.
- ThrowAssertFailed("Assert.AreEqual", finalMessage);
+ ThrowAssertAreEqualFailed(expected, actual, ignoreCase, culture, userMessage);
}
///
@@ -1456,6 +1891,12 @@ public static void AreNotEqual(string? notExpected, string? actual, bool ignoreC
public static void AreNotEqual(string? notExpected, string? actual, bool ignoreCase, string? message)
=> AreNotEqual(notExpected, actual, ignoreCase, message, null);
+ ///
+#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578
+ public static void AreNotEqual(string? notExpected, string? actual, bool ignoreCase, [InterpolatedStringHandlerArgument(nameof(notExpected), nameof(actual), nameof(ignoreCase))] ref AssertNonGenericAreNotEqualInterpolatedStringHandler message)
+#pragma warning restore IDE0060 // Remove unused parameter
+ => message.ComputeAssertion();
+
///
/// Tests whether the specified strings are unequal and throws an exception
/// if they are equal. The invariant culture is used for the comparison.
@@ -1540,6 +1981,16 @@ public static void AreNotEqual(string? notExpected, string? actual, bool ignoreC
CultureInfo? culture, string? message)
=> AreNotEqual(notExpected, actual, ignoreCase, culture, message, null);
+ ///
+#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578
+ public static void AreNotEqual(string? notExpected, string? actual, bool ignoreCase,
+#pragma warning restore IDE0060 // Remove unused parameter
+ CultureInfo? culture, [InterpolatedStringHandlerArgument(nameof(notExpected), nameof(actual), nameof(ignoreCase), nameof(culture))] ref AssertNonGenericAreNotEqualInterpolatedStringHandler message)
+ {
+ CheckParameterNotNull(culture, "Assert.AreNotEqual", nameof(culture), string.Empty);
+ message.ComputeAssertion();
+ }
+
///
/// Tests whether the specified strings are unequal and throws an exception
/// if they are equal.
@@ -1573,12 +2024,24 @@ public static void AreNotEqual(string? notExpected, string? actual, bool ignoreC
CultureInfo? culture, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters)
{
CheckParameterNotNull(culture, "Assert.AreNotEqual", "culture", string.Empty);
- if (CompareInternal(notExpected, actual, ignoreCase, culture) != 0)
+ if (!AreNotEqualFailing(notExpected, actual, ignoreCase, culture))
{
return;
}
string userMessage = BuildUserMessage(message, parameters);
+ ThrowAssertAreNotEqualFailed(notExpected, actual, userMessage);
+ }
+
+ private static bool AreNotEqualFailing(string? notExpected, string? actual, bool ignoreCase, CultureInfo culture)
+ => CompareInternal(notExpected, actual, ignoreCase, culture) == 0;
+
+ private static bool AreNotEqualFailing(T? notExpected, T? actual, IEqualityComparer? comparer)
+ => (comparer ?? EqualityComparer.Default).Equals(notExpected!, actual!);
+
+ [DoesNotReturn]
+ private static void ThrowAssertAreNotEqualFailed(object? notExpected, object? actual, string userMessage)
+ {
string finalMessage = string.Format(
CultureInfo.CurrentCulture,
FrameworkMessages.AreNotEqualFailMsg,
diff --git a/src/TestFramework/TestFramework/Assertions/Assert.AreSame.cs b/src/TestFramework/TestFramework/Assertions/Assert.AreSame.cs
index b2fd9113d1..0a7799900d 100644
--- a/src/TestFramework/TestFramework/Assertions/Assert.AreSame.cs
+++ b/src/TestFramework/TestFramework/Assertions/Assert.AreSame.cs
@@ -1,6 +1,8 @@
// 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.ComponentModel;
+
namespace Microsoft.VisualStudio.TestTools.UnitTesting;
///
@@ -10,6 +12,124 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting;
///
public sealed partial class Assert
{
+ [InterpolatedStringHandler]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public readonly struct AssertAreSameInterpolatedStringHandler
+ {
+ private readonly StringBuilder? _builder;
+ private readonly TArgument? _expected;
+ private readonly TArgument? _actual;
+
+ public AssertAreSameInterpolatedStringHandler(int literalLength, int formattedCount, TArgument? expected, TArgument? actual, out bool shouldAppend)
+ {
+ _expected = expected;
+ _actual = actual;
+ shouldAppend = IsAreSameFailing(expected, actual);
+ if (shouldAppend)
+ {
+ _builder = new StringBuilder(literalLength + formattedCount);
+ }
+ }
+
+ internal void ComputeAssertion()
+ {
+ if (_builder is not null)
+ {
+ ThrowAssertAreSameFailed(_expected, _actual, _builder.ToString());
+ }
+ }
+
+ public void AppendLiteral(string value) => _builder!.Append(value);
+
+ public void AppendFormatted(T value) => AppendFormatted(value, format: null);
+
+#if NETCOREAPP3_1_OR_GREATER
+ public void AppendFormatted(ReadOnlySpan value) => _builder!.Append(value);
+
+#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+ public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) => AppendFormatted(value.ToString(), alignment, format);
+#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+#endif
+
+ // NOTE: All the overloads involving format and/or alignment are not super efficient.
+ // This code path is only for when an assert is failing, so that's not the common scenario
+ // and should be okay if not very optimized.
+ // A more efficient implementation that can be used for .NET 6 and later is to delegate the work to
+ // the BCL's StringBuilder.AppendInterpolatedStringHandler
+ public void AppendFormatted(T value, string? format) => _builder!.AppendFormat(null, $"{{0:{format}}}", value);
+
+ public void AppendFormatted(T value, int alignment) => _builder!.AppendFormat(null, $"{{0,{alignment}}}", value);
+
+ public void AppendFormatted(T value, int alignment, string? format) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+
+ public void AppendFormatted(string? value) => _builder!.Append(value);
+
+#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 void AppendFormatted(string? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+
+ public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
+#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+ }
+
+ [InterpolatedStringHandler]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public readonly struct AssertAreNotSameInterpolatedStringHandler
+ {
+ private readonly StringBuilder? _builder;
+
+ public AssertAreNotSameInterpolatedStringHandler(int literalLength, int formattedCount, TArgument? notExpected, TArgument? actual, out bool shouldAppend)
+ {
+ shouldAppend = IsAreNotSameFailing(notExpected, actual);
+ if (shouldAppend)
+ {
+ _builder = new StringBuilder(literalLength + formattedCount);
+ }
+ }
+
+ internal void ComputeAssertion()
+ {
+ if (_builder is not null)
+ {
+ ThrowAssertAreNotSameFailed(_builder.ToString());
+ }
+ }
+
+ public void AppendLiteral(string value) => _builder!.Append(value);
+
+ public void AppendFormatted(T value) => AppendFormatted(value, format: null);
+
+#if NETCOREAPP3_1_OR_GREATER
+ public void AppendFormatted(ReadOnlySpan value) => _builder!.Append(value);
+
+#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+ public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) => AppendFormatted(value.ToString(), alignment, format);
+#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+#endif
+
+ // NOTE: All the overloads involving format and/or alignment are not super efficient.
+ // This code path is only for when an assert is failing, so that's not the common scenario
+ // and should be okay if not very optimized.
+ // A more efficient implementation that can be used for .NET 6 and later is to delegate the work to
+ // the BCL's StringBuilder.AppendInterpolatedStringHandler
+ public void AppendFormatted(T value, string? format) => _builder!.AppendFormat(null, $"{{0:{format}}}", value);
+
+ public void AppendFormatted(T value, int alignment) => _builder!.AppendFormat(null, $"{{0,{alignment}}}", value);
+
+ public void AppendFormatted(T value, int alignment, string? format) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+
+ public void AppendFormatted(string? value) => _builder!.Append(value);
+
+#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 void AppendFormatted(string? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+
+ public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
+#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+ }
+
///
/// Tests whether the specified objects both refer to the same object and
/// throws an exception if the two inputs do not refer to the same object.
@@ -55,6 +175,12 @@ public static void AreSame(T? expected, T? actual)
public static void AreSame(T? expected, T? actual, string? message)
=> AreSame(expected, actual, message, null);
+ ///
+#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578
+ public static void AreSame(T? expected, T? actual, [InterpolatedStringHandlerArgument(nameof(expected), nameof(actual))] ref AssertAreSameInterpolatedStringHandler message)
+#pragma warning restore IDE0060 // Remove unused parameter
+ => message.ComputeAssertion();
+
///
/// Tests whether the specified objects both refer to the same object and
/// throws an exception if the two inputs do not refer to the same object.
@@ -82,23 +208,28 @@ public static void AreSame(T? expected, T? actual, string? message)
///
public static void AreSame(T? expected, T? actual, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters)
{
- if (ReferenceEquals(expected, actual))
+ if (!IsAreSameFailing(expected, actual))
{
return;
}
string userMessage = BuildUserMessage(message, parameters);
- string finalMessage = userMessage;
+ ThrowAssertAreSameFailed(expected, actual, userMessage);
+ }
+
+ private static bool IsAreSameFailing(T? expected, T? actual)
+ => !ReferenceEquals(expected, actual);
- if (expected is ValueType)
+ [DoesNotReturn]
+ private static void ThrowAssertAreSameFailed(T? expected, T? actual, string userMessage)
+ {
+ string finalMessage = userMessage;
+ if (expected is ValueType && actual is ValueType)
{
- if (actual is ValueType)
- {
- finalMessage = string.Format(
- CultureInfo.CurrentCulture,
- FrameworkMessages.AreSameGivenValues,
- userMessage);
- }
+ finalMessage = string.Format(
+ CultureInfo.CurrentCulture,
+ FrameworkMessages.AreSameGivenValues,
+ userMessage);
}
ThrowAssertFailed("Assert.AreSame", finalMessage);
@@ -151,6 +282,12 @@ public static void AreNotSame(T? notExpected, T? actual)
public static void AreNotSame(T? notExpected, T? actual, string? message)
=> AreNotSame(notExpected, actual, message, null);
+ ///
+#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578
+ public static void AreNotSame(T? notExpected, T? actual, [InterpolatedStringHandlerArgument(nameof(notExpected), nameof(actual))] ref AssertAreNotSameInterpolatedStringHandler message)
+#pragma warning restore IDE0060 // Remove unused parameter
+ => message.ComputeAssertion();
+
///
/// Tests whether the specified objects refer to different objects and
/// throws an exception if the two inputs refer to the same object.
@@ -179,9 +316,16 @@ public static void AreNotSame(T? notExpected, T? actual, string? message)
///
public static void AreNotSame(T? notExpected, T? actual, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters)
{
- if (ReferenceEquals(notExpected, actual))
+ if (IsAreNotSameFailing(notExpected, actual))
{
- ThrowAssertFailed("Assert.AreNotSame", BuildUserMessage(message, parameters));
+ ThrowAssertAreNotSameFailed(BuildUserMessage(message, parameters));
}
}
+
+ private static bool IsAreNotSameFailing(T? notExpected, T? actual)
+ => ReferenceEquals(notExpected, actual);
+
+ [DoesNotReturn]
+ private static void ThrowAssertAreNotSameFailed(string userMessage)
+ => ThrowAssertFailed("Assert.AreNotSame", userMessage);
}
diff --git a/src/TestFramework/TestFramework/Assertions/Assert.IsInstanceOfType.cs b/src/TestFramework/TestFramework/Assertions/Assert.IsInstanceOfType.cs
index 84adbb12b5..63f62e38ba 100644
--- a/src/TestFramework/TestFramework/Assertions/Assert.IsInstanceOfType.cs
+++ b/src/TestFramework/TestFramework/Assertions/Assert.IsInstanceOfType.cs
@@ -1,6 +1,8 @@
// 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.ComponentModel;
+
namespace Microsoft.VisualStudio.TestTools.UnitTesting;
///
@@ -10,6 +12,246 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting;
///
public sealed partial class Assert
{
+ [InterpolatedStringHandler]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public readonly struct AssertIsInstanceOfTypeInterpolatedStringHandler
+ {
+ private readonly StringBuilder? _builder;
+ private readonly object? _value;
+ private readonly Type? _expectedType;
+
+ public AssertIsInstanceOfTypeInterpolatedStringHandler(int literalLength, int formattedCount, object? value, Type? expectedType, out bool shouldAppend)
+ {
+ _value = value;
+ _expectedType = expectedType;
+ shouldAppend = IsInstanceOfTypeFailing(value, expectedType);
+ if (shouldAppend)
+ {
+ _builder = new StringBuilder(literalLength + formattedCount);
+ }
+ }
+
+ internal void ComputeAssertion()
+ {
+ if (_builder is not null)
+ {
+ ThrowAssertIsInstanceOfTypeFailed(_value, _expectedType, _builder.ToString());
+ }
+ }
+
+ public void AppendLiteral(string value) => _builder!.Append(value);
+
+ public void AppendFormatted(T value) => AppendFormatted(value, format: null);
+
+#if NETCOREAPP3_1_OR_GREATER
+ public void AppendFormatted(ReadOnlySpan value) => _builder!.Append(value);
+
+#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+ public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) => AppendFormatted(value.ToString(), alignment, format);
+#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+#endif
+
+ // NOTE: All the overloads involving format and/or alignment are not super efficient.
+ // This code path is only for when an assert is failing, so that's not the common scenario
+ // and should be okay if not very optimized.
+ // A more efficient implementation that can be used for .NET 6 and later is to delegate the work to
+ // the BCL's StringBuilder.AppendInterpolatedStringHandler
+ public void AppendFormatted(T value, string? format) => _builder!.AppendFormat(null, $"{{0:{format}}}", value);
+
+ public void AppendFormatted(T value, int alignment) => _builder!.AppendFormat(null, $"{{0,{alignment}}}", value);
+
+ public void AppendFormatted(T value, int alignment, string? format) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+
+ public void AppendFormatted(string? value) => _builder!.Append(value);
+
+#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 void AppendFormatted(string? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+
+ public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
+#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+ }
+
+ [InterpolatedStringHandler]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public readonly struct AssertGenericIsInstanceOfTypeInterpolatedStringHandler
+ {
+ private readonly StringBuilder? _builder;
+ private readonly object? _value;
+
+ public AssertGenericIsInstanceOfTypeInterpolatedStringHandler(int literalLength, int formattedCount, object? value, out bool shouldAppend)
+ {
+ _value = value;
+ shouldAppend = IsInstanceOfTypeFailing(value, typeof(TArg));
+ if (shouldAppend)
+ {
+ _builder = new StringBuilder(literalLength + formattedCount);
+ }
+ }
+
+ internal void ComputeAssertion()
+ {
+ if (_builder is not null)
+ {
+ ThrowAssertIsInstanceOfTypeFailed(_value, typeof(TArg), _builder.ToString());
+ }
+ }
+
+ public void AppendLiteral(string value) => _builder!.Append(value);
+
+ public void AppendFormatted(T value) => AppendFormatted(value, format: null);
+
+#if NETCOREAPP3_1_OR_GREATER
+ public void AppendFormatted(ReadOnlySpan value) => _builder!.Append(value);
+
+#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+ public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) => AppendFormatted(value.ToString(), alignment, format);
+#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+#endif
+
+ // NOTE: All the overloads involving format and/or alignment are not super efficient.
+ // This code path is only for when an assert is failing, so that's not the common scenario
+ // and should be okay if not very optimized.
+ // A more efficient implementation that can be used for .NET 6 and later is to delegate the work to
+ // the BCL's StringBuilder.AppendInterpolatedStringHandler
+ public void AppendFormatted(T value, string? format) => _builder!.AppendFormat(null, $"{{0:{format}}}", value);
+
+ public void AppendFormatted(T value, int alignment) => _builder!.AppendFormat(null, $"{{0,{alignment}}}", value);
+
+ public void AppendFormatted(T value, int alignment, string? format) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+
+ public void AppendFormatted(string? value) => _builder!.Append(value);
+
+#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 void AppendFormatted(string? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+
+ public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
+#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+ }
+
+ [InterpolatedStringHandler]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public readonly struct AssertIsNotInstanceOfTypeInterpolatedStringHandler
+ {
+ private readonly StringBuilder? _builder;
+ private readonly object? _value;
+ private readonly Type? _wrongType;
+
+ public AssertIsNotInstanceOfTypeInterpolatedStringHandler(int literalLength, int formattedCount, object? value, Type? wrongType, out bool shouldAppend)
+ {
+ _value = value;
+ _wrongType = wrongType;
+ shouldAppend = IsNotInstanceOfTypeFailing(value, wrongType);
+ if (shouldAppend)
+ {
+ _builder = new StringBuilder(literalLength + formattedCount);
+ }
+ }
+
+ internal void ComputeAssertion()
+ {
+ if (_builder is not null)
+ {
+ ThrowAssertIsNotInstanceOfTypeFailed(_value, _wrongType, _builder.ToString());
+ }
+ }
+
+ public void AppendLiteral(string value) => _builder!.Append(value);
+
+ public void AppendFormatted(T value) => AppendFormatted(value, format: null);
+
+#if NETCOREAPP3_1_OR_GREATER
+ public void AppendFormatted(ReadOnlySpan value) => _builder!.Append(value);
+
+#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+ public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) => AppendFormatted(value.ToString(), alignment, format);
+#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+#endif
+
+ // NOTE: All the overloads involving format and/or alignment are not super efficient.
+ // This code path is only for when an assert is failing, so that's not the common scenario
+ // and should be okay if not very optimized.
+ // A more efficient implementation that can be used for .NET 6 and later is to delegate the work to
+ // the BCL's StringBuilder.AppendInterpolatedStringHandler
+ public void AppendFormatted(T value, string? format) => _builder!.AppendFormat(null, $"{{0:{format}}}", value);
+
+ public void AppendFormatted(T value, int alignment) => _builder!.AppendFormat(null, $"{{0,{alignment}}}", value);
+
+ public void AppendFormatted(T value, int alignment, string? format) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+
+ public void AppendFormatted(string? value) => _builder!.Append(value);
+
+#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 void AppendFormatted(string? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+
+ public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
+#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+ }
+
+ [InterpolatedStringHandler]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public readonly struct AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler
+ {
+ private readonly StringBuilder? _builder;
+ private readonly object? _value;
+
+ public AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler(int literalLength, int formattedCount, object? value, out bool shouldAppend)
+ {
+ _value = value;
+ shouldAppend = IsNotInstanceOfTypeFailing(value, typeof(TArg));
+ if (shouldAppend)
+ {
+ _builder = new StringBuilder(literalLength + formattedCount);
+ }
+ }
+
+ internal void ComputeAssertion()
+ {
+ if (_builder is not null)
+ {
+ ThrowAssertIsNotInstanceOfTypeFailed(_value, typeof(TArg), _builder.ToString());
+ }
+ }
+
+ public void AppendLiteral(string value) => _builder!.Append(value);
+
+ public void AppendFormatted(T value) => AppendFormatted(value, format: null);
+
+#if NETCOREAPP3_1_OR_GREATER
+ public void AppendFormatted(ReadOnlySpan value) => _builder!.Append(value);
+
+#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+ public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) => AppendFormatted(value.ToString(), alignment, format);
+#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+#endif
+
+ // NOTE: All the overloads involving format and/or alignment are not super efficient.
+ // This code path is only for when an assert is failing, so that's not the common scenario
+ // and should be okay if not very optimized.
+ // A more efficient implementation that can be used for .NET 6 and later is to delegate the work to
+ // the BCL's StringBuilder.AppendInterpolatedStringHandler
+ public void AppendFormatted(T value, string? format) => _builder!.AppendFormat(null, $"{{0:{format}}}", value);
+
+ public void AppendFormatted(T value, int alignment) => _builder!.AppendFormat(null, $"{{0,{alignment}}}", value);
+
+ public void AppendFormatted(T value, int alignment, string? format) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+
+ public void AppendFormatted(string? value) => _builder!.Append(value);
+
+#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 void AppendFormatted(string? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+
+ public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
+#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+ }
+
///
/// Tests whether the specified object is an instance of the expected
/// type and throws an exception if the expected type is not in the
@@ -71,6 +313,14 @@ public static void IsInstanceOfType([NotNull] object? value, out T instance)
public static void IsInstanceOfType([NotNull] object? value, [NotNull] Type? expectedType, string? message)
=> IsInstanceOfType(value, expectedType, message, null);
+ ///
+#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578
+ public static void IsInstanceOfType([NotNull] object? value, [NotNull] Type? expectedType, [InterpolatedStringHandlerArgument(nameof(value), nameof(expectedType))] ref AssertIsInstanceOfTypeInterpolatedStringHandler message)
+#pragma warning restore IDE0060 // Remove unused parameter
+#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. - Not sure how to express the semantics to the compiler, but the implementation guarantees that.
+ => message.ComputeAssertion();
+#pragma warning restore CS8777 // Parameter must have a non-null value when exiting.
+
///
/// Tests whether the specified object is an instance of the generic
/// type and throws an exception if the generic type is not in the
@@ -80,6 +330,14 @@ public static void IsInstanceOfType([NotNull] object? value, [NotNull] Type? exp
public static void IsInstanceOfType([NotNull] object? value, string? message)
=> IsInstanceOfType(value, typeof(T), message, null);
+ ///
+#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578
+ public static void IsInstanceOfType([NotNull] object? value, [InterpolatedStringHandlerArgument(nameof(value))] ref AssertGenericIsInstanceOfTypeInterpolatedStringHandler message)
+#pragma warning restore IDE0060 // Remove unused parameter
+#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. - Not sure how to express the semantics to the compiler, but the implementation guarantees that.
+ => message.ComputeAssertion();
+#pragma warning restore CS8777 // Parameter must have a non-null value when exiting.
+
///
/// Tests whether the specified object is an instance of the generic
/// type and throws an exception if the generic type is not in the
@@ -89,6 +347,15 @@ public static void IsInstanceOfType([NotNull] object? value, string? message)
public static void IsInstanceOfType([NotNull] object? value, out T instance, string? message)
=> IsInstanceOfType(value, out instance, message, null);
+ ///
+ public static void IsInstanceOfType([NotNull] object? value, out T instance, [InterpolatedStringHandlerArgument(nameof(value))] ref AssertGenericIsInstanceOfTypeInterpolatedStringHandler message)
+#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. - Not sure how to express the semantics to the compiler, but the implementation guarantees that.
+ {
+ message.ComputeAssertion();
+ instance = (T)value!;
+ }
+#pragma warning restore CS8777 // Parameter must have a non-null value when exiting.
+
///
/// Tests whether the specified object is an instance of the expected
/// type and throws an exception if the expected type is not in the
@@ -116,23 +383,30 @@ public static void IsInstanceOfType([NotNull] object? value, out T instance,
public static void IsInstanceOfType([NotNull] object? value, [NotNull] Type? expectedType, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message,
params object?[]? parameters)
{
- if (expectedType == null || value == null)
+ if (IsInstanceOfTypeFailing(value, expectedType))
{
- ThrowAssertFailed("Assert.IsInstanceOfType", BuildUserMessage(message, parameters));
+ ThrowAssertIsInstanceOfTypeFailed(value, expectedType, BuildUserMessage(message, parameters));
}
+ }
- Type elementType = value.GetType();
- if (!expectedType.IsAssignableFrom(elementType))
+ private static bool IsInstanceOfTypeFailing([NotNullWhen(false)] object? value, [NotNullWhen(false)] Type? expectedType)
+ => expectedType == null || value == null || !expectedType.IsAssignableFrom(value.GetType());
+
+ [DoesNotReturn]
+ private static void ThrowAssertIsInstanceOfTypeFailed(object? value, Type? expectedType, string userMessage)
+ {
+ string finalMessage = userMessage;
+ if (expectedType is not null && value is not null)
{
- string userMessage = BuildUserMessage(message, parameters);
- string finalMessage = string.Format(
+ finalMessage = string.Format(
CultureInfo.CurrentCulture,
FrameworkMessages.IsInstanceOfFailMsg,
userMessage,
expectedType.ToString(),
value.GetType().ToString());
- ThrowAssertFailed("Assert.IsInstanceOfType", finalMessage);
}
+
+ ThrowAssertFailed("Assert.IsInstanceOfType", finalMessage);
}
///
@@ -210,6 +484,14 @@ public static void IsNotInstanceOfType(object? value)
public static void IsNotInstanceOfType(object? value, [NotNull] Type? wrongType, string? message)
=> IsNotInstanceOfType(value, wrongType, message, null);
+ ///
+#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578
+ public static void IsNotInstanceOfType(object? value, [NotNull] Type? wrongType, [InterpolatedStringHandlerArgument(nameof(value), nameof(wrongType))] ref AssertIsNotInstanceOfTypeInterpolatedStringHandler message)
+#pragma warning restore IDE0060 // Remove unused parameter
+#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. - Not sure how to express the semantics to the compiler, but the implementation guarantees that.
+ => message.ComputeAssertion();
+#pragma warning restore CS8777 // Parameter must have a non-null value when exiting.
+
///
/// Tests whether the specified object is not an instance of the wrong generic
/// type and throws an exception if the specified type is in the
@@ -219,6 +501,12 @@ public static void IsNotInstanceOfType(object? value, [NotNull] Type? wrongType,
public static void IsNotInstanceOfType(object? value, string? message)
=> IsNotInstanceOfType(value, typeof(T), message, null);
+ ///
+#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578
+ public static void IsNotInstanceOfType(object? value, [InterpolatedStringHandlerArgument(nameof(value))] AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler message)
+#pragma warning restore IDE0060 // Remove unused parameter
+ => message.ComputeAssertion();
+
///
/// Tests whether the specified object is not an instance of the wrong
/// type and throws an exception if the specified type is in the
@@ -246,29 +534,32 @@ public static void IsNotInstanceOfType(object? value, string? message)
public static void IsNotInstanceOfType(object? value, [NotNull] Type? wrongType, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message,
params object?[]? parameters)
{
- if (wrongType == null)
+ if (IsNotInstanceOfTypeFailing(value, wrongType))
{
- ThrowAssertFailed("Assert.IsNotInstanceOfType", BuildUserMessage(message, parameters));
+ ThrowAssertIsNotInstanceOfTypeFailed(value, wrongType, BuildUserMessage(message, parameters));
}
+ }
- // Null is not an instance of any type.
- if (value == null)
- {
- return;
- }
+ private static bool IsNotInstanceOfTypeFailing(object? value, [NotNullWhen(false)] Type? wrongType)
+ => wrongType is null ||
+ // Null is not an instance of any type.
+ (value is not null && wrongType.IsAssignableFrom(value.GetType()));
- Type elementType = value.GetType();
- if (wrongType.IsAssignableFrom(elementType))
+ [DoesNotReturn]
+ private static void ThrowAssertIsNotInstanceOfTypeFailed(object? value, Type? wrongType, string userMessage)
+ {
+ string finalMessage = userMessage;
+ if (wrongType is not null)
{
- string userMessage = BuildUserMessage(message, parameters);
- string finalMessage = string.Format(
+ finalMessage = string.Format(
CultureInfo.CurrentCulture,
FrameworkMessages.IsNotInstanceOfFailMsg,
userMessage,
wrongType.ToString(),
- value.GetType().ToString());
- ThrowAssertFailed("Assert.IsNotInstanceOfType", finalMessage);
+ value!.GetType().ToString());
}
+
+ ThrowAssertFailed("Assert.IsNotInstanceOfType", finalMessage);
}
///
diff --git a/src/TestFramework/TestFramework/Assertions/Assert.IsNull.cs b/src/TestFramework/TestFramework/Assertions/Assert.IsNull.cs
index 96df80dd1d..74dd9a5e51 100644
--- a/src/TestFramework/TestFramework/Assertions/Assert.IsNull.cs
+++ b/src/TestFramework/TestFramework/Assertions/Assert.IsNull.cs
@@ -1,6 +1,8 @@
// 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.ComponentModel;
+
namespace Microsoft.VisualStudio.TestTools.UnitTesting;
///
@@ -10,6 +12,120 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting;
///
public sealed partial class Assert
{
+ [InterpolatedStringHandler]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public readonly struct AssertIsNullInterpolatedStringHandler
+ {
+ private readonly StringBuilder? _builder;
+
+ public AssertIsNullInterpolatedStringHandler(int literalLength, int formattedCount, object? value, out bool shouldAppend)
+ {
+ shouldAppend = IsNullFailing(value);
+ if (shouldAppend)
+ {
+ _builder = new StringBuilder(literalLength + formattedCount);
+ }
+ }
+
+ internal void ComputeAssertion()
+ {
+ if (_builder is not null)
+ {
+ ThrowAssertIsNullFailed(_builder.ToString());
+ }
+ }
+
+ public void AppendLiteral(string value) => _builder!.Append(value);
+
+ public void AppendFormatted(T value) => AppendFormatted(value, format: null);
+
+#if NETCOREAPP3_1_OR_GREATER
+ public void AppendFormatted(ReadOnlySpan value) => _builder!.Append(value);
+
+#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+ public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) => AppendFormatted(value.ToString(), alignment, format);
+#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+#endif
+
+ // NOTE: All the overloads involving format and/or alignment are not super efficient.
+ // This code path is only for when an assert is failing, so that's not the common scenario
+ // and should be okay if not very optimized.
+ // A more efficient implementation that can be used for .NET 6 and later is to delegate the work to
+ // the BCL's StringBuilder.AppendInterpolatedStringHandler
+ public void AppendFormatted(T value, string? format) => _builder!.AppendFormat(null, $"{{0:{format}}}", value);
+
+ public void AppendFormatted(T value, int alignment) => _builder!.AppendFormat(null, $"{{0,{alignment}}}", value);
+
+ public void AppendFormatted(T value, int alignment, string? format) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+
+ public void AppendFormatted(string? value) => _builder!.Append(value);
+
+#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 void AppendFormatted(string? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+
+ public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
+#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+ }
+
+ [InterpolatedStringHandler]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public readonly struct AssertIsNotNullInterpolatedStringHandler
+ {
+ private readonly StringBuilder? _builder;
+
+ public AssertIsNotNullInterpolatedStringHandler(int literalLength, int formattedCount, object? value, out bool shouldAppend)
+ {
+ shouldAppend = IsNotNullFailing(value);
+ if (shouldAppend)
+ {
+ _builder = new StringBuilder(literalLength + formattedCount);
+ }
+ }
+
+ internal void ComputeAssertion()
+ {
+ if (_builder is not null)
+ {
+ ThrowAssertIsNotNullFailed(_builder.ToString());
+ }
+ }
+
+ public void AppendLiteral(string value) => _builder!.Append(value);
+
+ public void AppendFormatted(T value) => AppendFormatted(value, format: null);
+
+#if NETCOREAPP3_1_OR_GREATER
+ public void AppendFormatted(ReadOnlySpan value) => _builder!.Append(value);
+
+#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+ public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) => AppendFormatted(value.ToString(), alignment, format);
+#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+#endif
+
+ // NOTE: All the overloads involving format and/or alignment are not super efficient.
+ // This code path is only for when an assert is failing, so that's not the common scenario
+ // and should be okay if not very optimized.
+ // A more efficient implementation that can be used for .NET 6 and later is to delegate the work to
+ // the BCL's StringBuilder.AppendInterpolatedStringHandler
+ public void AppendFormatted(T value, string? format) => _builder!.AppendFormat(null, $"{{0:{format}}}", value);
+
+ public void AppendFormatted(T value, int alignment) => _builder!.AppendFormat(null, $"{{0,{alignment}}}", value);
+
+ public void AppendFormatted(T value, int alignment, string? format) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+
+ public void AppendFormatted(string? value) => _builder!.Append(value);
+
+#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 void AppendFormatted(string? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+
+ public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
+#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+ }
+
///
/// Tests whether the specified object is null and throws an exception
/// if it is not.
@@ -40,6 +156,12 @@ public static void IsNull(object? value)
public static void IsNull(object? value, string? message)
=> IsNull(value, message, null);
+ ///
+#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578
+ public static void IsNull(object? value, [InterpolatedStringHandlerArgument(nameof(value))] ref AssertIsNullInterpolatedStringHandler message)
+#pragma warning restore IDE0060 // Remove unused parameter
+ => message.ComputeAssertion();
+
///
/// Tests whether the specified object is null and throws an exception
/// if it is not.
@@ -59,12 +181,17 @@ public static void IsNull(object? value, string? message)
///
public static void IsNull(object? value, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters)
{
- if (value != null)
+ if (IsNullFailing(value))
{
- ThrowAssertFailed("Assert.IsNull", BuildUserMessage(message, parameters));
+ ThrowAssertIsNullFailed(BuildUserMessage(message, parameters));
}
}
+ private static bool IsNullFailing(object? value) => value is not null;
+
+ private static void ThrowAssertIsNullFailed(string message)
+ => ThrowAssertFailed("Assert.IsNull", message);
+
///
/// Tests whether the specified object is non-null and throws an exception
/// if it is null.
@@ -95,6 +222,14 @@ public static void IsNotNull([NotNull] object? value)
public static void IsNotNull([NotNull] object? value, string? message)
=> IsNotNull(value, message, null);
+ ///
+#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578
+ public static void IsNotNull([NotNull] object? value, [InterpolatedStringHandlerArgument(nameof(value))] ref AssertIsNotNullInterpolatedStringHandler message)
+#pragma warning restore IDE0060 // Remove unused parameter
+#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. - Not sure how to express the semantics to the compiler, but the implementation guarantees that.
+ => message.ComputeAssertion();
+#pragma warning restore CS8777 // Parameter must have a non-null value when exiting.
+
///
/// Tests whether the specified object is non-null and throws an exception
/// if it is null.
@@ -114,9 +249,15 @@ public static void IsNotNull([NotNull] object? value, string? message)
///
public static void IsNotNull([NotNull] object? value, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters)
{
- if (value == null)
+ if (IsNotNullFailing(value))
{
- ThrowAssertFailed("Assert.IsNotNull", BuildUserMessage(message, parameters));
+ ThrowAssertIsNotNullFailed(BuildUserMessage(message, parameters));
}
}
+
+ private static bool IsNotNullFailing([NotNullWhen(false)] object? value) => value is null;
+
+ [DoesNotReturn]
+ private static void ThrowAssertIsNotNullFailed(string message)
+ => ThrowAssertFailed("Assert.IsNotNull", message);
}
diff --git a/src/TestFramework/TestFramework/Assertions/Assert.IsTrue.cs b/src/TestFramework/TestFramework/Assertions/Assert.IsTrue.cs
index 62e96b5470..53815e5b36 100644
--- a/src/TestFramework/TestFramework/Assertions/Assert.IsTrue.cs
+++ b/src/TestFramework/TestFramework/Assertions/Assert.IsTrue.cs
@@ -1,6 +1,8 @@
// 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.ComponentModel;
+
namespace Microsoft.VisualStudio.TestTools.UnitTesting;
///
@@ -10,6 +12,120 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting;
///
public sealed partial class Assert
{
+ [InterpolatedStringHandler]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public readonly struct AssertIsTrueInterpolatedStringHandler
+ {
+ private readonly StringBuilder? _builder;
+
+ public AssertIsTrueInterpolatedStringHandler(int literalLength, int formattedCount, bool? condition, out bool shouldAppend)
+ {
+ shouldAppend = IsTrueFailing(condition);
+ if (shouldAppend)
+ {
+ _builder = new StringBuilder(literalLength + formattedCount);
+ }
+ }
+
+ internal void ComputeAssertion()
+ {
+ if (_builder is not null)
+ {
+ ThrowAssertIsTrueFailed(_builder.ToString());
+ }
+ }
+
+ public void AppendLiteral(string value) => _builder!.Append(value);
+
+ public void AppendFormatted(T value) => AppendFormatted(value, format: null);
+
+#if NETCOREAPP3_1_OR_GREATER
+ public void AppendFormatted(ReadOnlySpan value) => _builder!.Append(value);
+
+#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+ public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) => AppendFormatted(value.ToString(), alignment, format);
+#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+#endif
+
+ // NOTE: All the overloads involving format and/or alignment are not super efficient.
+ // This code path is only for when an assert is failing, so that's not the common scenario
+ // and should be okay if not very optimized.
+ // A more efficient implementation that can be used for .NET 6 and later is to delegate the work to
+ // the BCL's StringBuilder.AppendInterpolatedStringHandler
+ public void AppendFormatted(T value, string? format) => _builder!.AppendFormat(null, $"{{0:{format}}}", value);
+
+ public void AppendFormatted(T value, int alignment) => _builder!.AppendFormat(null, $"{{0,{alignment}}}", value);
+
+ public void AppendFormatted(T value, int alignment, string? format) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+
+ public void AppendFormatted(string? value) => _builder!.Append(value);
+
+#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 void AppendFormatted(string? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+
+ public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
+#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+ }
+
+ [InterpolatedStringHandler]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public readonly struct AssertIsFalseInterpolatedStringHandler
+ {
+ private readonly StringBuilder? _builder;
+
+ public AssertIsFalseInterpolatedStringHandler(int literalLength, int formattedCount, bool? condition, out bool shouldAppend)
+ {
+ shouldAppend = IsFalseFailing(condition);
+ if (shouldAppend)
+ {
+ _builder = new StringBuilder(literalLength + formattedCount);
+ }
+ }
+
+ internal void ComputeAssertion()
+ {
+ if (_builder is not null)
+ {
+ ThrowAssertIsFalseFailed(_builder.ToString());
+ }
+ }
+
+ public void AppendLiteral(string value) => _builder!.Append(value);
+
+ public void AppendFormatted(T value) => AppendFormatted(value, format: null);
+
+#if NETCOREAPP3_1_OR_GREATER
+ public void AppendFormatted(ReadOnlySpan value) => _builder!.Append(value);
+
+#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+ public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) => AppendFormatted(value.ToString(), alignment, format);
+#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+#endif
+
+ // NOTE: All the overloads involving format and/or alignment are not super efficient.
+ // This code path is only for when an assert is failing, so that's not the common scenario
+ // and should be okay if not very optimized.
+ // A more efficient implementation that can be used for .NET 6 and later is to delegate the work to
+ // the BCL's StringBuilder.AppendInterpolatedStringHandler
+ public void AppendFormatted(T value, string? format) => _builder!.AppendFormat(null, $"{{0:{format}}}", value);
+
+ public void AppendFormatted(T value, int alignment) => _builder!.AppendFormat(null, $"{{0,{alignment}}}", value);
+
+ public void AppendFormatted(T value, int alignment, string? format) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+
+ public void AppendFormatted(string? value) => _builder!.Append(value);
+
+#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 void AppendFormatted(string? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+
+ public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
+#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+ }
+
///
/// Tests whether the specified condition is true and throws an exception
/// if the condition is false.
@@ -53,6 +169,12 @@ public static void IsTrue([DoesNotReturnIf(false)] bool? condition)
public static void IsTrue([DoesNotReturnIf(false)] bool condition, string? message)
=> IsTrue(condition, message, null);
+ ///
+#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578
+ public static void IsTrue([DoesNotReturnIf(false)] bool condition, [InterpolatedStringHandlerArgument(nameof(condition))] ref AssertIsTrueInterpolatedStringHandler message)
+#pragma warning restore IDE0060 // Remove unused parameter
+ => message.ComputeAssertion();
+
///
/// Tests whether the specified condition is true and throws an exception
/// if the condition is false.
@@ -70,6 +192,12 @@ public static void IsTrue([DoesNotReturnIf(false)] bool condition, string? messa
public static void IsTrue([DoesNotReturnIf(false)] bool? condition, string? message)
=> IsTrue(condition, message, null);
+ ///
+#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578
+ public static void IsTrue([DoesNotReturnIf(false)] bool? condition, [InterpolatedStringHandlerArgument(nameof(condition))] ref AssertIsTrueInterpolatedStringHandler message)
+#pragma warning restore IDE0060 // Remove unused parameter
+ => message.ComputeAssertion();
+
///
/// Tests whether the specified condition is true and throws an exception
/// if the condition is false.
@@ -90,9 +218,9 @@ public static void IsTrue([DoesNotReturnIf(false)] bool? condition, string? mess
public static void IsTrue([DoesNotReturnIf(false)] bool condition, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message,
params object?[]? parameters)
{
- if (!condition)
+ if (IsTrueFailing(condition))
{
- ThrowAssertFailed("Assert.IsTrue", BuildUserMessage(message, parameters));
+ ThrowAssertIsTrueFailed(BuildUserMessage(message, parameters));
}
}
@@ -116,12 +244,21 @@ public static void IsTrue([DoesNotReturnIf(false)] bool condition, [StringSyntax
public static void IsTrue([DoesNotReturnIf(false)] bool? condition, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message,
params object?[]? parameters)
{
- if (condition is false or null)
+ if (IsTrueFailing(condition))
{
- ThrowAssertFailed("Assert.IsTrue", BuildUserMessage(message, parameters));
+ ThrowAssertIsTrueFailed(BuildUserMessage(message, parameters));
}
}
+ private static bool IsTrueFailing(bool? condition)
+ => condition is false or null;
+
+ private static bool IsTrueFailing(bool condition)
+ => !condition;
+
+ private static void ThrowAssertIsTrueFailed(string message)
+ => ThrowAssertFailed("Assert.IsTrue", message);
+
///
/// Tests whether the specified condition is false and throws an exception
/// if the condition is true.
@@ -165,6 +302,12 @@ public static void IsFalse([DoesNotReturnIf(true)] bool? condition)
public static void IsFalse([DoesNotReturnIf(true)] bool condition, string? message)
=> IsFalse(condition, message, null);
+ ///
+#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578
+ public static void IsFalse([DoesNotReturnIf(true)] bool condition, [InterpolatedStringHandlerArgument(nameof(condition))] ref AssertIsFalseInterpolatedStringHandler message)
+#pragma warning restore IDE0060 // Remove unused parameter
+ => message.ComputeAssertion();
+
///
/// Tests whether the specified condition is false and throws an exception
/// if the condition is true.
@@ -182,6 +325,12 @@ public static void IsFalse([DoesNotReturnIf(true)] bool condition, string? messa
public static void IsFalse([DoesNotReturnIf(true)] bool? condition, string? message)
=> IsFalse(condition, message, null);
+ ///
+#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578
+ public static void IsFalse([DoesNotReturnIf(true)] bool? condition, [InterpolatedStringHandlerArgument(nameof(condition))] ref AssertIsFalseInterpolatedStringHandler message)
+#pragma warning restore IDE0060 // Remove unused parameter
+ => message.ComputeAssertion();
+
///
/// Tests whether the specified condition is false and throws an exception
/// if the condition is true.
@@ -202,9 +351,9 @@ public static void IsFalse([DoesNotReturnIf(true)] bool? condition, string? mess
public static void IsFalse([DoesNotReturnIf(true)] bool condition, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message,
params object?[]? parameters)
{
- if (condition)
+ if (IsFalseFailing(condition))
{
- ThrowAssertFailed("Assert.IsFalse", BuildUserMessage(message, parameters));
+ ThrowAssertIsFalseFailed(BuildUserMessage(message, parameters));
}
}
@@ -228,9 +377,19 @@ public static void IsFalse([DoesNotReturnIf(true)] bool condition, [StringSyntax
public static void IsFalse([DoesNotReturnIf(true)] bool? condition, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message,
params object?[]? parameters)
{
- if (condition is true or null)
+ if (IsFalseFailing(condition))
{
- ThrowAssertFailed("Assert.IsFalse", BuildUserMessage(message, parameters));
+ ThrowAssertIsFalseFailed(BuildUserMessage(message, parameters));
}
}
+
+ private static bool IsFalseFailing(bool? condition)
+ => condition is true or null;
+
+ private static bool IsFalseFailing(bool condition)
+ => condition;
+
+ [DoesNotReturn]
+ private static void ThrowAssertIsFalseFailed(string userMessage)
+ => ThrowAssertFailed("Assert.IsFalse", userMessage);
}
diff --git a/src/TestFramework/TestFramework/Assertions/Assert.ThrowsException.cs b/src/TestFramework/TestFramework/Assertions/Assert.ThrowsException.cs
index e926389093..66efac037d 100644
--- a/src/TestFramework/TestFramework/Assertions/Assert.ThrowsException.cs
+++ b/src/TestFramework/TestFramework/Assertions/Assert.ThrowsException.cs
@@ -12,6 +12,140 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting;
///
public sealed partial class Assert
{
+ [InterpolatedStringHandler]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public readonly struct AssertNonStrictThrowsInterpolatedStringHandler
+ where TException : Exception
+ {
+ private readonly StringBuilder? _builder;
+ private readonly ThrowsExceptionState _state;
+
+ public AssertNonStrictThrowsInterpolatedStringHandler(int literalLength, int formattedCount, Action action, out bool shouldAppend)
+ {
+ _state = IsThrowsFailing(action, isStrictType: false, "Throws");
+ shouldAppend = _state.FailAction is not null;
+ if (shouldAppend)
+ {
+ _builder = new StringBuilder(literalLength + formattedCount);
+ }
+ }
+
+ internal TException ComputeAssertion()
+ {
+ if (_state.FailAction is not null)
+ {
+ _state.FailAction(_builder!.ToString());
+ }
+ else
+ {
+ return (TException)_state.ExceptionWhenNotFailing!;
+ }
+
+ // This will not hit, but need it for compiler.
+ return null!;
+ }
+
+ public void AppendLiteral(string value) => _builder!.Append(value);
+
+ public void AppendFormatted(T value) => AppendFormatted(value, format: null);
+
+#if NETCOREAPP3_1_OR_GREATER
+ public void AppendFormatted(ReadOnlySpan value) => _builder!.Append(value);
+
+#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+ public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) => AppendFormatted(value.ToString(), alignment, format);
+#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+#endif
+
+ // NOTE: All the overloads involving format and/or alignment are not super efficient.
+ // This code path is only for when an assert is failing, so that's not the common scenario
+ // and should be okay if not very optimized.
+ // A more efficient implementation that can be used for .NET 6 and later is to delegate the work to
+ // the BCL's StringBuilder.AppendInterpolatedStringHandler
+ public void AppendFormatted(T value, string? format) => _builder!.AppendFormat(null, $"{{0:{format}}}", value);
+
+ public void AppendFormatted(T value, int alignment) => _builder!.AppendFormat(null, $"{{0,{alignment}}}", value);
+
+ public void AppendFormatted(T value, int alignment, string? format) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+
+ public void AppendFormatted(string? value) => _builder!.Append(value);
+
+#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 void AppendFormatted(string? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+
+ public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
+ }
+
+ [InterpolatedStringHandler]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public readonly struct AssertThrowsExactlyInterpolatedStringHandler
+ where TException : Exception
+ {
+ private readonly StringBuilder? _builder;
+ private readonly ThrowsExceptionState _state;
+
+ public AssertThrowsExactlyInterpolatedStringHandler(int literalLength, int formattedCount, Action action, out bool shouldAppend)
+ {
+ _state = IsThrowsFailing(action, isStrictType: true, "ThrowsExactly");
+ shouldAppend = _state.FailAction is not null;
+ if (shouldAppend)
+ {
+ _builder = new StringBuilder(literalLength + formattedCount);
+ }
+ }
+
+ internal TException ComputeAssertion()
+ {
+ if (_state.FailAction is not null)
+ {
+ _state.FailAction(_builder!.ToString());
+ }
+ else
+ {
+ return (TException)_state.ExceptionWhenNotFailing!;
+ }
+
+ // This will not hit, but need it for compiler.
+ return null!;
+ }
+
+ public void AppendLiteral(string value) => _builder!.Append(value);
+
+ public void AppendFormatted(T value) => AppendFormatted(value, format: null);
+
+#if NETCOREAPP3_1_OR_GREATER
+ public void AppendFormatted(ReadOnlySpan value) => _builder!.Append(value);
+
+#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+ public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) => AppendFormatted(value.ToString(), alignment, format);
+#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+#endif
+
+ // NOTE: All the overloads involving format and/or alignment are not super efficient.
+ // This code path is only for when an assert is failing, so that's not the common scenario
+ // and should be okay if not very optimized.
+ // A more efficient implementation that can be used for .NET 6 and later is to delegate the work to
+ // the BCL's StringBuilder.AppendInterpolatedStringHandler
+ public void AppendFormatted(T value, string? format) => _builder!.AppendFormat(null, $"{{0:{format}}}", value);
+
+ public void AppendFormatted(T value, int alignment) => _builder!.AppendFormat(null, $"{{0,{alignment}}}", value);
+
+ public void AppendFormatted(T value, int alignment, string? format) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+
+ public void AppendFormatted(string? value) => _builder!.Append(value);
+
+#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 void AppendFormatted(string? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+
+ public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value);
+#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
+#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
+ }
+
///
/// Asserts that the delegate throws an exception of type
/// (or derived type) and throws AssertFailedException if code does not throws exception or throws
@@ -39,6 +173,13 @@ public static TException Throws(Action action, string message = "",
where TException : Exception
=> ThrowsException(action, isStrictType: false, message, parameters: messageArgs);
+ ///
+#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578
+ public static TException Throws(Action action, [InterpolatedStringHandlerArgument(nameof(action))] ref AssertNonStrictThrowsInterpolatedStringHandler message)
+#pragma warning restore IDE0060 // Remove unused parameter
+ where TException : Exception
+ => message.ComputeAssertion();
+
///
/// Asserts that the delegate