Skip to content

Commit

Permalink
Avoid a direct xunit dependency due to binary compatibility concerns
Browse files Browse the repository at this point in the history
  • Loading branch information
sharwell committed Feb 13, 2024
1 parent fbcd0e8 commit 578c7eb
Show file tree
Hide file tree
Showing 7 changed files with 32 additions and 229 deletions.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@
<PackageTags>Roslyn Test Verifiers for xUnit</PackageTags>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="xunit.assert" Version="2.3.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Microsoft.CodeAnalysis.Analyzer.Testing\Microsoft.CodeAnalysis.Analyzer.Testing.csproj" />
</ItemGroup>
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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<string>
Microsoft.CodeAnalysis.Testing.Verifiers.XUnitVerifier.XUnitVerifier() -> void
Expand All @@ -19,6 +12,3 @@ virtual Microsoft.CodeAnalysis.Testing.Verifiers.XUnitVerifier.NotEmpty<T>(strin
virtual Microsoft.CodeAnalysis.Testing.Verifiers.XUnitVerifier.PushContext(string context) -> Microsoft.CodeAnalysis.Testing.IVerifier
virtual Microsoft.CodeAnalysis.Testing.Verifiers.XUnitVerifier.SequenceEqual<T>(System.Collections.Generic.IEnumerable<T> expected, System.Collections.Generic.IEnumerable<T> actual, System.Collections.Generic.IEqualityComparer<T> 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
Original file line number Diff line number Diff line change
Expand Up @@ -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<string>.Empty)
: this(ImmutableStack<string>.Empty, new DefaultVerifier())
{
}

protected XUnitVerifier(ImmutableStack<string> context)
{
Context = context ?? throw new ArgumentNullException(nameof(context));
}

protected ImmutableStack<string> Context { get; }

public virtual void Empty<T>(string collectionName, IEnumerable<T> 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>(T expected, T actual, string? message = null)
private XUnitVerifier(ImmutableStack<string> context, DefaultVerifier defaultVerifier)
{
if (message is null && Context.IsEmpty)
{
Assert.Equal(expected, actual);
}
else
{
if (!EqualityComparer<T>.Default.Equals(expected, actual))
{
throw new EqualWithMessageException(expected, actual, CreateMessage(message));
}
}
Context = context;
_defaultVerifer = defaultVerifier;
}

protected ImmutableStack<string> Context { get; }

public virtual void Empty<T>(string collectionName, IEnumerable<T> collection)
=> _defaultVerifer.Empty(collectionName, collection);

public virtual void Equal<T>(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<T>(string collectionName, IEnumerable<T> collection)
{
using (var enumerator = collection.GetEnumerator())
{
if (!enumerator.MoveNext())
{
throw new NotEmptyWithMessageException(CreateMessage($"'{collectionName}' is empty"));
}
}
}
=> _defaultVerifer.NotEmpty(collectionName, collection);

public virtual void SequenceEqual<T>(IEnumerable<T> expected, IEnumerable<T> actual, IEqualityComparer<T>? equalityComparer = null, string? message = null)
=> _defaultVerifer.SequenceEqual(expected, actual, equalityComparer, message);

public virtual IVerifier PushContext(string context)
{
var comparer = new SequenceEqualEnumerableEqualityComparer<T>(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<XUnitVerifier>(this);
return new XUnitVerifier(Context.Push(context));
return new XUnitVerifier(Context.Push(context), (DefaultVerifier)_defaultVerifer.PushContext(context));
}

protected virtual string CreateMessage(string? message)
Expand All @@ -131,41 +85,5 @@ protected virtual string CreateMessage(string? message)

return message ?? string.Empty;
}

private sealed class SequenceEqualEnumerableEqualityComparer<T> : IEqualityComparer<IEnumerable<T>?>
{
private readonly IEqualityComparer<T> _itemEqualityComparer;

public SequenceEqualEnumerableEqualityComparer(IEqualityComparer<T>? itemEqualityComparer)
{
_itemEqualityComparer = itemEqualityComparer ?? EqualityComparer<T>.Default;
}

public bool Equals(IEnumerable<T>? x, IEnumerable<T>? 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<T>? obj)
{
if (obj is null)
{
return 0;
}

// From System.Tuple
//
// The suppression is required due to an invalid contract in IEqualityComparer<T>
// https://github.com/dotnet/runtime/issues/30998
return obj
.Select(item => _itemEqualityComparer.GetHashCode(item!))
.Aggregate(
0,
(aggHash, nextHash) => ((aggHash << 5) + aggHash) ^ nextHash);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,10 @@
</PropertyGroup>

<ItemGroup>
<Compile Include="..\..\..\src\Microsoft.CodeAnalysis.Testing\Microsoft.CodeAnalysis.Analyzer.Testing\DefaultVerifier.cs" Link="DefaultVerifier.cs" />
<Compile Include="..\..\..\src\Microsoft.CodeAnalysis.Testing\Microsoft.CodeAnalysis.Analyzer.Testing\ExceptionUtilities.cs" Link="ExceptionUtilities.cs" />
<Compile Include="..\..\..\src\Microsoft.CodeAnalysis.Testing\Microsoft.CodeAnalysis.Analyzer.Testing\Extensions\IVerifierExtensions.cs" Link="IVerifierExtensions.cs" />
<Compile Include="..\..\..\src\Microsoft.CodeAnalysis.Testing\Microsoft.CodeAnalysis.Analyzer.Testing\IVerifier.cs" Link="IVerifier.cs" />
<Compile Include="..\..\..\src\Microsoft.CodeAnalysis.Testing\Microsoft.CodeAnalysis.Testing.Verifiers.XUnit\EmptyWithMessageException.cs" Link="EmptyWithMessageException.cs" />
<Compile Include="..\..\..\src\Microsoft.CodeAnalysis.Testing\Microsoft.CodeAnalysis.Testing.Verifiers.XUnit\EqualWithMessageException.cs" Link="EqualWithMessageException.cs" />
<Compile Include="..\..\..\src\Microsoft.CodeAnalysis.Testing\Microsoft.CodeAnalysis.Testing.Verifiers.XUnit\NotEmptyWithMessageException.cs" Link="NotEmptyWithMessageException.cs" />
<Compile Include="..\..\..\src\Microsoft.CodeAnalysis.Testing\Microsoft.CodeAnalysis.Testing.Verifiers.XUnit\XUnitVerifier.cs" Link="XUnitVerifier.cs" />
</ItemGroup>

Expand Down

0 comments on commit 578c7eb

Please sign in to comment.