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 throws an exception of type /// (and not of derived type) and throws AssertFailedException if code does not throws exception or throws @@ -66,6 +207,13 @@ public static TException ThrowsExactly(Action action, string message where TException : Exception => ThrowsException(action, isStrictType: true, message, parameters: messageArgs); + /// +#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578 + public static TException ThrowsExactly(Action action, [InterpolatedStringHandlerArgument(nameof(action))] ref AssertThrowsExactlyInterpolatedStringHandler message) +#pragma warning restore IDE0060 // Remove unused parameter + where TException : Exception + => message.ComputeAssertion(); + /// /// Tests whether the code specified by delegate throws exact given exception /// of type (and not of derived type) and throws AssertFailedException @@ -229,41 +377,18 @@ private static TException ThrowsException(Action action, bool isStri Guard.NotNull(action); Guard.NotNull(message); - string userMessage, finalMessage; - try + ThrowsExceptionState state = IsThrowsFailing(action, isStrictType, assertMethodName); + if (state.FailAction is not null) { - action(); + state.FailAction(BuildUserMessage(message, parameters)); } - catch (Exception ex) + else { - bool isExceptionOfType = isStrictType - ? typeof(TException) == ex.GetType() - : ex is TException; - if (!isExceptionOfType) - { - userMessage = BuildUserMessage(message, parameters); - finalMessage = string.Format( - CultureInfo.CurrentCulture, - FrameworkMessages.WrongExceptionThrown, - userMessage, - typeof(TException), - ex.GetType()); - ThrowAssertFailed("Assert." + assertMethodName, finalMessage); - } - - return (TException)ex; + return (TException)state.ExceptionWhenNotFailing!; } - userMessage = BuildUserMessage(message, parameters); - finalMessage = string.Format( - CultureInfo.CurrentCulture, - FrameworkMessages.NoExceptionThrown, - userMessage, - typeof(TException)); - ThrowAssertFailed("Assert." + assertMethodName, finalMessage); - // This will not hit, but need it for compiler. - return null; + return null!; } /// @@ -398,7 +523,23 @@ private static async Task ThrowsExceptionAsync(Func(action, isStrictType, assertMethodName).ConfigureAwait(false); + if (state.FailAction is not null) + { + state.FailAction(BuildUserMessage(message, parameters)); + } + else + { + return (TException)state.ExceptionWhenNotFailing!; + } + + // This will not hit, but need it for compiler. + return null!; + } + + private static async Task IsThrowsAsyncFailingAsync(Func action, bool isStrictType, string assertMethodName) + where TException : Exception + { try { await action().ConfigureAwait(false); @@ -409,30 +550,88 @@ private static async Task ThrowsExceptionAsync(Func + { + string finalMessage = string.Format( + CultureInfo.CurrentCulture, + FrameworkMessages.WrongExceptionThrown, + userMessage, + typeof(TException), + ex.GetType()); + ThrowAssertFailed("Assert." + assertMethodName, finalMessage); + }); + } + + return ThrowsExceptionState.CreateFailingState(failAction: userMessage => + { + string finalMessage = string.Format( + CultureInfo.CurrentCulture, + FrameworkMessages.NoExceptionThrown, + userMessage, + typeof(TException)); + ThrowAssertFailed("Assert." + assertMethodName, finalMessage); + }); + } - return (TException)ex; + private static ThrowsExceptionState IsThrowsFailing(Action action, bool isStrictType, string assertMethodName) + where TException : Exception + { + try + { + action(); } + catch (Exception ex) + { + bool isExceptionOfType = isStrictType + ? typeof(TException) == ex.GetType() + : ex is TException; - userMessage = BuildUserMessage(message, parameters); - finalMessage = string.Format( - CultureInfo.CurrentCulture, - FrameworkMessages.NoExceptionThrown, - userMessage, - typeof(TException)); - ThrowAssertFailed("Assert." + assertMethodName, finalMessage); + return isExceptionOfType + ? ThrowsExceptionState.CreateNotFailingState(ex) + : ThrowsExceptionState.CreateFailingState(userMessage => + { + string finalMessage = string.Format( + CultureInfo.CurrentCulture, + FrameworkMessages.WrongExceptionThrown, + userMessage, + typeof(TException), + ex.GetType()); + ThrowAssertFailed("Assert." + assertMethodName, finalMessage); + }); + } - // This will not hit, but need it for compiler. - return null!; + return ThrowsExceptionState.CreateFailingState(failAction: userMessage => + { + string finalMessage = string.Format( + CultureInfo.CurrentCulture, + FrameworkMessages.NoExceptionThrown, + userMessage, + typeof(TException)); + ThrowAssertFailed("Assert." + assertMethodName, finalMessage); + }); + } + + private readonly struct ThrowsExceptionState + { + public Exception? ExceptionWhenNotFailing { get; } + + public Action? FailAction { get; } + + private ThrowsExceptionState(Exception? exceptionWhenNotFailing, Action? failAction) + { + // If the assert is failing, failAction should be non-null, and exceptionWhenNotFailing should be null. + // If the assert is not failing, exceptionWhenNotFailing should be non-null, and failAction should be null. + Debug.Assert(exceptionWhenNotFailing is null ^ failAction is null, "Exactly one of exceptionWhenNotFailing and failAction should be null."); + ExceptionWhenNotFailing = exceptionWhenNotFailing; + FailAction = failAction; + } + + public static ThrowsExceptionState CreateFailingState(Action failAction) + => new(exceptionWhenNotFailing: null, failAction); + + public static ThrowsExceptionState CreateNotFailingState(Exception exception) + => new(exception, failAction: null); } } diff --git a/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt b/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt index 3ceaf8e88e..9572aa2c29 100644 --- a/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt @@ -1,4 +1,193 @@ #nullable enable +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreEqualInterpolatedStringHandler +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreEqualInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreEqualInterpolatedStringHandler.AppendFormatted(string? value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreEqualInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreEqualInterpolatedStringHandler.AppendFormatted(T value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreEqualInterpolatedStringHandler.AppendFormatted(T value, int alignment) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreEqualInterpolatedStringHandler.AppendFormatted(T value, int alignment, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreEqualInterpolatedStringHandler.AppendFormatted(T value, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreEqualInterpolatedStringHandler.AppendLiteral(string? value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreEqualInterpolatedStringHandler.AssertAreEqualInterpolatedStringHandler() -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreEqualInterpolatedStringHandler.AssertAreEqualInterpolatedStringHandler(int literalLength, int formattedCount, System.IEquatable? expected, System.IEquatable? actual, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreEqualInterpolatedStringHandler.AssertAreEqualInterpolatedStringHandler(int literalLength, int formattedCount, TArgument? expected, TArgument? actual, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreEqualInterpolatedStringHandler.AssertAreEqualInterpolatedStringHandler(int literalLength, int formattedCount, TArgument? expected, TArgument? actual, System.Collections.Generic.IEqualityComparer? comparer, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotEqualInterpolatedStringHandler +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotEqualInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotEqualInterpolatedStringHandler.AppendFormatted(string? value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotEqualInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotEqualInterpolatedStringHandler.AppendFormatted(T value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotEqualInterpolatedStringHandler.AppendFormatted(T value, int alignment) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotEqualInterpolatedStringHandler.AppendFormatted(T value, int alignment, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotEqualInterpolatedStringHandler.AppendFormatted(T value, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotEqualInterpolatedStringHandler.AppendLiteral(string! value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotEqualInterpolatedStringHandler.AssertAreNotEqualInterpolatedStringHandler() -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotEqualInterpolatedStringHandler.AssertAreNotEqualInterpolatedStringHandler(int literalLength, int formattedCount, TArgument? notExpected, TArgument? actual, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotEqualInterpolatedStringHandler.AssertAreNotEqualInterpolatedStringHandler(int literalLength, int formattedCount, TArgument? notExpected, TArgument? actual, System.Collections.Generic.IEqualityComparer? comparer, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotSameInterpolatedStringHandler +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotSameInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotSameInterpolatedStringHandler.AppendFormatted(string? value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotSameInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotSameInterpolatedStringHandler.AppendFormatted(T value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotSameInterpolatedStringHandler.AppendFormatted(T value, int alignment) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotSameInterpolatedStringHandler.AppendFormatted(T value, int alignment, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotSameInterpolatedStringHandler.AppendFormatted(T value, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotSameInterpolatedStringHandler.AppendLiteral(string! value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotSameInterpolatedStringHandler.AssertAreNotSameInterpolatedStringHandler() -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotSameInterpolatedStringHandler.AssertAreNotSameInterpolatedStringHandler(int literalLength, int formattedCount, TArgument? notExpected, TArgument? actual, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreSameInterpolatedStringHandler +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreSameInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreSameInterpolatedStringHandler.AppendFormatted(string? value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreSameInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreSameInterpolatedStringHandler.AppendFormatted(T value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreSameInterpolatedStringHandler.AppendFormatted(T value, int alignment) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreSameInterpolatedStringHandler.AppendFormatted(T value, int alignment, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreSameInterpolatedStringHandler.AppendFormatted(T value, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreSameInterpolatedStringHandler.AppendLiteral(string! value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreSameInterpolatedStringHandler.AssertAreSameInterpolatedStringHandler() -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreSameInterpolatedStringHandler.AssertAreSameInterpolatedStringHandler(int literalLength, int formattedCount, TArgument? expected, TArgument? actual, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsInstanceOfTypeInterpolatedStringHandler +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsInstanceOfTypeInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsInstanceOfTypeInterpolatedStringHandler.AppendFormatted(string? value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsInstanceOfTypeInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsInstanceOfTypeInterpolatedStringHandler.AppendFormatted(T value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsInstanceOfTypeInterpolatedStringHandler.AppendFormatted(T value, int alignment) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsInstanceOfTypeInterpolatedStringHandler.AppendFormatted(T value, int alignment, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsInstanceOfTypeInterpolatedStringHandler.AppendFormatted(T value, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsInstanceOfTypeInterpolatedStringHandler.AppendLiteral(string! value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsInstanceOfTypeInterpolatedStringHandler.AssertGenericIsInstanceOfTypeInterpolatedStringHandler() -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsInstanceOfTypeInterpolatedStringHandler.AssertGenericIsInstanceOfTypeInterpolatedStringHandler(int literalLength, int formattedCount, object? value, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler.AppendFormatted(string? value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler.AppendFormatted(T value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler.AppendFormatted(T value, int alignment) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler.AppendFormatted(T value, int alignment, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler.AppendFormatted(T value, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler.AppendLiteral(string! value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler.AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler() -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler.AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler(int literalLength, int formattedCount, object? value, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsFalseInterpolatedStringHandler +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsFalseInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsFalseInterpolatedStringHandler.AppendFormatted(string? value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsFalseInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsFalseInterpolatedStringHandler.AppendFormatted(T value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsFalseInterpolatedStringHandler.AppendFormatted(T value, int alignment) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsFalseInterpolatedStringHandler.AppendFormatted(T value, int alignment, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsFalseInterpolatedStringHandler.AppendFormatted(T value, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsFalseInterpolatedStringHandler.AppendLiteral(string! value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsFalseInterpolatedStringHandler.AssertIsFalseInterpolatedStringHandler() -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsFalseInterpolatedStringHandler.AssertIsFalseInterpolatedStringHandler(int literalLength, int formattedCount, bool? condition, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsInstanceOfTypeInterpolatedStringHandler +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsInstanceOfTypeInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsInstanceOfTypeInterpolatedStringHandler.AppendFormatted(string? value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsInstanceOfTypeInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsInstanceOfTypeInterpolatedStringHandler.AppendFormatted(T value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsInstanceOfTypeInterpolatedStringHandler.AppendFormatted(T value, int alignment) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsInstanceOfTypeInterpolatedStringHandler.AppendFormatted(T value, int alignment, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsInstanceOfTypeInterpolatedStringHandler.AppendFormatted(T value, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsInstanceOfTypeInterpolatedStringHandler.AppendLiteral(string! value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsInstanceOfTypeInterpolatedStringHandler.AssertIsInstanceOfTypeInterpolatedStringHandler() -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsInstanceOfTypeInterpolatedStringHandler.AssertIsInstanceOfTypeInterpolatedStringHandler(int literalLength, int formattedCount, object? value, System.Type? expectedType, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotInstanceOfTypeInterpolatedStringHandler +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotInstanceOfTypeInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotInstanceOfTypeInterpolatedStringHandler.AppendFormatted(string? value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotInstanceOfTypeInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotInstanceOfTypeInterpolatedStringHandler.AppendFormatted(T value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotInstanceOfTypeInterpolatedStringHandler.AppendFormatted(T value, int alignment) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotInstanceOfTypeInterpolatedStringHandler.AppendFormatted(T value, int alignment, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotInstanceOfTypeInterpolatedStringHandler.AppendFormatted(T value, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotInstanceOfTypeInterpolatedStringHandler.AppendLiteral(string! value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotInstanceOfTypeInterpolatedStringHandler.AssertIsNotInstanceOfTypeInterpolatedStringHandler() -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotInstanceOfTypeInterpolatedStringHandler.AssertIsNotInstanceOfTypeInterpolatedStringHandler(int literalLength, int formattedCount, object? value, System.Type? wrongType, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotNullInterpolatedStringHandler +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotNullInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotNullInterpolatedStringHandler.AppendFormatted(string? value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotNullInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotNullInterpolatedStringHandler.AppendFormatted(T value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotNullInterpolatedStringHandler.AppendFormatted(T value, int alignment) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotNullInterpolatedStringHandler.AppendFormatted(T value, int alignment, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotNullInterpolatedStringHandler.AppendFormatted(T value, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotNullInterpolatedStringHandler.AppendLiteral(string! value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotNullInterpolatedStringHandler.AssertIsNotNullInterpolatedStringHandler() -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotNullInterpolatedStringHandler.AssertIsNotNullInterpolatedStringHandler(int literalLength, int formattedCount, object? value, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNullInterpolatedStringHandler +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNullInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNullInterpolatedStringHandler.AppendFormatted(string? value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNullInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNullInterpolatedStringHandler.AppendFormatted(T value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNullInterpolatedStringHandler.AppendFormatted(T value, int alignment) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNullInterpolatedStringHandler.AppendFormatted(T value, int alignment, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNullInterpolatedStringHandler.AppendFormatted(T value, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNullInterpolatedStringHandler.AppendLiteral(string! value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNullInterpolatedStringHandler.AssertIsNullInterpolatedStringHandler() -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNullInterpolatedStringHandler.AssertIsNullInterpolatedStringHandler(int literalLength, int formattedCount, object? value, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsTrueInterpolatedStringHandler +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsTrueInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsTrueInterpolatedStringHandler.AppendFormatted(string? value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsTrueInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsTrueInterpolatedStringHandler.AppendFormatted(T value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsTrueInterpolatedStringHandler.AppendFormatted(T value, int alignment) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsTrueInterpolatedStringHandler.AppendFormatted(T value, int alignment, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsTrueInterpolatedStringHandler.AppendFormatted(T value, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsTrueInterpolatedStringHandler.AppendLiteral(string! value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsTrueInterpolatedStringHandler.AssertIsTrueInterpolatedStringHandler() -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsTrueInterpolatedStringHandler.AssertIsTrueInterpolatedStringHandler(int literalLength, int formattedCount, bool? condition, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler.AppendFormatted(string? value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler.AppendFormatted(T value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler.AppendFormatted(T value, int alignment) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler.AppendFormatted(T value, int alignment, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler.AppendFormatted(T value, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler.AppendLiteral(string! value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler.AssertNonGenericAreEqualInterpolatedStringHandler() -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler.AssertNonGenericAreEqualInterpolatedStringHandler(int literalLength, int formattedCount, decimal expected, decimal actual, decimal delta, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler.AssertNonGenericAreEqualInterpolatedStringHandler(int literalLength, int formattedCount, double expected, double actual, double delta, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler.AssertNonGenericAreEqualInterpolatedStringHandler(int literalLength, int formattedCount, float expected, float actual, float delta, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler.AssertNonGenericAreEqualInterpolatedStringHandler(int literalLength, int formattedCount, long expected, long actual, long delta, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler.AssertNonGenericAreEqualInterpolatedStringHandler(int literalLength, int formattedCount, string? expected, string? actual, bool ignoreCase, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler.AssertNonGenericAreEqualInterpolatedStringHandler(int literalLength, int formattedCount, string? expected, string? actual, bool ignoreCase, System.Globalization.CultureInfo? culture, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler.AppendFormatted(string? value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler.AppendFormatted(T value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler.AppendFormatted(T value, int alignment) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler.AppendFormatted(T value, int alignment, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler.AppendFormatted(T value, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler.AppendLiteral(string! value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler.AssertNonGenericAreNotEqualInterpolatedStringHandler() -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler.AssertNonGenericAreNotEqualInterpolatedStringHandler(int literalLength, int formattedCount, decimal notExpected, decimal actual, decimal delta, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler.AssertNonGenericAreNotEqualInterpolatedStringHandler(int literalLength, int formattedCount, double notExpected, double actual, double delta, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler.AssertNonGenericAreNotEqualInterpolatedStringHandler(int literalLength, int formattedCount, float notExpected, float actual, float delta, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler.AssertNonGenericAreNotEqualInterpolatedStringHandler(int literalLength, int formattedCount, long notExpected, long actual, long delta, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler.AssertNonGenericAreNotEqualInterpolatedStringHandler(int literalLength, int formattedCount, string? notExpected, string? actual, bool ignoreCase, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler.AssertNonGenericAreNotEqualInterpolatedStringHandler(int literalLength, int formattedCount, string? notExpected, string? actual, bool ignoreCase, System.Globalization.CultureInfo? culture, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonStrictThrowsInterpolatedStringHandler +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonStrictThrowsInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonStrictThrowsInterpolatedStringHandler.AppendFormatted(string? value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonStrictThrowsInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonStrictThrowsInterpolatedStringHandler.AppendFormatted(T value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonStrictThrowsInterpolatedStringHandler.AppendFormatted(T value, int alignment) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonStrictThrowsInterpolatedStringHandler.AppendFormatted(T value, int alignment, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonStrictThrowsInterpolatedStringHandler.AppendFormatted(T value, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonStrictThrowsInterpolatedStringHandler.AppendLiteral(string! value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonStrictThrowsInterpolatedStringHandler.AssertNonStrictThrowsInterpolatedStringHandler() -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonStrictThrowsInterpolatedStringHandler.AssertNonStrictThrowsInterpolatedStringHandler(int literalLength, int formattedCount, System.Action! action, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpolatedStringHandler +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpolatedStringHandler.AppendFormatted(string? value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpolatedStringHandler.AppendFormatted(T value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpolatedStringHandler.AppendFormatted(T value, int alignment) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpolatedStringHandler.AppendFormatted(T value, int alignment, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpolatedStringHandler.AppendFormatted(T value, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpolatedStringHandler.AppendLiteral(string! value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpolatedStringHandler.AssertThrowsExactlyInterpolatedStringHandler() -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpolatedStringHandler.AssertThrowsExactlyInterpolatedStringHandler(int literalLength, int formattedCount, System.Action! action, out bool shouldAppend) -> void Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataAttribute.DynamicDataAttribute(string! dynamicDataSourceName) -> void Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataAttribute.DynamicDataAttribute(string! dynamicDataSourceName, Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataSourceType dynamicDataSourceType) -> void Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataAttribute.DynamicDataAttribute(string! dynamicDataSourceName, System.Type! dynamicDataDeclaringType) -> void @@ -6,7 +195,39 @@ Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataAttribute.DynamicDataAtt Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataSourceType.AutoDetect = 2 -> Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataSourceType *REMOVED*Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataAttribute.DynamicDataAttribute(string! dynamicDataSourceName, Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataSourceType dynamicDataSourceType = Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataSourceType.Property) -> void *REMOVED*Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataAttribute.DynamicDataAttribute(string! dynamicDataSourceName, System.Type! dynamicDataDeclaringType, Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataSourceType dynamicDataSourceType = Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataSourceType.Property) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(decimal expected, decimal actual, decimal delta, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(double expected, double actual, double delta, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(float expected, float actual, float delta, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(long expected, long actual, long delta, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(string? expected, string? actual, bool ignoreCase, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(string? expected, string? actual, bool ignoreCase, System.Globalization.CultureInfo? culture, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(System.IEquatable? expected, System.IEquatable? actual, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreEqualInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(T? expected, T? actual, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreEqualInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(T? expected, T? actual, System.Collections.Generic.IEqualityComparer? comparer, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreEqualInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(decimal notExpected, decimal actual, decimal delta, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(double notExpected, double actual, double delta, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(float notExpected, float actual, float delta, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(long notExpected, long actual, long delta, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(string? notExpected, string? actual, bool ignoreCase, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(string? notExpected, string? actual, bool ignoreCase, System.Globalization.CultureInfo? culture, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(T? notExpected, T? actual, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotEqualInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(T? notExpected, T? actual, System.Collections.Generic.IEqualityComparer? comparer, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotEqualInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotSame(T? notExpected, T? actual, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotSameInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreSame(T? expected, T? actual, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreSameInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsFalse(bool condition, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsFalseInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsFalse(bool? condition, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsFalseInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInstanceOfType(object? value, System.Type? expectedType, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsInstanceOfTypeInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInstanceOfType(object? value, out T instance, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsInstanceOfTypeInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInstanceOfType(object? value, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsInstanceOfTypeInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNotInstanceOfType(object? value, System.Type? wrongType, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotInstanceOfTypeInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNotInstanceOfType(object? value, Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNotNull(object? value, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotNullInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNull(object? value, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNullInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsTrue(bool condition, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsTrueInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsTrue(bool? condition, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsTrueInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Throws(System.Action! action, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonStrictThrowsInterpolatedStringHandler message) -> TException! static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Throws(System.Action! action, string! message = "", params object![]! messageArgs) -> TException! static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsAsync(System.Func! action, string! message = "", params object![]! messageArgs) -> System.Threading.Tasks.Task! +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsExactly(System.Action! action, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpolatedStringHandler message) -> TException! static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsExactly(System.Action! action, string! message = "", params object![]! messageArgs) -> TException! static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsExactlyAsync(System.Func! action, string! message = "", params object![]! messageArgs) -> System.Threading.Tasks.Task! diff --git a/src/TestFramework/TestFramework/PublicAPI/net/PublicAPI.Shipped.txt b/src/TestFramework/TestFramework/PublicAPI/net/PublicAPI.Shipped.txt new file mode 100644 index 0000000000..7dc5c58110 --- /dev/null +++ b/src/TestFramework/TestFramework/PublicAPI/net/PublicAPI.Shipped.txt @@ -0,0 +1 @@ +#nullable enable diff --git a/src/TestFramework/TestFramework/PublicAPI/net/PublicAPI.Unshipped.txt b/src/TestFramework/TestFramework/PublicAPI/net/PublicAPI.Unshipped.txt new file mode 100644 index 0000000000..9a182b770a --- /dev/null +++ b/src/TestFramework/TestFramework/PublicAPI/net/PublicAPI.Unshipped.txt @@ -0,0 +1,33 @@ +#nullable enable +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreEqualInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreEqualInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotEqualInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotEqualInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotSameInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotSameInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreSameInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreSameInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsInstanceOfTypeInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsInstanceOfTypeInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsFalseInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsFalseInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsInstanceOfTypeInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsInstanceOfTypeInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotInstanceOfTypeInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotInstanceOfTypeInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotNullInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotNullInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNullInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNullInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsTrueInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsTrueInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonStrictThrowsInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonStrictThrowsInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value, int alignment = 0, string? format = null) -> void diff --git a/src/TestFramework/TestFramework/TestFramework.csproj b/src/TestFramework/TestFramework/TestFramework.csproj index 8f67b0069d..79ae688ade 100644 --- a/src/TestFramework/TestFramework/TestFramework.csproj +++ b/src/TestFramework/TestFramework/TestFramework.csproj @@ -14,6 +14,9 @@ + + + diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreEqualTests.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreEqualTests.cs index e059628875..4760984225 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreEqualTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreEqualTests.cs @@ -290,6 +290,166 @@ public void AreEqualUsingDynamicsDoesNotFail() #pragma warning restore IDE0004 + public void GenericAreEqual_InterpolatedString_EqualValues_ShouldPass() + { + DummyClassTrackingToStringCalls o = new(); + Assert.AreEqual(o, o, $"User-provided message: {o}"); + Verify(!o.WasToStringCalled); + } + + public async Task GenericAreEqual_InterpolatedString_DifferentValues_ShouldFail() + { + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.AreEqual(0, 1, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.AreEqual failed. Expected:<0>. Actual:<1>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + Verify(o.WasToStringCalled); + } + + public void GenericAreNotEqual_InterpolatedString_DifferentValues_ShouldPass() + { + DummyClassTrackingToStringCalls o = new(); + Assert.AreNotEqual(0, 1, $"User-provided message: {o}"); + Verify(!o.WasToStringCalled); + } + + public async Task GenericAreNotEqual_InterpolatedString_SameValues_ShouldFail() + { + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.AreNotEqual(0, 0, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.AreNotEqual failed. Expected any value except:<0>. Actual:<0>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + Verify(o.WasToStringCalled); + } + + public void FloatAreEqual_InterpolatedString_EqualValues_ShouldPass() + { + DummyClassTrackingToStringCalls o = new(); + Assert.AreEqual(1.0f, 1.1f, delta: 0.2f, $"User-provided message: {o}"); + Verify(!o.WasToStringCalled); + } + + public async Task FloatAreEqual_InterpolatedString_DifferentValues_ShouldFail() + { + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.AreEqual(1.0f, 1.1f, 0.001f, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.AreEqual failed. Expected a difference no greater than <0.001> between expected value <1> and actual value <1.1>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + Verify(o.WasToStringCalled); + } + + public void FloatAreNotEqual_InterpolatedString_DifferentValues_ShouldPass() + { + DummyClassTrackingToStringCalls o = new(); + Assert.AreNotEqual(1.0f, 1.1f, 0.001f, $"User-provided message: {o}"); + Verify(!o.WasToStringCalled); + } + + public async Task FloatAreNotEqual_InterpolatedString_SameValues_ShouldFail() + { + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.AreNotEqual(1.0f, 1.1f, 0.2f, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.AreNotEqual failed. Expected a difference greater than <0.2> between expected value <1> and actual value <1.1>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + Verify(o.WasToStringCalled); + } + + public void DecimalAreEqual_InterpolatedString_EqualValues_ShouldPass() + { + DummyClassTrackingToStringCalls o = new(); + Assert.AreEqual(1.0m, 1.1m, delta: 0.2m, $"User-provided message: {o}"); + Verify(!o.WasToStringCalled); + } + + public async Task DecimalAreEqual_InterpolatedString_DifferentValues_ShouldFail() + { + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.AreEqual(1.0m, 1.1m, 0.001m, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.AreEqual failed. Expected a difference no greater than <0.001> between expected value <1.0> and actual value <1.1>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + Verify(o.WasToStringCalled); + } + + public void DecimalAreNotEqual_InterpolatedString_DifferentValues_ShouldPass() + { + DummyClassTrackingToStringCalls o = new(); + Assert.AreNotEqual(1.0m, 1.1m, 0.001m, $"User-provided message: {o}"); + Verify(!o.WasToStringCalled); + } + + public async Task DecimalAreNotEqual_InterpolatedString_SameValues_ShouldFail() + { + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.AreNotEqual(1.0m, 1.1m, 0.2m, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.AreNotEqual failed. Expected a difference greater than <0.2> between expected value <1.0> and actual value <1.1>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + Verify(o.WasToStringCalled); + } + + public void LongAreEqual_InterpolatedString_EqualValues_ShouldPass() + { + DummyClassTrackingToStringCalls o = new(); + Assert.AreEqual(1L, 2L, delta: 1L, $"User-provided message: {o}"); + Verify(!o.WasToStringCalled); + } + + public async Task LongAreEqual_InterpolatedString_DifferentValues_ShouldFail() + { + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.AreEqual(1L, 2L, 0L, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.AreEqual failed. Expected a difference no greater than <0> between expected value <1> and actual value <2>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + Verify(o.WasToStringCalled); + } + + public void LongAreNotEqual_InterpolatedString_DifferentValues_ShouldPass() + { + DummyClassTrackingToStringCalls o = new(); + Assert.AreNotEqual(1L, 2L, 0L, $"User-provided message: {o}"); + Verify(!o.WasToStringCalled); + } + + public async Task LongAreNotEqual_InterpolatedString_SameValues_ShouldFail() + { + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.AreNotEqual(1L, 2L, 1L, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.AreNotEqual failed. Expected a difference greater than <1> between expected value <1> and actual value <2>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + Verify(o.WasToStringCalled); + } + + public void DoubleAreEqual_InterpolatedString_EqualValues_ShouldPass() + { + DummyClassTrackingToStringCalls o = new(); + Assert.AreEqual(1.0d, 1.1d, delta: 0.2d, $"User-provided message: {o}"); + Verify(!o.WasToStringCalled); + } + + public async Task DoubleAreEqual_InterpolatedString_DifferentValues_ShouldFail() + { + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.AreEqual(1.0d, 1.1d, 0.001d, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.AreEqual failed. Expected a difference no greater than <0.001> between expected value <1> and actual value <1.1>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + Verify(o.WasToStringCalled); + } + + public void DoubleAreNotEqual_InterpolatedString_DifferentValues_ShouldPass() + { + DummyClassTrackingToStringCalls o = new(); + Assert.AreNotEqual(1.0d, 1.1d, 0.001d, $"User-provided message: {o}"); + Verify(!o.WasToStringCalled); + } + + public async Task DoubleAreNotEqual_InterpolatedString_SameValues_ShouldFail() + { + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.AreNotEqual(1.0d, 1.1d, 0.2d, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.AreNotEqual failed. Expected a difference greater than <0.2> between expected value <1> and actual value <1.1>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + Verify(o.WasToStringCalled); + } + private CultureInfo? GetCultureInfo() => CultureInfo.CurrentCulture; private class TypeOverridesEquals diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreSame.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreSame.cs new file mode 100644 index 0000000000..eaced7c334 --- /dev/null +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreSame.cs @@ -0,0 +1,133 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#nullable enable + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Microsoft.VisualStudio.TestPlatform.TestFramework.UnitTests; + +public partial class AssertTests +{ + public void AreSame_PassSameObject_ShouldPass() + { + object o = new(); + Assert.AreSame(o, o); + } + + public void AreSame_PassDifferentObject_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreSame(new object(), new object())); + Verify(ex.Message == "Assert.AreSame failed. "); + } + + public void AreSame_StringMessage_PassSameObject_ShouldPass() + { + object o = new(); + Assert.AreSame(o, o, "User-provided message"); + } + + public void AreSame_StringMessage_PassDifferentObject_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreSame(new object(), new object(), "User-provided message")); + Verify(ex.Message == "Assert.AreSame failed. User-provided message"); + } + + public void AreSame_InterpolatedString_PassSameObject_ShouldPass() + { + DummyClassTrackingToStringCalls o = new(); + Assert.AreSame(o, o, $"User-provided message: {o}"); + Verify(!o.WasToStringCalled); + } + + public async Task AreSame_InterpolatedString_PassDifferentObject_ShouldFail() + { + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.AreSame(new object(), new object(), $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.AreSame failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + Verify(o.WasToStringCalled); + } + + public void AreSame_MessageArgs_PassSameObject_ShouldPass() + { + object o = new(); + Assert.AreSame(o, o, "User-provided message: {0}", new object().GetType()); + } + + public void AreSame_MessageArgs_PassDifferentObject_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreSame(new object(), new object(), "User-provided message: System.Object type: {0}", new object().GetType())); + Verify(ex.Message == "Assert.AreSame failed. User-provided message: System.Object type: System.Object"); + } + + public void AreNotSame_PassDifferentObject_ShouldPass() + => Assert.AreNotSame(new object(), new object()); + + public void AreSame_BothAreValueTypes_ShouldFailWithSpecializedMessage() + { + Exception ex = VerifyThrows(() => Assert.AreSame(1, 1)); + Verify(ex.Message == "Assert.AreSame failed. Do not pass value types to AreSame(). Values converted to Object will never be the same. Consider using AreEqual(). "); + } + + public void AreSame_StringMessage_BothAreValueTypes_ShouldFailWithSpecializedMessage() + { + Exception ex = VerifyThrows(() => Assert.AreSame(1, 1, "User-provided message")); + Verify(ex.Message == "Assert.AreSame failed. Do not pass value types to AreSame(). Values converted to Object will never be the same. Consider using AreEqual(). User-provided message"); + } + + public void AreSame_InterpolatedString_BothAreValueTypes_ShouldFailWithSpecializedMessage() + { + Exception ex = VerifyThrows(() => Assert.AreSame(1, 1, $"User-provided message {new object().GetType()}")); + Verify(ex.Message == "Assert.AreSame failed. Do not pass value types to AreSame(). Values converted to Object will never be the same. Consider using AreEqual(). User-provided message System.Object"); + } + + public void AreSame_MessageArgs_BothAreValueTypes_ShouldFailWithSpecializedMessage() + { + Exception ex = VerifyThrows(() => Assert.AreSame(1, 1, "User-provided message {0}", new object().GetType())); + Verify(ex.Message == "Assert.AreSame failed. Do not pass value types to AreSame(). Values converted to Object will never be the same. Consider using AreEqual(). User-provided message System.Object"); + } + + public void AreNotSame_PassSameObject_ShouldFail() + { + object o = new(); + Exception ex = VerifyThrows(() => Assert.AreNotSame(o, o)); + Verify(ex.Message == "Assert.AreNotSame failed. "); + } + + public void AreNotSame_StringMessage_PassDifferentObject_ShouldPass() + => Assert.AreNotSame(new object(), new object(), "User-provided message"); + + public void AreNotSame_StringMessage_PassSameObject_ShouldFail() + { + object o = new(); + Exception ex = VerifyThrows(() => Assert.AreNotSame(o, o, "User-provided message")); + Verify(ex.Message == "Assert.AreNotSame failed. User-provided message"); + } + + public void AreNotSame_InterpolatedString_PassDifferentObject_ShouldPass() + { + DummyClassTrackingToStringCalls o = new(); + Assert.AreNotSame(new object(), new object(), $"User-provided message: {o}"); + Verify(!o.WasToStringCalled); + } + + public async Task AreNotSame_InterpolatedString_PassSameObject_ShouldFail() + { + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.AreNotSame(o, o, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.AreNotSame failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + Verify(o.WasToStringCalled); + } + + public void AreNotSame_MessageArgs_PassDifferentObject_ShouldPass() + => Assert.AreNotSame(new object(), new object(), "User-provided message: {0}", new object().GetType()); + + public void AreNotSame_MessageArgs_PassSameObject_ShouldFail() + { + object o = new(); + Exception ex = VerifyThrows(() => Assert.AreNotSame(o, o, "User-provided message: System.Object type: {0}", new object().GetType())); + Verify(ex.Message == "Assert.AreNotSame failed. User-provided message: System.Object type: System.Object"); + } +} diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsInstanceOfTypeTests.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsInstanceOfTypeTests.cs index fc2d51bf56..4ec85f34d3 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsInstanceOfTypeTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsInstanceOfTypeTests.cs @@ -10,16 +10,81 @@ namespace Microsoft.VisualStudio.TestPlatform.TestFramework.UnitTests; [SuppressMessage("Usage", "CA2263:Prefer generic overload when type is known", Justification = "We want to test also the non-generic API")] public partial class AssertTests { - public void InstanceOfTypeShouldFailWhenValueIsNull() => - VerifyThrows(() => Assert.IsInstanceOfType(null, typeof(AssertTests))); + public void InstanceOfTypeShouldFailWhenValueIsNull() + { + Exception ex = VerifyThrows(() => Assert.IsInstanceOfType(null, typeof(AssertTests))); + Verify(ex.Message == "Assert.IsInstanceOfType failed. "); + } + + public void InstanceOfTypeShouldFailWhenTypeIsNull() + { + Exception ex = VerifyThrows(() => Assert.IsInstanceOfType(5, null)); + Verify(ex.Message == "Assert.IsInstanceOfType failed. "); + } - public void InstanceOfTypeShouldFailWhenTypeIsNull() => - VerifyThrows(() => Assert.IsInstanceOfType(5, null)); + public void InstanceOfTypeShouldFailWhenTypeIsMismatched() + { + Exception ex = VerifyThrows(() => Assert.IsInstanceOfType(5, typeof(string))); + Verify(ex.Message == "Assert.IsInstanceOfType failed. Expected type:. Actual type:."); + } public void InstanceOfTypeShouldPassOnSameInstance() => Assert.IsInstanceOfType(5, typeof(int)); public void InstanceOfTypeShouldPassOnHigherInstance() => Assert.IsInstanceOfType(5, typeof(object)); + public void InstanceOfType_WithStringMessage_ShouldFailWhenValueIsNull() + { + Exception ex = VerifyThrows(() => Assert.IsInstanceOfType(null, typeof(AssertTests), "User-provided message")); + Verify(ex.Message == "Assert.IsInstanceOfType failed. User-provided message"); + } + + public void InstanceOfType_WithStringMessage_ShouldFailWhenTypeIsNull() + { + Exception ex = VerifyThrows(() => Assert.IsInstanceOfType(5, null, "User-provided message")); + Verify(ex.Message == "Assert.IsInstanceOfType failed. User-provided message"); + } + + public void InstanceOfType_WithStringMessage_ShouldFailWhenTypeIsMismatched() + { + Exception ex = VerifyThrows(() => Assert.IsInstanceOfType(5, typeof(string), "User-provided message")); + Verify(ex.Message == "Assert.IsInstanceOfType failed. User-provided message Expected type:. Actual type:."); + } + + public void InstanceOfType_WithStringMessage_ShouldPassWhenTypeIsCorrect() + => Assert.IsInstanceOfType(5, typeof(int), "User-provided message"); + + public async Task InstanceOfType_WithInterpolatedString_ShouldFailWhenValueIsNull() + { + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.IsInstanceOfType(null, typeof(AssertTests), $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.IsInstanceOfType failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + Verify(o.WasToStringCalled); + } + + public void InstanceOfType_WithInterpolatedString_ShouldFailWhenTypeIsNull() + { + DummyClassTrackingToStringCalls o = new(); + Exception ex = VerifyThrows(() => Assert.IsInstanceOfType(5, null, $"User-provided message {o}")); + Verify(ex.Message == "Assert.IsInstanceOfType failed. User-provided message DummyClassTrackingToStringCalls"); + Verify(o.WasToStringCalled); + } + + public void InstanceOfType_WithInterpolatedString_ShouldFailWhenTypeIsMismatched() + { + DummyClassTrackingToStringCalls o = new(); + Exception ex = VerifyThrows(() => Assert.IsInstanceOfType(5, typeof(string), $"User-provided message {o}")); + Verify(ex.Message == "Assert.IsInstanceOfType failed. User-provided message DummyClassTrackingToStringCalls Expected type:. Actual type:."); + Verify(o.WasToStringCalled); + } + + public void InstanceOfType_WithInterpolatedString_ShouldPassWhenTypeIsCorrect() + { + DummyClassTrackingToStringCalls o = new(); + Assert.IsInstanceOfType(5, typeof(int), $"User-provided message {o}"); + Verify(!o.WasToStringCalled); + } + public void InstanceNotOfTypeShouldFailWhenValueIsNull() => Assert.IsNotInstanceOfType(null, typeof(object)); public void InstanceNotOfTypeShouldFailWhenTypeIsNull() => @@ -30,8 +95,17 @@ public void InstanceNotOfTypeShouldFailWhenTypeIsNull() => public void InstanceNotOfTypeShouldPassOnSubInstance() => Assert.IsNotInstanceOfType(new object(), typeof(int)); [TestMethod] - public void IsInstanceOfTypeUsingGenericType_WhenValueIsNull_Fails() => - VerifyThrows(() => Assert.IsInstanceOfType(null)); + public void IsInstanceOfTypeUsingGenericType_WhenValueIsNull_Fails() + { + Exception ex = VerifyThrows(() => Assert.IsInstanceOfType(null)); + Verify(ex.Message == "Assert.IsInstanceOfType failed. "); + } + + public void IsInstanceOfTypeUsingGenericType_WhenTypeMismatch_Fails() + { + Exception ex = VerifyThrows(() => Assert.IsInstanceOfType(5)); + Verify(ex.Message == "Assert.IsInstanceOfType failed. Expected type:. Actual type:."); + } [TestMethod] public void IsInstanceOfTypeUsingGenericTypeWithOutParameter_WhenValueIsNull_Fails() @@ -41,6 +115,12 @@ public void IsInstanceOfTypeUsingGenericTypeWithOutParameter_WhenValueIsNull_Fai Verify(assertTests is null); } + public void IsInstanceOfTypeUsingGenericTypeWithOutParameter_WhenTypeMismatch_Fails() + { + Exception ex = VerifyThrows(() => Assert.IsInstanceOfType(5, out _)); + Verify(ex.Message == "Assert.IsInstanceOfType failed. Expected type:. Actual type:."); + } + [TestMethod] public void IsInstanceOfTypeUsingGenericType_OnSameInstance_DoesNotThrow() => Assert.IsInstanceOfType(5); diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsNull.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsNull.cs index bec3b88580..15eb6d2249 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsNull.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsNull.cs @@ -7,10 +7,53 @@ using TestFramework.ForTestingMSTest; -namespace Microsoft.VisualStudio.TestPlatform.TestFramework.UnitTests.Assertions; +namespace Microsoft.VisualStudio.TestPlatform.TestFramework.UnitTests; public partial class AssertTests : TestContainer { + public void IsNull_PassNull_ShouldPass() + => Assert.IsNull(null); + + public void IsNull_PassNonNull_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.IsNull(new object())); + Verify(ex.Message == "Assert.IsNull failed. "); + } + + public void IsNull_StringMessage_PassNull_ShouldPass() + => Assert.IsNull(null, "User-provided message"); + + public void IsNull_StringMessage_PassNonNull_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.IsNull(new object(), "User-provided message")); + Verify(ex.Message == "Assert.IsNull failed. User-provided message"); + } + + public void IsNull_InterpolatedString_PassNull_ShouldPass() + { + DummyClassTrackingToStringCalls o = new(); + Assert.IsNull(null, $"User-provided message {o}"); + Verify(!o.WasToStringCalled); + } + + public async Task IsNull_InterpolatedString_PassNonNull_ShouldFail() + { + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.IsNull(new object(), $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.IsNull failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + Verify(o.WasToStringCalled); + } + + public void IsNull_MessageFormat_PassNull_ShouldPass() + => Assert.IsNull(null, "User-provided message {0}", new object().GetType()); + + public void IsNull_MessageFormat_PassNonNull_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.IsNull(new object(), "User-provided message {0}", new object().GetType())); + Verify(ex.Message == "Assert.IsNull failed. User-provided message System.Object"); + } + public void IsNotNull_WhenNonNullNullableValue_DoesNotThrowAndLearnNotNull() { object? obj = GetObj(); @@ -25,6 +68,15 @@ public void IsNotNull_WhenNonNullNullableValueAndMessage_DoesNotThrowAndLearnNot _ = obj.ToString(); // No potential NRE warning } + public void IsNotNull_WhenNonNullNullableValueAndInterpolatedStringMessage_DoesNotThrowAndLearnNotNull() + { + object? obj = GetObj(); + DummyClassTrackingToStringCalls o = new(); + Assert.IsNotNull(obj, $"my message {o}"); + Verify(!o.WasToStringCalled); + _ = obj.ToString(); // No potential NRE warning + } + public void IsNotNull_WhenNonNullNullableValueAndCompositeMessage_DoesNotThrowAndLearnNotNull() { object? obj = GetObj(); @@ -32,5 +84,30 @@ public void IsNotNull_WhenNonNullNullableValueAndCompositeMessage_DoesNotThrowAn _ = obj.ToString(); // No potential NRE warning } - private object? GetObj() => new(); + public void IsNotNull_PassNull_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.IsNotNull(null)); + Verify(ex.Message == "Assert.IsNotNull failed. "); + } + + public void IsNotNull_StringMessage_PassNonNull_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.IsNotNull(null, "User-provided message")); + Verify(ex.Message == "Assert.IsNotNull failed. User-provided message"); + } + + public async Task IsNotNull_InterpolatedString_PassNonNull_ShouldFail() + { + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.IsNotNull(null, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.IsNotNull failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + Verify(o.WasToStringCalled); + } + + public void IsNotNull_MessageFormat_PassNonNull_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.IsNotNull(null, "User-provided message {0}", new object().GetType())); + Verify(ex.Message == "Assert.IsNotNull failed. User-provided message System.Object"); + } } diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsTrueTests.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsTrueTests.cs index 437a0919f9..3cf8d1ea23 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsTrueTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsTrueTests.cs @@ -7,18 +7,247 @@ namespace Microsoft.VisualStudio.TestPlatform.TestFramework.UnitTests; public partial class AssertTests { - public void IsFalseNullableBooleansShouldFailWithNull() + public void IsFalseNullableBooleanShouldFailWithNull() { bool? nullBool = null; Exception ex = VerifyThrows(() => Assert.IsFalse(nullBool)); - Verify(ex.Message.Contains("Assert.IsFalse failed")); + Verify(ex.Message == "Assert.IsFalse failed. "); } - public void IsTrueNullableBooleansShouldFailWithNull() + public void IsFalseNullableBooleanShouldFailWithTrue() + { + bool? nullBool = true; + Exception ex = VerifyThrows(() => Assert.IsFalse(nullBool)); + Verify(ex.Message == "Assert.IsFalse failed. "); + } + + public void IsFalseNullableBooleanShouldNotFailWithFalse() + { + bool? nullBool = false; + Assert.IsFalse(nullBool); + } + + public void IsFalseBooleanShouldFailWithTrue() + { + Exception ex = VerifyThrows(() => Assert.IsFalse(true)); + Verify(ex.Message == "Assert.IsFalse failed. "); + } + + public void IsFalseBooleanShouldNotFailWithFalse() + => Assert.IsFalse(false); + + public void IsFalseNullableBooleanStringMessageShouldFailWithNull() { bool? nullBool = null; + Exception ex = VerifyThrows(() => Assert.IsFalse(nullBool, "User-provided message")); + Verify(ex.Message == "Assert.IsFalse failed. User-provided message"); + } + public void IsFalseNullableBooleanStringMessageShouldFailWithTrue() + { + bool? nullBool = true; + Exception ex = VerifyThrows(() => Assert.IsFalse(nullBool, "User-provided message")); + Verify(ex.Message == "Assert.IsFalse failed. User-provided message"); + } + + public void IsFalseNullableBooleanStringMessageShouldNotFailWithFalse() + { + bool? nullBool = false; + Assert.IsFalse(nullBool, "User-provided message"); + } + + public void IsFalseBooleanStringMessageShouldFailWithTrue() + { + Exception ex = VerifyThrows(() => Assert.IsFalse(true, "User-provided message")); + Verify(ex.Message == "Assert.IsFalse failed. User-provided message"); + } + + public void IsFalseBooleanStringMessageShouldNotFailWithFalse() + => Assert.IsFalse(false, "User-provided message"); + + public async Task IsFalseNullableBooleanInterpolatedStringMessageShouldFailWithNull() + { + bool? nullBool = null; + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.IsFalse(nullBool, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.IsFalse failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + } + + public async Task IsFalseNullableBooleanInterpolatedStringMessageShouldFailWithTrue() + { + bool? nullBool = true; + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.IsFalse(nullBool, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.IsFalse failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + } + + public void IsFalseNullableBooleanInterpolatedStringMessageShouldNotFailWithFalse() + { + bool? nullBool = false; + Assert.IsFalse(nullBool, $"User-provided message. Input: {nullBool}"); + } + + public async Task IsFalseBooleanInterpolatedStringMessageShouldFailWithTrue() + { + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.IsFalse(true, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.IsFalse failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + } + + public void IsFalseBooleanInterpolatedStringMessageShouldNotFailWithFalse() + => Assert.IsFalse(false, $"User-provided message. Input: {false}"); + + public void IsFalseNullableBooleanMessageArgsShouldFailWithNull() + { + bool? nullBool = null; + Exception ex = VerifyThrows(() => Assert.IsFalse(nullBool, "User-provided message. Input: {0}", nullBool)); + Verify(ex.Message == "Assert.IsFalse failed. User-provided message. Input: "); + } + + public void IsFalseNullableBooleanMessageArgsShouldFailWithTrue() + { + bool? nullBool = true; + Exception ex = VerifyThrows(() => Assert.IsFalse(nullBool, "User-provided message. Input: {0}", nullBool)); + Verify(ex.Message == "Assert.IsFalse failed. User-provided message. Input: True"); + } + + public void IsFalseNullableBooleanMessageArgsShouldNotFailWithFalse() + { + bool? nullBool = false; + Assert.IsFalse(nullBool, "User-provided message. Input: {0}", nullBool); + } + + public void IsFalseBooleanMessageArgsShouldFailWithTrue() + { + Exception ex = VerifyThrows(() => Assert.IsFalse(true, "User-provided message. Input: {0}", true)); + Verify(ex.Message == "Assert.IsFalse failed. User-provided message. Input: True"); + } + + public void IsFalseBooleanMessageArgsShouldNotFailWithFalse() + => Assert.IsFalse(false, "User-provided message. Input: {0}", false); + + public void IsTrueNullableBooleanShouldFailWithNull() + { + bool? nullBool = null; Exception ex = VerifyThrows(() => Assert.IsTrue(nullBool)); - Verify(ex.Message.Contains("Assert.IsTrue failed")); + Verify(ex.Message == "Assert.IsTrue failed. "); } + + public void IsTrueNullableBooleanShouldFailWithFalse() + { + bool? nullBool = false; + Exception ex = VerifyThrows(() => Assert.IsTrue(nullBool)); + Verify(ex.Message == "Assert.IsTrue failed. "); + } + + public void IsTrueNullableBooleanShouldNotFailWithTrue() + { + bool? nullBool = true; + Assert.IsTrue(nullBool); + } + + public void IsTrueBooleanShouldFailWithFalse() + { + Exception ex = VerifyThrows(() => Assert.IsTrue(false)); + Verify(ex.Message == "Assert.IsTrue failed. "); + } + + public void IsTrueBooleanShouldNotFailWithTrue() + => Assert.IsTrue(true); + + public void IsTrueNullableBooleanStringMessageShouldFailWithNull() + { + bool? nullBool = null; + Exception ex = VerifyThrows(() => Assert.IsTrue(nullBool, "User-provided message")); + Verify(ex.Message == "Assert.IsTrue failed. User-provided message"); + } + + public void IsTrueNullableBooleanStringMessageShouldFailWithFalse() + { + bool? nullBool = false; + Exception ex = VerifyThrows(() => Assert.IsTrue(nullBool, "User-provided message")); + Verify(ex.Message == "Assert.IsTrue failed. User-provided message"); + } + + public void IsTrueNullableBooleanStringMessageShouldNotFailWithTrue() + { + bool? nullBool = true; + Assert.IsTrue(nullBool, "User-provided message"); + } + + public void IsTrueBooleanStringMessageShouldFailWithFalse() + { + Exception ex = VerifyThrows(() => Assert.IsTrue(false, "User-provided message")); + Verify(ex.Message == "Assert.IsTrue failed. User-provided message"); + } + + public void IsTrueBooleanStringMessageShouldNotFailWithTrue() + => Assert.IsTrue(true, "User-provided message"); + + public async Task IsTrueNullableBooleanInterpolatedStringMessageShouldFailWithNull() + { + bool? nullBool = null; + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.IsTrue(nullBool, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.IsTrue failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + } + + public async Task IsTrueNullableBooleanInterpolatedStringMessageShouldFailWithFalse() + { + bool? nullBool = false; + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.IsTrue(nullBool, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.IsTrue failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + } + + public void IsTrueNullableBooleanInterpolatedStringMessageShouldNotFailWithTrue() + { + bool? nullBool = true; + Assert.IsTrue(nullBool, $"User-provided message. Input: {nullBool}"); + } + + public async Task IsTrueBooleanInterpolatedStringMessageShouldFailWithFalse() + { + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.IsTrue(false, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.IsTrue failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + } + + public void IsTrueBooleanInterpolatedStringMessageShouldNotFailWithTrue() + => Assert.IsTrue(true, $"User-provided message. Input: {true}"); + + public void IsTrueNullableBooleanMessageArgsShouldFailWithNull() + { + bool? nullBool = null; + Exception ex = VerifyThrows(() => Assert.IsTrue(nullBool, "User-provided message. Input: {0}", nullBool)); + Verify(ex.Message == "Assert.IsTrue failed. User-provided message. Input: "); + } + + public void IsTrueNullableBooleanMessageArgsShouldFailWithFalse() + { + bool? nullBool = false; + Exception ex = VerifyThrows(() => Assert.IsTrue(nullBool, "User-provided message. Input: {0}", nullBool)); + Verify(ex.Message == "Assert.IsTrue failed. User-provided message. Input: False"); + } + + public void IsTrueNullableBooleanMessageArgsShouldNotFailWithTrue() + { + bool? nullBool = true; + Assert.IsTrue(nullBool, "User-provided message. Input: {0}", nullBool); + } + + public void IsTrueBooleanMessageArgsShouldFailWithFalse() + { + Exception ex = VerifyThrows(() => Assert.IsTrue(false, "User-provided message. Input: {0}", false)); + Verify(ex.Message == "Assert.IsTrue failed. User-provided message. Input: False"); + } + + public void IsTrueBooleanMessageArgsShouldNotFailWithTrue() + => Assert.IsTrue(true, "User-provided message. Input: {0}", true); } diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.cs index 8b3584fc46..49cdeb4897 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.cs @@ -39,4 +39,24 @@ public void BuildUserMessageDoesNotThrowWhenMessageContainsInvalidStringFormatCo Verify(message == "{"); } #endregion + + private static Task GetHelloStringAsync() + => Task.FromResult("Hello"); + + private sealed class DummyClassTrackingToStringCalls + { + public bool WasToStringCalled { get; private set; } + + public override string ToString() + { + WasToStringCalled = true; + return nameof(DummyClassTrackingToStringCalls); + } + } + + private sealed class DummyIFormattable : IFormattable + { + public string ToString(string format, IFormatProvider formatProvider) + => "DummyIFormattable.ToString()"; + } } diff --git a/test/Utilities/TestFramework.ForTestingMSTest/TestContainer.cs b/test/Utilities/TestFramework.ForTestingMSTest/TestContainer.cs index 0c85b5f063..5c4cd8aff2 100644 --- a/test/Utilities/TestFramework.ForTestingMSTest/TestContainer.cs +++ b/test/Utilities/TestFramework.ForTestingMSTest/TestContainer.cs @@ -80,6 +80,26 @@ public static Exception VerifyThrows( return null; } + public static async Task VerifyThrowsAsync( + Func taskGetter, + [CallerArgumentExpression(nameof(taskGetter))] string? expression = default, + [CallerMemberName] string? caller = default, + [CallerFilePath] string? filePath = default, + [CallerLineNumber] int lineNumber = default) + { + try + { + await taskGetter(); + } + catch (Exception ex) + { + return ex; + } + + Throw(expression, caller, filePath, lineNumber); + return null; + } + public static T VerifyThrows( Action action, [CallerArgumentExpression(nameof(action))] @@ -102,6 +122,28 @@ public static T VerifyThrows( return null; } + public static async Task VerifyThrowsAsync( + Func taskGetter, + [CallerArgumentExpression(nameof(taskGetter))] + string? expression = default, + [CallerMemberName] string? caller = default, + [CallerFilePath] string? filePath = default, + [CallerLineNumber] int lineNumber = default) + where T : Exception + { + try + { + await taskGetter(); + } + catch (T ex) + { + return ex; + } + + Throw(expression, caller, filePath, lineNumber); + return null; + } + public static void Fail( [CallerMemberName] string? caller = default, [CallerFilePath] string? filePath = default,