Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 6 additions & 7 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -114,14 +114,13 @@
<!-- Tests -->
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.5.0-preview-20221003-04" />
<PackageVersion Include="Moq" Version="4.16.1" />
<PackageVersion Include="Verify.Xunit" Version="14.2.0" />
<PackageVersion Include="xunit" Version="2.4.2" />
<PackageVersion Include="xunit.analyzers" Version="1.0.0"/>
<PackageVersion Include="xunit.assert" Version="2.4.2" />
<PackageVersion Include="xunit" Version="2.9.0" />
<PackageVersion Include="xunit.analyzers" Version="1.15.0"/>
<PackageVersion Include="xunit.assert" Version="2.9.0" />
<PackageVersion Include="xunit.combinatorial" Version="1.5.25" />
<PackageVersion Include="xunit.extensibility.core" Version="2.4.2" />
<PackageVersion Include="xunit.extensibility.execution" Version="2.4.2" />
<PackageVersion Include="xunit.runner.console" Version="2.4.2" />
<PackageVersion Include="xunit.extensibility.core" Version="2.9.0" />
<PackageVersion Include="xunit.extensibility.execution" Version="2.9.0" />
<PackageVersion Include="xunit.runner.console" Version="2.9.0" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.4.5" />

<!-- Integration Tests -->
Expand Down
1 change: 0 additions & 1 deletion eng/imports/HostAgnostic.props
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@
<ItemGroup Condition="'$(IsUnitTestProject)' == 'true'">
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="Moq" />
<PackageReference Include="Verify.Xunit" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.analyzers" />
<PackageReference Include="xunit.assert" />
Expand Down
2 changes: 1 addition & 1 deletion eng/imports/UnitTests.targets
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
<Delete Files="$(TrxTestResultsFile);$(HtmlTestResultsFile)" />

<!-- xUnit writes to STDERR (test name) and STDOUT (error message, stack). STDERR gets logged as an error. -->
<Exec Command='$(TestRunner) --verbosity quiet --nologo --logger "trx;logfilename=$(TrxTestResultsFile)" --logger "html;logfilename=$(HtmlTestResultsFile)" $(TargetPath)' LogStandardErrorAsError="true" IgnoreExitCode="true">
<Exec Command='$(TestRunner) --verbosity diagnostic --nologo --logger "trx;logfilename=$(TrxTestResultsFile)" --logger "html;logfilename=$(HtmlTestResultsFile)" --logger:console;verbosity=detailed $(TargetPath)' LogStandardErrorAsError="true" IgnoreExitCode="true">
<Output TaskParameter="ExitCode" PropertyName="ExitCode" />
</Exec>

Expand Down
7 changes: 7 additions & 0 deletions tests/Common/Test/xunit.runner.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"$schema": "https://xunit.net/schema/current/xunit.runner.schema.json",
"diagnosticMessages": true,
"longRunningTestSeconds": 20,
"showLiveOutput": true,
"shadowCopy": false
}
1 change: 1 addition & 0 deletions tests/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
</ItemGroup>

<ItemGroup>
<None Include="$(MSBuildThisFileDirectory)Common\Test\xunit.runner.json" CopyToOutputDirectory="PreserveNewest" Condition="'$(IsTestProject)' == 'true'" />
<None Include="$(MSBuildThisFileDirectory)Common\Test\App.config" CopyToOutputDirectory="PreserveNewest" Condition="'$(IsUnitTestProject)' == 'true'" />
<None Include="$(MSBuildThisFileDirectory)Common\Integration\App.config" CopyToOutputDirectory="PreserveNewest" Condition="'$(IsIntegrationTestProject)' == 'true'" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// 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.md file in the project root for more information.

using System.Collections;
using System.Text;
using Microsoft;
using Microsoft.CodeAnalysis.Test.Utilities;
using Xunit.Sdk;

