Skip to content

Commit 939b77a

Browse files
authored
Various test fixes (#1633)
* Fix broken tests in WSL2 on Ubuntu * Run tests for all frameworks in single command, explicitly use VSTest mode * Add diagnostics to investigate intermittent hostname lookup failures on macOS * Add workaround to enable hang/crashdumps on macOS * Reduce hang timeout (otherwise component job is killed before dump is uploaded) * TEST: Add new tests to verify coverage diff * Revert "TEST: Add new tests to verify coverage diff" This reverts commit 28263c5. * TEST: Add test that crashes with StackOverflowException * Revert "TEST: Add test that crashes with StackOverflowException" This reverts commit 4f84d3c. * TEST: Add test that never completes * Revert "TEST: Add test that never completes" This reverts commit 251ca8d. * Fixed: unable to discover tests in WSL Based on xunit/xunit#3457 (comment)
1 parent 254b5c6 commit 939b77a

File tree

19 files changed

+229
-128
lines changed

19 files changed

+229
-128
lines changed

.github/workflows/Steeltoe.All.yml

Lines changed: 11 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@ permissions:
1717
pull-requests: write
1818

1919
env:
20+
STEELTOE_MACOS_DIAGNOSE_HOSTNAME_LOOKUP: true
2021
DOTNET_CLI_TELEMETRY_OPTOUT: 1
2122
DOTNET_NOLOGO: true
2223
SOLUTION_FILE: 'src/Steeltoe.All.sln'
2324
COMMON_TEST_ARGS: >-
24-
--no-build --configuration Release --collect "XPlat Code Coverage" --logger trx --results-directory ${{ github.workspace }}/dumps
25-
--settings coverlet.runsettings --blame-crash --blame-hang-timeout 3m
25+
--no-build --configuration Release --collect "XPlat Code Coverage" --logger trx --results-directory ${{ github.workspace }}/TestOutput
26+
--settings coverlet.runsettings --blame-crash --blame-hang-timeout 1m
2627
2728
jobs:
2829
analyze:
@@ -36,9 +37,9 @@ jobs:
3637
- os: ubuntu-latest
3738
runDockerContainers: true
3839
- os: windows-latest
39-
skipFilter: Category!=Integration
40+
skipIntegrationTests: true
4041
- os: macos-latest
41-
skipFilter: Category!=Integration
42+
skipIntegrationTests: true
4243
runs-on: ${{ matrix.os }}
4344
continue-on-error: true
4445

@@ -89,38 +90,20 @@ jobs:
8990
- name: Build solution
9091
run: dotnet build ${{ env.SOLUTION_FILE }} --no-restore --configuration Release --verbosity minimal
9192

92-
- name: Set skip filters for tests
93-
shell: bash
94-
run: |
95-
echo SKIP_FILTER_NO_MEMORY_DUMPS="${{ matrix.skipFilter && format('{0}&Category!=MemoryDumps', matrix.skipFilter) || 'Category!=MemoryDumps' }}" >> $GITHUB_ENV
96-
echo SKIP_FILTER_WITH_MEMORY_DUMPS="${{ matrix.skipFilter && format('{0}&Category=MemoryDumps', matrix.skipFilter) || 'Category=MemoryDumps' }}" >> $GITHUB_ENV
97-
98-
- name: Test (net10.0)
99-
run: dotnet test ${{ env.SOLUTION_FILE }} --framework net10.0 --filter "${{ env.SKIP_FILTER_NO_MEMORY_DUMPS }}" ${{ env.COMMON_TEST_ARGS }}
100-
101-
- name: Test (net10.0) (memory dumps)
102-
run: dotnet test ${{ env.SOLUTION_FILE }} --framework net10.0 --filter "${{ env.SKIP_FILTER_WITH_MEMORY_DUMPS }}" ${{ env.COMMON_TEST_ARGS }}
103-
104-
- name: Test (net9.0)
105-
run: dotnet test ${{ env.SOLUTION_FILE }} --framework net9.0 --filter "${{ env.SKIP_FILTER_NO_MEMORY_DUMPS }}" ${{ env.COMMON_TEST_ARGS }}
106-
107-
- name: Test (net9.0) (memory dumps)
108-
run: dotnet test ${{ env.SOLUTION_FILE }} --framework net9.0 --filter "${{ env.SKIP_FILTER_WITH_MEMORY_DUMPS }}" ${{ env.COMMON_TEST_ARGS }}
109-
110-
- name: Test (net8.0)
111-
run: dotnet test ${{ env.SOLUTION_FILE }} --framework net8.0 --filter "${{ env.SKIP_FILTER_NO_MEMORY_DUMPS }}" ${{ env.COMMON_TEST_ARGS }}
93+
- name: Test
94+
run: dotnet test ${{ env.SOLUTION_FILE }} --filter "${{ matrix.skipIntegrationTests == true && 'Category!=MemoryDumps&Category!=Integration' || 'Category!=MemoryDumps' }}" ${{ env.COMMON_TEST_ARGS }}
11295

113-
- name: Test (net8.0) (memory dumps)
114-
run: dotnet test ${{ env.SOLUTION_FILE }} --framework net8.0 --filter "${{ env.SKIP_FILTER_WITH_MEMORY_DUMPS }}" ${{ env.COMMON_TEST_ARGS }}
96+
- name: Test (memory dumps)
97+
run: dotnet test src/Management/test/Endpoint.Test --filter "Category=MemoryDumps" ${{ env.COMMON_TEST_ARGS }}
11598

11699
- name: Upload crash/hang dumps (on failure)
117100
if: ${{ failure() }}
118101
uses: actions/upload-artifact@v5
119102
with:
120103
name: FailedTestOutput-${{ matrix.os }}
121104
path: |
122-
${{ github.workspace }}/dumps/**/*.dmp
123-
${{ github.workspace }}/dumps/**/Sequence_*.xml
105+
${{ github.workspace }}/TestOutput/**/*.dmp
106+
${{ github.workspace }}/TestOutput/**/Sequence_*.xml
124107
if-no-files-found: ignore
125108

126109
- name: Report test results

.github/workflows/component-shared-workflow.yml

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,13 @@ permissions:
1919
pull-requests: write
2020

2121
env:
22+
STEELTOE_MACOS_DIAGNOSE_HOSTNAME_LOOKUP: true
2223
DOTNET_CLI_TELEMETRY_OPTOUT: 1
2324
DOTNET_NOLOGO: true
2425
SOLUTION_FILE: 'src/Steeltoe.${{ inputs.component }}.slnf'
2526
COMMON_TEST_ARGS: >-
26-
--no-build --configuration Release --collect "XPlat Code Coverage" --logger trx --results-directory ${{ github.workspace }}/dumps
27-
--settings coverlet.runsettings --blame-crash --blame-hang-timeout 3m
27+
--no-build --configuration Release --collect "XPlat Code Coverage" --logger trx --results-directory ${{ github.workspace }}/TestOutput
28+
--settings coverlet.runsettings --blame-crash --blame-hang-timeout 1m
2829
2930
jobs:
3031
build:
@@ -79,35 +80,21 @@ jobs:
7980
- name: Build solution
8081
run: dotnet build ${{ env.SOLUTION_FILE }} --no-restore --configuration Release --verbosity minimal
8182

82-
- name: Test (net10.0)
83-
run: dotnet test ${{ env.SOLUTION_FILE }} --framework net10.0 --filter "Category!=MemoryDumps" ${{ env.COMMON_TEST_ARGS }}
83+
- name: Test
84+
run: dotnet test ${{ env.SOLUTION_FILE }} --filter "Category!=MemoryDumps" ${{ env.COMMON_TEST_ARGS }}
8485

85-
- name: Test (net10.0) (memory dumps)
86+
- name: Test (memory dumps)
8687
if: ${{ inputs.component == 'Management' }}
87-
run: dotnet test ${{ env.SOLUTION_FILE }} --framework net10.0 --filter "Category=MemoryDumps" ${{ env.COMMON_TEST_ARGS }}
88-
89-
- name: Test (net9.0)
90-
run: dotnet test ${{ env.SOLUTION_FILE }} --framework net9.0 --filter "Category!=MemoryDumps" ${{ env.COMMON_TEST_ARGS }}
91-
92-
- name: Test (net9.0) (memory dumps)
93-
if: ${{ inputs.component == 'Management' }}
94-
run: dotnet test ${{ env.SOLUTION_FILE }} --framework net9.0 --filter "Category=MemoryDumps" ${{ env.COMMON_TEST_ARGS }}
95-
96-
- name: Test (net8.0)
97-
run: dotnet test ${{ env.SOLUTION_FILE }} --framework net8.0 --filter "Category!=MemoryDumps" ${{ env.COMMON_TEST_ARGS }}
98-
99-
- name: Test (net8.0) (memory dumps)
100-
if: ${{ inputs.component == 'Management' }}
101-
run: dotnet test ${{ env.SOLUTION_FILE }} --framework net8.0 --filter "Category=MemoryDumps" ${{ env.COMMON_TEST_ARGS }}
88+
run: dotnet test src/Management/test/Endpoint.Test --filter "Category=MemoryDumps" ${{ env.COMMON_TEST_ARGS }}
10289

10390
- name: Upload crash/hang dumps (on failure)
10491
if: ${{ failure() }}
10592
uses: actions/upload-artifact@v5
10693
with:
10794
name: FailedTestOutput-${{ inputs.OS }}-latest
10895
path: |
109-
${{ github.workspace }}/dumps/**/*.dmp
110-
${{ github.workspace }}/dumps/**/Sequence_*.xml
96+
${{ github.workspace }}/TestOutput/**/*.dmp
97+
${{ github.workspace }}/TestOutput/**/Sequence_*.xml
11198
if-no-files-found: ignore
11299

113100
- name: Report test results

.github/workflows/sonarcube.yml

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@ permissions:
1818
pull-requests: write
1919

2020
env:
21+
STEELTOE_MACOS_DIAGNOSE_HOSTNAME_LOOKUP: true
2122
DOTNET_CLI_TELEMETRY_OPTOUT: 1
2223
DOTNET_NOLOGO: true
2324
SOLUTION_FILE: 'src/Steeltoe.All.sln'
2425
SONAR_TEST_ARGS: >-
25-
--no-build --configuration Release --collect "XPlat Code Coverage" --logger trx --results-directory ${{ github.workspace }}
26+
--no-build --configuration Release --collect "XPlat Code Coverage" --logger trx --results-directory ${{ github.workspace }}/TestOutput
2627
--settings coverlet.runsettings -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.UseSourceLink=false
2728
2829
jobs:
@@ -79,23 +80,11 @@ jobs:
7980
- name: Build solution
8081
run: dotnet build ${{ env.SOLUTION_FILE }} --no-restore --configuration Release --verbosity minimal
8182

82-
- name: Test (net10.0)
83-
run: dotnet test ${{ env.SOLUTION_FILE }} --filter "Category!=MemoryDumps" --framework net10.0 ${{ env.SONAR_TEST_ARGS }}
83+
- name: Test
84+
run: dotnet test ${{ env.SOLUTION_FILE }} --filter "Category!=MemoryDumps" ${{ env.SONAR_TEST_ARGS }}
8485

85-
- name: Test (net10.0) (memory dumps)
86-
run: dotnet test ${{ env.SOLUTION_FILE }} --filter "Category=MemoryDumps" --framework net10.0 ${{ env.SONAR_TEST_ARGS }}
87-
88-
- name: Test (net9.0)
89-
run: dotnet test ${{ env.SOLUTION_FILE }} --filter "Category!=MemoryDumps" --framework net9.0 ${{ env.SONAR_TEST_ARGS }}
90-
91-
- name: Test (net9.0) (memory dumps)
92-
run: dotnet test ${{ env.SOLUTION_FILE }} --filter "Category=MemoryDumps" --framework net9.0 ${{ env.SONAR_TEST_ARGS }}
93-
94-
- name: Test (net8.0)
95-
run: dotnet test ${{ env.SOLUTION_FILE }} --filter "Category!=MemoryDumps" --framework net8.0 ${{ env.SONAR_TEST_ARGS }}
96-
97-
- name: Test (net8.0) (memory dumps)
98-
run: dotnet test ${{ env.SOLUTION_FILE }} --filter "Category=MemoryDumps" --framework net8.0 ${{ env.SONAR_TEST_ARGS }}
86+
- name: Test (memory dumps)
87+
run: dotnet test src/Management/test/Endpoint.Test --filter "Category=MemoryDumps" ${{ env.SONAR_TEST_ARGS }}
9988

10089
- name: End Sonar .NET scanner
10190
if: ${{ !cancelled() && steps.sonar_begin.outcome == 'success' }}

Directory.Build.targets

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
<Project>
2+
<!-- Workaround for https://github.com/microsoft/testfx/issues/7045#issuecomment-3611560686 -->
3+
<Target Name="WorkaroundMacOSDumpIssue" AfterTargets="Build"
4+
Condition="$([MSBuild]::IsOSPlatform('OSX')) AND '$(UseAppHost)' != 'false' AND '$(OutputType)' == 'Exe' AND '$(TargetFramework)' != '' AND '$(RunCommand)' != '' AND '$(RunCommand)' != 'dotnet'">
5+
<Exec Command="codesign --sign - --force --entitlements '$(MSBuildThisFileDirectory)macos-dump-entitlements.plist' '$(RunCommand)'" />
6+
</Target>
7+
28
<!--
3-
Steeltoe: This file was copied from the .NET Aspire Configuration Schema generator
9+
Steeltoe: The remainder of this file was copied from the .NET Aspire Configuration Schema generator
410
at https://github.com/dotnet/aspire/tree/cb7cc4d78f8dd2b4df1053a229493cdbf88f50df/src/Tools/ConfigurationSchemaGenerator.
511
-->
612

@@ -101,7 +107,7 @@
101107
<Exec Command="$(GeneratorCommandLine)" />
102108

103109
<ItemGroup>
104-
<FileWrites Include="$(GeneratedConfigurationSchemaOutputPath)" Condition="Exists('$(GeneratedConfigurationSchemaOutputPath)')"/>
110+
<FileWrites Include="$(GeneratedConfigurationSchemaOutputPath)" Condition="Exists('$(GeneratedConfigurationSchemaOutputPath)')" />
105111
</ItemGroup>
106112
</Target>
107113

@@ -123,7 +129,7 @@
123129
</PropertyGroup>
124130

125131
<Warning Condition="'$(CurrentConfigurationSchemaFileContent)' != '$(GeneratedConfigurationSchemaFileContent)'"
126-
Text="ConfigurationSchema.json is out of date for $(MSBuildProjectFile). Run 'dotnet build --no-incremental /p:UpdateConfigurationSchema=true' to update it." />
132+
Text="ConfigurationSchema.json is out of date for $(MSBuildProjectFile). Run 'dotnet build --no-incremental /p:UpdateConfigurationSchema=true' to update it." />
127133
</Target>
128134

129135
</Project>

macos-dump-entitlements.plist

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>com.apple.security.cs.allow-jit</key>
6+
<true/>
7+
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
8+
<true/>
9+
<key>com.apple.security.cs.disable-library-validation</key>
10+
<true/>
11+
<key>com.apple.security.cs.debugger</key>
12+
<true/>
13+
<key>com.apple.security.get-task-allow</key>
14+
<true/>
15+
</dict>
16+
</plist>

shared-test.props

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
11
<Project>
22
<PropertyGroup>
33
<OutputType>Exe</OutputType>
4-
<NoWarn>$(NoWarn);S2094;S3717;SA1602;CA1062;CA1707;NU5104</NoWarn>
5-
</PropertyGroup>
6-
7-
<PropertyGroup>
8-
<!-- https://github.com/xunit/xunit/issues/3238#issuecomment-2770720936 -->
94
<TestTfmsInParallel>false</TestTfmsInParallel>
10-
<DisableTestingPlatformServerCapability>true</DisableTestingPlatformServerCapability>
5+
<NoWarn>$(NoWarn);S2094;S3717;SA1602;CA1062;CA1707;NU5104</NoWarn>
116
</PropertyGroup>
127

138
<ItemGroup>

src/Common/src/Common/Net/DomainNameResolver.cs

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ namespace Steeltoe.Common.Net;
99

1010
internal sealed class DomainNameResolver : IDomainNameResolver
1111
{
12+
private static readonly bool IsInDiagnosticsMode = Environment.GetEnvironmentVariable("STEELTOE_MACOS_DIAGNOSE_HOSTNAME_LOOKUP") == "true";
13+
1214
public static DomainNameResolver Instance { get; } = new();
1315

1416
private DomainNameResolver()
@@ -42,22 +44,44 @@ private DomainNameResolver()
4244

4345
public string? ResolveHostName(bool throwOnError = false)
4446
{
47+
// Gather diagnostic information to investigate intermittent failures on macOS.
48+
string? resultFromGetHostName = null;
49+
string? resultFromGetHostEntry = null;
50+
bool? workaroundApplied = null;
51+
4552
try
4653
{
4754
string hostName = Dns.GetHostName();
55+
resultFromGetHostName = hostName;
4856

4957
if (string.IsNullOrEmpty(hostName))
5058
{
5159
// Workaround for failure when running on macOS.
5260
// See https://github.com/actions/runner-images/issues/1335 and https://github.com/dotnet/runtime/issues/36849.
61+
5362
hostName = "localhost";
63+
workaroundApplied = true;
5464
}
5565

5666
IPHostEntry hostEntry = Dns.GetHostEntry(hostName);
57-
return hostEntry.HostName;
67+
resultFromGetHostEntry = hostEntry.HostName;
68+
69+
if (IsInDiagnosticsMode && string.IsNullOrEmpty(resultFromGetHostEntry))
70+
{
71+
throw new InvalidOperationException($"IPHostEntry.HostName returned {GetTextFor(resultFromGetHostEntry)}.");
72+
}
73+
74+
return resultFromGetHostEntry;
5875
}
59-
catch (Exception)
76+
catch (Exception exception)
6077
{
78+
if (IsInDiagnosticsMode)
79+
{
80+
throw new InvalidOperationException(
81+
$"Failed to resolve hostname. GetHostName={GetTextFor(resultFromGetHostName)}, GetHostEntry={GetTextFor(resultFromGetHostEntry)}, WorkaroundApplied={workaroundApplied}",
82+
exception);
83+
}
84+
6185
if (throwOnError)
6286
{
6387
throw;
@@ -66,4 +90,14 @@ private DomainNameResolver()
6690
return null;
6791
}
6892
}
93+
94+
private static string GetTextFor(string? value)
95+
{
96+
if (value == null)
97+
{
98+
return "(null)";
99+
}
100+
101+
return value.Length == 0 ? "(empty)" : value;
102+
}
69103
}

src/Common/test/TestResources/FluentAssertionsExtensions.cs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,48 @@ private static string ToJsonString(JsonDocument document)
5252
writer.Flush();
5353
return Encoding.UTF8.GetString(stream.ToArray());
5454
}
55+
56+
/// <summary>
57+
/// Same as the built-in Be() method, but allows specifying a custom comparer.
58+
/// </summary>
59+
/// <param name="source">
60+
/// The source text to assert on.
61+
/// </param>
62+
/// <param name="expected">
63+
/// The expected text.
64+
/// </param>
65+
/// <param name="comparer">
66+
/// An equality comparer to compare values.
67+
/// </param>
68+
[CustomAssertion]
69+
public static void Be(this StringAssertions source, string expected, IEqualityComparer<string> comparer)
70+
{
71+
ArgumentNullException.ThrowIfNull(source);
72+
ArgumentNullException.ThrowIfNull(expected);
73+
ArgumentNullException.ThrowIfNull(comparer);
74+
75+
object subject = source.Subject;
76+
subject.Should().Be(expected, comparer);
77+
}
78+
79+
/// <summary>
80+
/// Same as the built-in Contain() method, but normalizes line endings upfront.
81+
/// </summary>
82+
/// <param name="source">
83+
/// The source text to assert on.
84+
/// </param>
85+
/// <param name="expected">
86+
/// The expected text.
87+
/// </param>
88+
[CustomAssertion]
89+
public static void ContainLines(this StringAssertions source, string expected)
90+
{
91+
ArgumentNullException.ThrowIfNull(source);
92+
ArgumentNullException.ThrowIfNull(expected);
93+
94+
string sourceText = source.Subject.ReplaceLineEndings();
95+
string expectedText = expected.ReplaceLineEndings();
96+
97+
sourceText.Should().Contain(expectedText);
98+
}
5599
}

