Skip to content

Commit

Permalink
dotnet/roslyn-sdk#1099: Port XunitVerifier to use xUnit.net 2.5 asser…
Browse files Browse the repository at this point in the history
…tion library (as source)
  • Loading branch information
bradwilson committed Jun 17, 2023
1 parent 4ff97d3 commit 1b8cf8a
Show file tree
Hide file tree
Showing 7 changed files with 241 additions and 7 deletions.
30 changes: 30 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
Unless otherwise noted, the source code here is covered by the following license:

Copyright (c) .NET Foundation and Contributors
All Rights Reserved

Expand All @@ -12,3 +14,31 @@ distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

The source in src/xunit.analyzers.tests/Utilities/XunitVerifier.cs was adapted from code
that is covered the following license (copied from
https://github.com/dotnet/roslyn-sdk/blob/35d5e46fd5c403194692c645d912a17d36ed74f5/LICENSE.txt):

The MIT License (MIT)

Copyright (c) .NET Foundation and Contributors

All rights reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
14 changes: 14 additions & 0 deletions src/xunit.analyzers.tests/AssertsExtensions/EmptyException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;

namespace Xunit.Sdk;

internal partial class EmptyException
{
public static EmptyException ForNamedNonEmptyCollection(
string collection,
string collectionName) =>
new(
$"Assert.Empty() Failure: Collection '{collectionName}' was not empty" + Environment.NewLine +
"Collection: " + collection
);
}
23 changes: 23 additions & 0 deletions src/xunit.analyzers.tests/AssertsExtensions/EqualException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;

namespace Xunit.Sdk;

internal partial class EqualException
{
public static EqualException ForMismatchedValuesWithMessage(
object? expected,
object? actual,
string? message)
{
var expectedText = expected as string ?? ArgumentFormatter.Format(expected);
var actualText = actual as string ?? ArgumentFormatter.Format(actual);

if (string.IsNullOrWhiteSpace(message))
message =
"Assert.Equal() Failure: Values differ" + Environment.NewLine +
"Expected: " + expectedText.Replace(Environment.NewLine, newLineAndIndent) + Environment.NewLine +
"Actual: " + actualText.Replace(Environment.NewLine, newLineAndIndent);

return new(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Xunit.Sdk;

internal partial class NotEmptyException
{
public static NotEmptyException ForNamedNonEmptyCollection(string collectionName) =>
new($"Assert.NotEmpty() Failure: Collection '{collectionName}' was empty");
}
6 changes: 3 additions & 3 deletions src/xunit.analyzers.tests/Utilities/CSharpVerifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ public class CSharpVerifier<TAnalyzer>
where TAnalyzer : DiagnosticAnalyzer, new()
{
public static DiagnosticResult Diagnostic() =>
CSharpCodeFixVerifier<TAnalyzer, EmptyCodeFixProvider, XUnitVerifier>.Diagnostic();
CSharpCodeFixVerifier<TAnalyzer, EmptyCodeFixProvider, XunitVerifier>.Diagnostic();

public static DiagnosticResult Diagnostic(string diagnosticId) =>
CSharpCodeFixVerifier<TAnalyzer, EmptyCodeFixProvider, XUnitVerifier>.Diagnostic(diagnosticId);
CSharpCodeFixVerifier<TAnalyzer, EmptyCodeFixProvider, XunitVerifier>.Diagnostic(diagnosticId);

public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor) =>
new(descriptor);
Expand Down Expand Up @@ -98,7 +98,7 @@ public class CSharpVerifier<TAnalyzer>
return test.RunAsync();
}

public class TestV2 : CSharpCodeFixTest<TAnalyzer, EmptyCodeFixProvider, XUnitVerifier>
public class TestV2 : CSharpCodeFixTest<TAnalyzer, EmptyCodeFixProvider, XunitVerifier>
{
readonly LanguageVersion languageVersion;

Expand Down
158 changes: 158 additions & 0 deletions src/xunit.analyzers.tests/Utilities/XunitVerifier.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
// 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.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Xunit;
using Xunit.Sdk;

namespace Microsoft.CodeAnalysis.Testing.Verifiers
{
public class XunitVerifier : IVerifier
{
public XunitVerifier() :
this(ImmutableStack<string>.Empty)
{ }

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)
{
var tracker = CollectionTracker<T>.Wrap(collection);
using var enumerator = tracker.GetEnumerator();

if (enumerator.MoveNext())
throw EmptyException.ForNamedNonEmptyCollection(tracker.FormatStart(), collectionName);
}

public virtual void Equal<T>(
T expected,
T actual,
string? message = null)
{
if (message is null && Context.IsEmpty)
Assert.Equal(expected, actual);
else if (!EqualityComparer<T>.Default.Equals(expected, actual))
throw EqualException.ForMismatchedValuesWithMessage(expected, actual, CreateMessage(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));
}

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));
}

[DoesNotReturn]
public virtual void Fail(string? message = null)
{
if (message is null && Context.IsEmpty)
Assert.True(false);
else
Assert.True(false, CreateMessage(message));

throw new InvalidOperationException("This code is unreachable");
}

public virtual void LanguageIsSupported(string language)
{
Assert.False(language != LanguageNames.CSharp && language != LanguageNames.VisualBasic, CreateMessage($"Unsupported Language: '{language}'"));
}

public virtual void NotEmpty<T>(
string collectionName,
IEnumerable<T> collection)
{
using var enumerator = collection.GetEnumerator();

if (!enumerator.MoveNext())
throw NotEmptyException.ForNamedNonEmptyCollection(collectionName);
}

public virtual void SequenceEqual<T>(
IEnumerable<T> expected,
IEnumerable<T> actual,
IEqualityComparer<T>? equalityComparer = null,
string? message = null)
{
var comparer = new SequenceEqualEnumerableEqualityComparer<T>(equalityComparer);
var areEqual = comparer.Equals(expected, actual);

if (!areEqual)
throw EqualException.ForMismatchedValuesWithMessage(expected, actual, CreateMessage(message));
}

public virtual IVerifier PushContext(string context)
{
Assert.IsType<XunitVerifier>(this);

return new XunitVerifier(Context.Push(context));
}

protected virtual string CreateMessage(string? message)
{
foreach (var frame in Context)
message = "Context: " + frame + Environment.NewLine + message;

return message ?? string.Empty;
}

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

public SequenceEqualEnumerableEqualityComparer(IEqualityComparer<T>? itemEqualityComparer)
{
this.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);
}
}
}
}
10 changes: 6 additions & 4 deletions src/xunit.analyzers.tests/xunit.analyzers.tests.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<DefineConstants>$(DefineConstants);XUNIT_VISIBILITY_INTERNAL;XUNIT_NULLABLE</DefineConstants>
<NoWarn>$(NoWarn);AD0001;xUnit2007;xUnit2015</NoWarn>
<RootNamespace>Xunit.Analyzers</RootNamespace>
<TargetFrameworks>net6.0;net472</TargetFrameworks>
</PropertyGroup>
Expand All @@ -10,13 +12,13 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.XUnit" Version="1.1.1" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing" Version="1.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.2" />
<PackageReference Include="NSubstitute" Version="5.0.0" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
<PackageReference Include="xunit.assert" Version="2.4.2" />
<PackageReference Include="xunit.core" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0-pre.19" />
<PackageReference Include="xunit.assert.source" Version="2.5.0-pre.29" />
<PackageReference Include="xunit.core" Version="2.5.0-pre.29" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0-pre.20" />
</ItemGroup>

<ItemGroup>
Expand Down

0 comments on commit 1b8cf8a

Please sign in to comment.