namespace Xunit;
Expand All @@ -9,11 +12,10 @@ internal static class AssertEx
{
public static void CollectionLength<T>(IEnumerable<T> collection, int expectedCount)
{
int actualCount = collection.Count();

if (actualCount != expectedCount)
var actualCount = collection.Count();
if (expectedCount != actualCount)
{
throw new CollectionException(collection, expectedCount, actualCount);
throw CollectionException.ForMismatchedItemCount(expectedCount, actualCount, "Collection lengths not equal.");
}
}

Expand All @@ -22,28 +24,128 @@ public static void CollectionLength(IEnumerable collection, int expectedCount)
CollectionLength(collection.Cast<object>(), expectedCount);
}

public static void SequenceSame<T>(IEnumerable<T> expected, IEnumerable<T> actual) where T : class
public static void SequenceEqual<T>(
IEnumerable<T> expected,
IEnumerable<T> actual,
IEqualityComparer<T>? comparer = null,
string? itemSeparator = null,
Func<T, string>? itemInspector = null)
{
using IEnumerator<T> expectedEnumerator = expected.GetEnumerator();
using IEnumerator<T> actualEnumerator = actual.GetEnumerator();
if (expected is null)
{
Assert.Null(actual);
}
else
{
Assert.NotNull(actual);
}

Assumes.NotNull(expected);
Assumes.NotNull(actual);

while (true)
if (!expected.SequenceEqual(actual, comparer))
{
bool nextExpected = expectedEnumerator.MoveNext();
bool nextActual = actualEnumerator.MoveNext();
Assert.Fail(GetAssertMessage(expected, actual, itemInspector: itemInspector, itemSeparator: itemSeparator));
}
}

if (nextExpected && nextActual)
public static string GetAssertMessage<T>(
IEnumerable<T> expected,
IEnumerable<T> actual,
IEqualityComparer<T>? comparer = null,
string? prefix = null,
Func<T, string>? itemInspector = null,
string? itemSeparator = null,
string? expectedValueSourcePath = null,
int expectedValueSourceLine = 0)
{
if (itemInspector is null)
{
if (typeof(T) == typeof(byte))
{
itemInspector = b => $"0x{b:X2}";
}
else
{
itemInspector = new Func<T, string>(obj => (obj is null) ? "<null>" : obj.ToString());
}
}

if (itemSeparator is null)
{
if (typeof(T) == typeof(byte))
{
itemSeparator = ", ";
}
else
{
Assert.Same(expectedEnumerator.Current, actualEnumerator.Current);
itemSeparator = "," + Environment.NewLine;
}
else if (!nextExpected && !nextActual)
}

var expectedString = string.Join(itemSeparator, expected.Take(10).Select(itemInspector));
var actualString = string.Join(itemSeparator, actual.Select(itemInspector));
var diffString = DiffUtil.DiffReport(expected, actual, itemSeparator, comparer, itemInspector);

if (DifferOnlyInWhitespace(expectedString, actualString))
{
expectedString = VisualizeWhitespace(expectedString);
actualString = VisualizeWhitespace(actualString);
diffString = VisualizeWhitespace(diffString);
}

var message = new StringBuilder();

if (!string.IsNullOrEmpty(prefix))
{
message.AppendLine(prefix);
message.AppendLine();
}

message.AppendLine("Expected:");
message.AppendLine(expectedString);
if (expected.Count() > 10)
{
message.AppendLine("... truncated ...");
}

message.AppendLine("Actual:");
message.AppendLine(actualString);
message.AppendLine("Differences:");
message.AppendLine(diffString);

return message.ToString();
}

private static bool DifferOnlyInWhitespace(IEnumerable<char> expected, IEnumerable<char> actual)
=> expected.Where(c => !char.IsWhiteSpace(c)).SequenceEqual(actual.Where(c => !char.IsWhiteSpace(c)));

private static string VisualizeWhitespace(string str)
{
var result = new StringBuilder(str.Length);

var i = 0;
while (i < str.Length)
{
var c = str[i++];
if (c == '\r' && i < str.Length && str[i] == '\n')
{
return;
result.Append("␍␊\r\n");
i++;
}
else
{
throw new XunitException("Sequences have different lengths");
result.Append(c switch
{
' ' => "·",
'\t' => "→",
'\r' => "␍\r",
'\n' => "␊\n",
_ => c,
});
}
}

return result.ToString();
}
}
Loading