src/Discovery/src/Consul/Registry/ConsulRegistration.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ internal sealed class ConsulRegistration : IServiceInstance
3535
public bool IsSecure => _optionsMonitor.CurrentValue.EffectiveScheme == "https";
3636

3737
/// <inheritdoc />
38-
public Uri Uri => new($"{_optionsMonitor.CurrentValue.EffectiveScheme}://{Host}:{Port}");
38+
public Uri Uri => FormatUri();
3939

4040
/// <inheritdoc />
4141
public Uri? NonSecureUri => IsSecure ? null : Uri;
@@ -73,6 +73,20 @@ internal ConsulRegistration(AgentServiceRegistration innerRegistration, IOptions
7373
Metadata = innerRegistration.Meta.AsReadOnly();
7474
}
7575

76+
private Uri FormatUri()
77+
{
78+
string scheme = _optionsMonitor.CurrentValue.EffectiveScheme;
79+
80+
try
81+
{
82+
return new Uri($"{scheme}://{Host}:{Port}");
83+
}
84+
catch (UriFormatException exception)
85+
{
86+
throw new UriFormatException($"Failed to build URI from components. Scheme={scheme}, Host={Host},Port={Port}.", exception);
87+
}
88+
}
89+
7690
/// <summary>
7791
/// Creates a registration for the currently running app, to be submitted to the Consul server.
7892
/// </summary>

src/Logging/test/DynamicConsole.Test/DynamicConsoleLoggerProviderTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,7 @@ public async Task CanUseJsonFormatterWithScopes()
504504
]
505505
}
506506
507-
""");
507+
""", IgnoreLineEndingsComparer.Instance);
508508
}
509509

510510
[Fact]

0 commit comments

Comments
 (0)