Skip to content

Commit 5f58617

Browse files
authored
[Tests] Upgrade to xUnit v3 (#2907)
* [Tests] Upgrade to xunit v3 Upgrading xunit to v3 to see if this helps our stability issues any. * More async and stabilization * Don't install .NET 6 + 7 anymore on AppVeyor * Fix CodeQL for latest C# * Explicit .NET 9 install * Revert TestAutomaticHeartbeat approach for now * More IAsyncDisposable * IAsyncDisposable for NoConnectionException * Fix shared on aborted connection * Merge latest in * Tweak DisconnectAndReconnectThrowsConnectionExceptionSync * Buffer on abort tests * Saveeeeeee * Fix sharing, method overlaps, source info + parallelism * Add console runner * Prevent cluster tests running first from hosing the default connection * More stability * Run only net8.0 tests on AppVeyor * Cleanup + stability * Try speeding up Sentinel * More key conflict cleanup * More optimization + stability * Idle test stability with split for time * Cleanup + more stability/perf * ClusterNodeSubscriptionFailover * More stability fixes * Cleanup
1 parent 894b95e commit 5f58617

File tree

137 files changed

+2697
-3143
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

137 files changed

+2697
-3143
lines changed

.github/workflows/codeql.yml

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,18 @@ jobs:
2929

3030
steps:
3131
- name: Checkout repository
32-
uses: actions/checkout@v3
32+
uses: actions/checkout@v4
3333
with:
3434
fetch-depth: 0
3535

36+
- name: Setup .NET
37+
uses: actions/setup-dotnet@v4
38+
with:
39+
dotnet-version: '9.0.x'
40+
3641
# Initializes the CodeQL tools for scanning.
3742
- name: Initialize CodeQL
38-
uses: github/codeql-action/init@v2
43+
uses: github/codeql-action/init@v3
3944
with:
4045
languages: ${{ matrix.language }}
4146
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -45,18 +50,11 @@ jobs:
4550
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
4651
# queries: security-extended,security-and-quality
4752

48-
49-
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
50-
# If this step fails, then you should remove it and run the build manually (see below)
51-
- if: matrix.language != 'csharp'
52-
name: Autobuild
53-
uses: github/codeql-action/autobuild@v2
54-
5553
- if: matrix.language == 'csharp'
5654
name: .NET Build
5755
run: dotnet build Build.csproj -c Release /p:CI=true
5856

5957
- name: Perform CodeQL Analysis
60-
uses: github/codeql-action/analyze@v2
58+
uses: github/codeql-action/analyze@v3
6159
with:
6260
category: "/language:${{matrix.language}}"

Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<PackageProjectUrl>https://stackexchange.github.io/StackExchange.Redis/</PackageProjectUrl>
1616
<PackageLicenseExpression>MIT</PackageLicenseExpression>
1717

18-
<LangVersion>11</LangVersion>
18+
<LangVersion>13</LangVersion>
1919
<RepositoryType>git</RepositoryType>
2020
<RepositoryUrl>https://github.com/StackExchange/StackExchange.Redis/</RepositoryUrl>
2121

Directory.Packages.props

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
<PackageVersion Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" />
1616
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
1717
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
18+
<PackageVersion Include="Microsoft.Testing.Platform" Version="1.7.3" />
1819
<PackageVersion Include="Nerdbank.GitVersioning" Version="3.7.115" />
1920
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
2021
<PackageVersion Include="NSubstitute" Version="5.3.0" />
@@ -25,7 +26,8 @@
2526
<!-- For binding redirect testing, main package gets this transitively -->
2627
<PackageVersion Include="System.IO.Pipelines" Version="9.0.0" />
2728
<PackageVersion Include="System.Runtime.Caching" Version="9.0.0" />
28-
<PackageVersion Include="xunit" Version="2.9.3" />
29-
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.1" />
29+
<PackageVersion Include="xunit.v3" Version="3.0.0" />
30+
<PackageVersion Include="xunit.v3.runner.console" Version="3.0.0" />
31+
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.3" />
3032
</ItemGroup>
3133
</Project>

appveyor.yml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@ init:
66

77
install:
88
- cmd: >-
9-
choco install dotnet-6.0-sdk
10-
11-
choco install dotnet-7.0-sdk
12-
139
choco install dotnet-9.0-sdk
1410
1511
cd tests\RedisConfigs\3.0.503
@@ -71,7 +67,7 @@ nuget:
7167
disable_publish_on_pr: true
7268

7369
build_script:
74-
- ps: .\build.ps1 -PullRequestNumber "$env:APPVEYOR_PULL_REQUEST_NUMBER" -CreatePackages ($env:OS -eq "Windows_NT")
70+
- ps: .\build.ps1 -PullRequestNumber "$env:APPVEYOR_PULL_REQUEST_NUMBER" -CreatePackages ($env:OS -eq "Windows_NT") -NetCoreOnlyTests
7571

7672
test: off
7773
artifacts:

build.ps1

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ param(
33
[bool] $CreatePackages,
44
[switch] $StartServers,
55
[bool] $RunTests = $true,
6-
[string] $PullRequestNumber
6+
[string] $PullRequestNumber,
7+
[switch] $NetCoreOnlyTests
78
)
89

910
Write-Host "Run Parameters:" -ForegroundColor Cyan
@@ -29,7 +30,11 @@ if ($RunTests) {
2930
Write-Host "Servers Started." -ForegroundColor "Green"
3031
}
3132
Write-Host "Running tests: Build.csproj traversal (all frameworks)" -ForegroundColor "Magenta"
32-
dotnet test ".\Build.csproj" -c Release --no-build --logger trx
33+
if ($NetCoreOnlyTests) {
34+
dotnet test ".\Build.csproj" -c Release -f net8.0 --no-build --logger trx
35+
} else {
36+
dotnet test ".\Build.csproj" -c Release --no-build --logger trx
37+
}
3338
if ($LastExitCode -ne 0) {
3439
Write-Host "Error with tests, aborting build." -Foreground "Red"
3540
Exit 1

src/StackExchange.Redis/ConnectionMultiplexer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -535,7 +535,7 @@ static void LogWithThreadPoolStats(ILogger? log, string message, out int busyWor
535535
}
536536
try
537537
{
538-
await Task.WhenAny(task, Task.Delay(remaining)).ObserveErrors().ForAwait();
538+
await task.TimeoutAfter(remaining).ObserveErrors().ForAwait();
539539
}
540540
catch
541541
{ }

src/StackExchange.Redis/TaskExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ internal static Task<T> ObserveErrors<T>(this Task<T> task)
3434
[MethodImpl(MethodImplOptions.AggressiveInlining)]
3535
internal static ConfiguredValueTaskAwaitable<T> ForAwait<T>(this in ValueTask<T> task) => task.ConfigureAwait(false);
3636

37-
internal static void RedisFireAndForget(this Task task) => task?.ContinueWith(t => GC.KeepAlive(t.Exception), TaskContinuationOptions.OnlyOnFaulted);
37+
internal static void RedisFireAndForget(this Task task) => task?.ContinueWith(static t => GC.KeepAlive(t.Exception), TaskContinuationOptions.OnlyOnFaulted);
3838

3939
/// <summary>
4040
/// Licensed to the .NET Foundation under one or more agreements.

tests/StackExchange.Redis.Tests/AbortOnConnectFailTests.cs

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,15 @@
22
using System.Threading.Tasks;
33
using StackExchange.Redis.Tests.Helpers;
44
using Xunit;
5-
using Xunit.Abstractions;
65

76
namespace StackExchange.Redis.Tests;
87

9-
public class AbortOnConnectFailTests : TestBase
8+
public class AbortOnConnectFailTests(ITestOutputHelper output) : TestBase(output)
109
{
11-
public AbortOnConnectFailTests(ITestOutputHelper output) : base(output) { }
12-
1310
[Fact]
14-
public void NeverEverConnectedNoBacklogThrowsConnectionNotAvailableSync()
11+
public async Task NeverEverConnectedNoBacklogThrowsConnectionNotAvailableSync()
1512
{
16-
using var conn = GetFailFastConn();
13+
await using var conn = GetFailFastConn();
1714
var db = conn.GetDatabase();
1815
var key = Me();
1916

@@ -26,7 +23,7 @@ public void NeverEverConnectedNoBacklogThrowsConnectionNotAvailableSync()
2623
[Fact]
2724
public async Task NeverEverConnectedNoBacklogThrowsConnectionNotAvailableAsync()
2825
{
29-
using var conn = GetFailFastConn();
26+
await using var conn = GetFailFastConn();
3027
var db = conn.GetDatabase();
3128
var key = Me();
3229

@@ -37,13 +34,13 @@ public async Task NeverEverConnectedNoBacklogThrowsConnectionNotAvailableAsync()
3734
}
3835

3936
[Fact]
40-
public void DisconnectAndReconnectThrowsConnectionExceptionSync()
37+
public async Task DisconnectAndReconnectThrowsConnectionExceptionSync()
4138
{
42-
using var conn = GetWorkingBacklogConn();
39+
await using var conn = GetWorkingBacklogConn();
4340

4441
var db = conn.GetDatabase();
4542
var key = Me();
46-
_ = db.Ping(); // Doesn't throw - we're connected
43+
await db.PingAsync(); // Doesn't throw - we're connected
4744

4845
// Disconnect and don't allow re-connection
4946
conn.AllowConnect = false;
@@ -54,7 +51,7 @@ public void DisconnectAndReconnectThrowsConnectionExceptionSync()
5451
var ex = Assert.ThrowsAny<Exception>(() => db.Ping());
5552
Log("Exception: " + ex.Message);
5653
Assert.True(ex is RedisConnectionException or RedisTimeoutException);
57-
Assert.StartsWith("The message timed out in the backlog attempting to send because no connection became available (400ms) - Last Connection Exception: ", ex.Message);
54+
Assert.StartsWith("The message timed out in the backlog attempting to send because no connection became available (1000ms) - Last Connection Exception: ", ex.Message);
5855
Assert.NotNull(ex.InnerException);
5956
var iex = Assert.IsType<RedisConnectionException>(ex.InnerException);
6057
Assert.Contains(iex.Message, ex.Message);
@@ -63,11 +60,11 @@ public void DisconnectAndReconnectThrowsConnectionExceptionSync()
6360
[Fact]
6461
public async Task DisconnectAndNoReconnectThrowsConnectionExceptionAsync()
6562
{
66-
using var conn = GetWorkingBacklogConn();
63+
await using var conn = GetWorkingBacklogConn();
6764

6865
var db = conn.GetDatabase();
6966
var key = Me();
70-
_ = db.Ping(); // Doesn't throw - we're connected
67+
await db.PingAsync(); // Doesn't throw - we're connected
7168

7269
// Disconnect and don't allow re-connection
7370
conn.AllowConnect = false;
@@ -77,25 +74,25 @@ public async Task DisconnectAndNoReconnectThrowsConnectionExceptionAsync()
7774
// Exception: The message timed out in the backlog attempting to send because no connection became available (400ms) - Last Connection Exception: SocketFailure (InputReaderCompleted, last-recv: 7) on 127.0.0.1:6379/Interactive, Idle/ReadAsync, last: PING, origin: SimulateConnectionFailure, outstanding: 0, last-read: 0s ago, last-write: 0s ago, keep-alive: 100s, state: ConnectedEstablished, mgr: 8 of 10 available, in: 0, in-pipe: 0, out-pipe: 0, last-heartbeat: never, last-mbeat: 0s ago, global: 0s ago, v: 2.6.120.51136, command=PING, timeout: 100, inst: 0, qu: 0, qs: 0, aw: False, bw: CheckingForTimeout, last-in: 0, cur-in: 0, sync-ops: 1, async-ops: 1, serverEndpoint: 127.0.0.1:6379, conn-sec: n/a, aoc: 0, mc: 1/1/0, mgr: 8 of 10 available, clientName: CRAVERTOP7(SE.Redis-v2.6.120.51136), IOCP: (Busy=0,Free=1000,Min=16,Max=1000), WORKER: (Busy=6,Free=32761,Min=16,Max=32767), POOL: (Threads=33,QueuedItems=0,CompletedItems=5547,Timers=60), v: 2.6.120.51136 (Please take a look at this article for some common client-side issues that can cause timeouts: https://stackexchange.github.io/StackExchange.Redis/Timeouts)
7875
var ex = await Assert.ThrowsAsync<RedisConnectionException>(() => db.PingAsync());
7976
Log("Exception: " + ex.Message);
80-
Assert.StartsWith("The message timed out in the backlog attempting to send because no connection became available (400ms) - Last Connection Exception: ", ex.Message);
77+
Assert.StartsWith("The message timed out in the backlog attempting to send because no connection became available (1000ms) - Last Connection Exception: ", ex.Message);
8178
Assert.NotNull(ex.InnerException);
8279
var iex = Assert.IsType<RedisConnectionException>(ex.InnerException);
8380
Assert.Contains(iex.Message, ex.Message);
8481
}
8582

8683
private ConnectionMultiplexer GetFailFastConn() =>
87-
ConnectionMultiplexer.Connect(GetOptions(BacklogPolicy.FailFast).Apply(o => o.EndPoints.Add($"doesnot.exist.{Guid.NewGuid():N}:6379")), Writer);
84+
ConnectionMultiplexer.Connect(GetOptions(BacklogPolicy.FailFast, 400).Apply(o => o.EndPoints.Add($"doesnot.exist.{Guid.NewGuid():N}:6379")), Writer);
8885

8986
private ConnectionMultiplexer GetWorkingBacklogConn() =>
90-
ConnectionMultiplexer.Connect(GetOptions(BacklogPolicy.Default).Apply(o => o.EndPoints.Add(GetConfiguration())), Writer);
87+
ConnectionMultiplexer.Connect(GetOptions(BacklogPolicy.Default, 1000).Apply(o => o.EndPoints.Add(GetConfiguration())), Writer);
9188

92-
private ConfigurationOptions GetOptions(BacklogPolicy policy) => new ConfigurationOptions()
89+
private static ConfigurationOptions GetOptions(BacklogPolicy policy, int duration) => new ConfigurationOptions()
9390
{
9491
AbortOnConnectFail = false,
9592
BacklogPolicy = policy,
9693
ConnectTimeout = 500,
97-
SyncTimeout = 400,
98-
KeepAlive = 400,
94+
SyncTimeout = duration,
95+
KeepAlive = duration,
9996
AllowAdmin = true,
10097
}.WithoutSubscriptions();
10198
}

tests/StackExchange.Redis.Tests/AdhocTests.cs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
1-
using Xunit;
2-
using Xunit.Abstractions;
1+
using System.Threading.Tasks;
2+
using Xunit;
33

44
namespace StackExchange.Redis.Tests;
55

6-
[Collection(SharedConnectionFixture.Key)]
7-
public class AdhocTests : TestBase
6+
public class AdhocTests(ITestOutputHelper output, SharedConnectionFixture fixture) : TestBase(output, fixture)
87
{
9-
public AdhocTests(ITestOutputHelper output, SharedConnectionFixture fixture) : base(output, fixture) { }
10-
118
[Fact]
12-
public void TestAdhocCommandsAPI()
9+
public async Task TestAdhocCommandsAPI()
1310
{
14-
using var conn = Create();
11+
await using var conn = Create();
1512
var db = conn.GetDatabase();
1613

1714
// needs explicit RedisKey type for key-based

tests/StackExchange.Redis.Tests/AggressiveTests.cs

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
using System.Threading;
22
using System.Threading.Tasks;
33
using Xunit;
4-
using Xunit.Abstractions;
54

65
namespace StackExchange.Redis.Tests;
76

87
[Collection(NonParallelCollection.Name)]
9-
public class AggressiveTests : TestBase
8+
public class AggressiveTests(ITestOutputHelper output) : TestBase(output)
109
{
11-
public AggressiveTests(ITestOutputHelper output) : base(output) { }
12-
13-
[FactLongRunning]
10+
[Fact]
1411
public async Task ParallelTransactionsWithConditions()
1512
{
13+
Skip.UnlessLongRunning();
1614
const int Muxers = 4, Workers = 20, PerThread = 250;
1715

1816
var muxers = new IConnectionMultiplexer[Muxers];
@@ -24,7 +22,7 @@ public async Task ParallelTransactionsWithConditions()
2422
RedisKey hits = Me(), trigger = Me() + "3";
2523
int expectedSuccess = 0;
2624

27-
await muxers[0].GetDatabase().KeyDeleteAsync(new[] { hits, trigger }).ForAwait();
25+
await muxers[0].GetDatabase().KeyDeleteAsync([hits, trigger]).ForAwait();
2826

2927
Task[] tasks = new Task[Workers];
3028
for (int i = 0; i < tasks.Length; i++)
@@ -73,10 +71,11 @@ public async Task ParallelTransactionsWithConditions()
7371

7472
private const int IterationCount = 5000, InnerCount = 20;
7573

76-
[FactLongRunning]
77-
public void RunCompetingBatchesOnSameMuxer()
74+
[Fact]
75+
public async Task RunCompetingBatchesOnSameMuxer()
7876
{
79-
using var conn = Create();
77+
Skip.UnlessLongRunning();
78+
await using var conn = Create();
8079
var db = conn.GetDatabase();
8180

8281
Thread x = new Thread(state => BatchRunPings((IDatabase)state!))
@@ -132,10 +131,11 @@ private static void BatchRunPings(IDatabase db)
132131
}
133132
}
134133

135-
[FactLongRunning]
134+
[Fact]
136135
public async Task RunCompetingBatchesOnSameMuxerAsync()
137136
{
138-
using var conn = Create();
137+
Skip.UnlessLongRunning();
138+
await using var conn = Create();
139139
var db = conn.GetDatabase();
140140

141141
var x = Task.Run(() => BatchRunPingsAsync(db));
@@ -189,10 +189,11 @@ private static async Task BatchRunPingsAsync(IDatabase db)
189189
}
190190
}
191191

192-
[FactLongRunning]
193-
public void RunCompetingTransactionsOnSameMuxer()
192+
[Fact]
193+
public async Task RunCompetingTransactionsOnSameMuxer()
194194
{
195-
using var conn = Create(logTransactionData: false);
195+
Skip.UnlessLongRunning();
196+
await using var conn = Create(logTransactionData: false);
196197
var db = conn.GetDatabase();
197198

198199
Thread x = new Thread(state => TranRunPings((IDatabase)state!))
@@ -252,10 +253,11 @@ private void TranRunPings(IDatabase db)
252253
}
253254
}
254255

255-
[FactLongRunning]
256+
[Fact]
256257
public async Task RunCompetingTransactionsOnSameMuxerAsync()
257258
{
258-
using var conn = Create(logTransactionData: false);
259+
Skip.UnlessLongRunning();
260+
await using var conn = Create(logTransactionData: false);
259261
var db = conn.GetDatabase();
260262

261263
var x = Task.Run(() => TranRunPingsAsync(db));

0 commit comments

Comments
 (0)