From 578c7eb7139c48191cada915de432e02417b8b46 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Tue, 6 Feb 2024 11:28:01 -0600 Subject: [PATCH] Avoid a direct xunit dependency due to binary compatibility concerns Closes #1099 --- .../EmptyWithMessageException.cs | 32 ---- .../EqualWithMessageException.cs | 37 ----- ...odeAnalysis.Testing.Verifiers.XUnit.csproj | 4 - .../NotEmptyWithMessageException.cs | 30 ---- .../PublicAPI.Unshipped.txt | 10 -- .../XUnitVerifier.cs | 144 ++++-------------- .../Roslyn.SDK.IntegrationTests.csproj | 4 +- 7 files changed, 32 insertions(+), 229 deletions(-) delete mode 100644 src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/EmptyWithMessageException.cs delete mode 100644 src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/EqualWithMessageException.cs delete mode 100644 src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/NotEmptyWithMessageException.cs diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/EmptyWithMessageException.cs b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/EmptyWithMessageException.cs deleted file mode 100644 index d91b9519d..000000000 --- a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/EmptyWithMessageException.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections; -using Xunit.Sdk; - -namespace Microsoft.CodeAnalysis.Testing.Verifiers -{ - public class EmptyWithMessageException : EmptyException - { - public EmptyWithMessageException(IEnumerable collection, string userMessage) - : base(collection) - { - UserMessage = userMessage; - } - - public override string Message - { - get - { - if (string.IsNullOrEmpty(UserMessage)) - { - return base.Message; - } - - return UserMessage + Environment.NewLine + base.Message; - } - } - } -} diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/EqualWithMessageException.cs b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/EqualWithMessageException.cs deleted file mode 100644 index 3a14e78ed..000000000 --- a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/EqualWithMessageException.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using Xunit.Sdk; - -namespace Microsoft.CodeAnalysis.Testing.Verifiers -{ - public class EqualWithMessageException : EqualException - { - public EqualWithMessageException(object? expected, object? actual, string userMessage) - : base(expected, actual) - { - UserMessage = userMessage; - } - - public EqualWithMessageException(string? expected, string? actual, int expectedIndex, int actualIndex, string userMessage) - : base(expected, actual, expectedIndex, actualIndex) - { - UserMessage = userMessage; - } - - public override string Message - { - get - { - if (string.IsNullOrEmpty(UserMessage)) - { - return base.Message; - } - - return UserMessage + Environment.NewLine + base.Message; - } - } - } -} diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit.csproj b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit.csproj index 2947cb5d1..e9eab7295 100644 --- a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit.csproj +++ b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit.csproj @@ -13,10 +13,6 @@ Roslyn Test Verifiers for xUnit - - - - diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/NotEmptyWithMessageException.cs b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/NotEmptyWithMessageException.cs deleted file mode 100644 index 9204a8b22..000000000 --- a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/NotEmptyWithMessageException.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using Xunit.Sdk; - -namespace Microsoft.CodeAnalysis.Testing.Verifiers -{ - public class NotEmptyWithMessageException : NotEmptyException - { - public NotEmptyWithMessageException(string userMessage) - { - UserMessage = userMessage; - } - - public override string Message - { - get - { - if (string.IsNullOrEmpty(UserMessage)) - { - return base.Message; - } - - return UserMessage + Environment.NewLine + base.Message; - } - } - } -} diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/PublicAPI.Unshipped.txt b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/PublicAPI.Unshipped.txt index 2745b0193..3b935e8fd 100644 --- a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/PublicAPI.Unshipped.txt +++ b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/PublicAPI.Unshipped.txt @@ -1,10 +1,3 @@ -Microsoft.CodeAnalysis.Testing.Verifiers.EmptyWithMessageException -Microsoft.CodeAnalysis.Testing.Verifiers.EmptyWithMessageException.EmptyWithMessageException(System.Collections.IEnumerable collection, string userMessage) -> void -Microsoft.CodeAnalysis.Testing.Verifiers.EqualWithMessageException -Microsoft.CodeAnalysis.Testing.Verifiers.EqualWithMessageException.EqualWithMessageException(object expected, object actual, string userMessage) -> void -Microsoft.CodeAnalysis.Testing.Verifiers.EqualWithMessageException.EqualWithMessageException(string expected, string actual, int expectedIndex, int actualIndex, string userMessage) -> void -Microsoft.CodeAnalysis.Testing.Verifiers.NotEmptyWithMessageException -Microsoft.CodeAnalysis.Testing.Verifiers.NotEmptyWithMessageException.NotEmptyWithMessageException(string userMessage) -> void Microsoft.CodeAnalysis.Testing.Verifiers.XUnitVerifier Microsoft.CodeAnalysis.Testing.Verifiers.XUnitVerifier.Context.get -> System.Collections.Immutable.ImmutableStack Microsoft.CodeAnalysis.Testing.Verifiers.XUnitVerifier.XUnitVerifier() -> void @@ -19,6 +12,3 @@ virtual Microsoft.CodeAnalysis.Testing.Verifiers.XUnitVerifier.NotEmpty(strin virtual Microsoft.CodeAnalysis.Testing.Verifiers.XUnitVerifier.PushContext(string context) -> Microsoft.CodeAnalysis.Testing.IVerifier virtual Microsoft.CodeAnalysis.Testing.Verifiers.XUnitVerifier.SequenceEqual(System.Collections.Generic.IEnumerable expected, System.Collections.Generic.IEnumerable actual, System.Collections.Generic.IEqualityComparer equalityComparer = null, string message = null) -> void virtual Microsoft.CodeAnalysis.Testing.Verifiers.XUnitVerifier.True(bool assert, string message = null) -> void -override Microsoft.CodeAnalysis.Testing.Verifiers.EmptyWithMessageException.Message.get -> string -override Microsoft.CodeAnalysis.Testing.Verifiers.EqualWithMessageException.Message.get -> string -override Microsoft.CodeAnalysis.Testing.Verifiers.NotEmptyWithMessageException.Message.get -> string diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/XUnitVerifier.cs b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/XUnitVerifier.cs index 76279fbd4..64aa7068b 100644 --- a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/XUnitVerifier.cs +++ b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/XUnitVerifier.cs @@ -7,119 +7,73 @@ using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Linq; -using Xunit; namespace Microsoft.CodeAnalysis.Testing.Verifiers { public class XUnitVerifier : IVerifier { + private readonly DefaultVerifier _defaultVerifer; + public XUnitVerifier() - : this(ImmutableStack.Empty) + : this(ImmutableStack.Empty, new DefaultVerifier()) { } protected XUnitVerifier(ImmutableStack context) { Context = context ?? throw new ArgumentNullException(nameof(context)); - } - - protected ImmutableStack Context { get; } - public virtual void Empty(string collectionName, IEnumerable collection) - { - using (var enumerator = collection.GetEnumerator()) + // Construct an equivalent DefaultVerifier from the provided context + var defaultVerifier = new DefaultVerifier(); + foreach (var frame in context.Reverse()) { - if (enumerator.MoveNext()) - { - throw new EmptyWithMessageException(collection, CreateMessage($"'{collectionName}' is not empty")); - } + defaultVerifier = (DefaultVerifier)defaultVerifier.PushContext(frame); } + + _defaultVerifer = defaultVerifier; } - public virtual void Equal(T expected, T actual, string? message = null) + private XUnitVerifier(ImmutableStack context, DefaultVerifier defaultVerifier) { - if (message is null && Context.IsEmpty) - { - Assert.Equal(expected, actual); - } - else - { - if (!EqualityComparer.Default.Equals(expected, actual)) - { - throw new EqualWithMessageException(expected, actual, CreateMessage(message)); - } - } + Context = context; + _defaultVerifer = defaultVerifier; } + protected ImmutableStack Context { get; } + + public virtual void Empty(string collectionName, IEnumerable collection) + => _defaultVerifer.Empty(collectionName, collection); + + public virtual void Equal(T expected, T actual, string? message = null) + => _defaultVerifer.Equal(expected, actual, message); + public virtual void True([DoesNotReturnIf(false)] bool assert, string? message = null) - { - if (message is null && Context.IsEmpty) - { - Assert.True(assert); - } - else - { - Assert.True(assert, CreateMessage(message)); - } - } + => _defaultVerifer.True(assert, message); public virtual void False([DoesNotReturnIf(true)] bool assert, string? message = null) - { - if (message is null && Context.IsEmpty) - { - Assert.False(assert); - } - else - { - Assert.False(assert, CreateMessage(message)); - } - } + => _defaultVerifer.False(assert, message); [DoesNotReturn] public virtual void Fail(string? message = null) - { - if (message is null && Context.IsEmpty) - { - Assert.True(false); - } - else - { - Assert.True(false, CreateMessage(message)); - } - - throw ExceptionUtilities.Unreachable; - } + => _defaultVerifer.Fail(message); public virtual void LanguageIsSupported(string language) - { - Assert.False(language != LanguageNames.CSharp && language != LanguageNames.VisualBasic, CreateMessage($"Unsupported Language: '{language}'")); - } + => _defaultVerifer.LanguageIsSupported(language); public virtual void NotEmpty(string collectionName, IEnumerable collection) - { - using (var enumerator = collection.GetEnumerator()) - { - if (!enumerator.MoveNext()) - { - throw new NotEmptyWithMessageException(CreateMessage($"'{collectionName}' is empty")); - } - } - } + => _defaultVerifer.NotEmpty(collectionName, collection); public virtual void SequenceEqual(IEnumerable expected, IEnumerable actual, IEqualityComparer? equalityComparer = null, string? message = null) + => _defaultVerifer.SequenceEqual(expected, actual, equalityComparer, message); + + public virtual IVerifier PushContext(string context) { - var comparer = new SequenceEqualEnumerableEqualityComparer(equalityComparer); - var areEqual = comparer.Equals(expected, actual); - if (!areEqual) + if (GetType() != typeof(XUnitVerifier)) { - throw new EqualWithMessageException(expected, actual, CreateMessage(message)); + throw new InvalidOperationException($"'{nameof(PushContext)}' must be overridden to support types derived from '{typeof(XUnitVerifier)}'"); } - } - public virtual IVerifier PushContext(string context) - { - Assert.IsType(this); - return new XUnitVerifier(Context.Push(context)); + return new XUnitVerifier(Context.Push(context), (DefaultVerifier)_defaultVerifer.PushContext(context)); } protected virtual string CreateMessage(string? message) @@ -131,41 +85,5 @@ protected virtual string CreateMessage(string? message) return message ?? string.Empty; } - - private sealed class SequenceEqualEnumerableEqualityComparer : IEqualityComparer?> - { - private readonly IEqualityComparer _itemEqualityComparer; - - public SequenceEqualEnumerableEqualityComparer(IEqualityComparer? itemEqualityComparer) - { - _itemEqualityComparer = itemEqualityComparer ?? EqualityComparer.Default; - } - - public bool Equals(IEnumerable? x, IEnumerable? y) - { - if (ReferenceEquals(x, y)) { return true; } - if (x is null || y is null) { return false; } - - return x.SequenceEqual(y, _itemEqualityComparer); - } - - public int GetHashCode(IEnumerable? obj) - { - if (obj is null) - { - return 0; - } - - // From System.Tuple - // - // The suppression is required due to an invalid contract in IEqualityComparer - // https://github.com/dotnet/runtime/issues/30998 - return obj - .Select(item => _itemEqualityComparer.GetHashCode(item!)) - .Aggregate( - 0, - (aggHash, nextHash) => ((aggHash << 5) + aggHash) ^ nextHash); - } - } } } diff --git a/tests/VisualStudio.Roslyn.SDK/Roslyn.SDK.IntegrationTests/Roslyn.SDK.IntegrationTests.csproj b/tests/VisualStudio.Roslyn.SDK/Roslyn.SDK.IntegrationTests/Roslyn.SDK.IntegrationTests.csproj index 88375f10b..263861a35 100644 --- a/tests/VisualStudio.Roslyn.SDK/Roslyn.SDK.IntegrationTests/Roslyn.SDK.IntegrationTests.csproj +++ b/tests/VisualStudio.Roslyn.SDK/Roslyn.SDK.IntegrationTests/Roslyn.SDK.IntegrationTests.csproj @@ -8,12 +8,10 @@ + - - -