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
1 change: 1 addition & 0 deletions tests/StackExchange.Redis.Tests/ClusterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ public async Task TestIdentity()
[Fact]
public async Task IntentionalWrongServer()
{
SkipOnWindowsRelease();
static string? StringGet(IServer server, RedisKey key, CommandFlags flags = CommandFlags.None)
=> (string?)server.Execute(0, "GET", [key], flags);

Expand Down
18 changes: 16 additions & 2 deletions tests/StackExchange.Redis.Tests/KeyIdleAsyncTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,35 @@ namespace StackExchange.Redis.Tests;
[RunPerProtocol]
public class KeyIdleAsyncTests(ITestOutputHelper output, SharedConnectionFixture fixture) : TestBase(output, fixture)
{
// Target the standalone secure server (6381) rather than the default primary (6379).
// OBJECT IDLETIME is reset via the value's LRU access clock, but Redis deliberately
// suppresses that update while a save/AOF/replication-sync child is active (copy-on-write
// avoidance). The default primary has a replica attached, and every replica full-sync forks
// such a child; if one overlaps a touch/read in these tests, the idle time isn't reset and the
// test flakes (observed on Windows CI). 6381 has no replica and no persistence, so no fork ever
// suppresses the reset and the behaviour is deterministic. Overriding GetConfiguration here also
// opts these tests out of the shared connection fixture, giving each a dedicated 6381 connection.
protected override string GetConfiguration()
=> TestConfig.Current.SecureServerAndPort + ",password=" + TestConfig.Current.SecurePassword;

[Fact]
public async Task IdleTimeAsync()
{
SkipOnWindowsRelease("WSL exacerbates replication time");
await using var conn = Create();

RedisKey key = Me();
var db = conn.GetDatabase();
db.KeyDelete(key, CommandFlags.FireAndForget);
db.StringSet(key, "new value", flags: CommandFlags.FireAndForget);
var timer = Stopwatch.StartNew();
await Task.Delay(2000).ForAwait();
var idleTime = await db.KeyIdleTimeAsync(key).ForAwait();
Assert.True(idleTime > TimeSpan.Zero, "First check");
Assert.True(idleTime > TimeSpan.Zero, $"First check: {idleTime} should be > 0; elapsed: {timer.ElapsedMilliseconds}ms");

db.StringSet(key, "new value2", flags: CommandFlags.FireAndForget);
var idleTime2 = await db.KeyIdleTimeAsync(key).ForAwait();
Assert.True(idleTime2 < idleTime, "Second check");
Assert.True(idleTime2 < idleTime, $"Second check: {idleTime2} should be < {idleTime}; elapsed: {timer.ElapsedMilliseconds}ms");

db.KeyDelete(key);
var idleTime3 = await db.KeyIdleTimeAsync(key).ForAwait();
Expand All @@ -33,6 +46,7 @@ public async Task IdleTimeAsync()
[Fact]
public async Task TouchIdleTimeAsync()
{
SkipOnWindowsRelease("WSL exacerbates replication time");
await using var conn = Create(require: RedisFeatures.v3_2_1);

RedisKey key = Me();
Expand Down
26 changes: 21 additions & 5 deletions tests/StackExchange.Redis.Tests/KeyIdleTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Xunit;

Expand All @@ -7,22 +8,35 @@ namespace StackExchange.Redis.Tests;
[RunPerProtocol]
public class KeyIdleTests(ITestOutputHelper output, SharedConnectionFixture fixture) : TestBase(output, fixture)
{
// Target the standalone secure server (6381) rather than the default primary (6379).
// OBJECT IDLETIME is reset via the value's LRU access clock, but Redis deliberately
// suppresses that update while a save/AOF/replication-sync child is active (copy-on-write
// avoidance). The default primary has a replica attached, and every replica full-sync forks
// such a child; if one overlaps a touch/read in these tests, the idle time isn't reset and the
// test flakes (observed on Windows CI). 6381 has no replica and no persistence, so no fork ever
// suppresses the reset and the behaviour is deterministic. Overriding GetConfiguration here also
// opts these tests out of the shared connection fixture, giving each a dedicated 6381 connection.
protected override string GetConfiguration()
=> TestConfig.Current.SecureServerAndPort + ",password=" + TestConfig.Current.SecurePassword;

[Fact]
public async Task IdleTime()
{
SkipOnWindowsRelease("WSL exacerbates replication time");
await using var conn = Create();

RedisKey key = Me();
var db = conn.GetDatabase();
db.KeyDelete(key, CommandFlags.FireAndForget);
db.StringSet(key, "new value", flags: CommandFlags.FireAndForget);
var timer = Stopwatch.StartNew();
await Task.Delay(2000).ForAwait();
var idleTime = db.KeyIdleTime(key);
Assert.True(idleTime > TimeSpan.Zero);
Assert.True(idleTime > TimeSpan.Zero, $"First check: {idleTime} should be > 0; elapsed: {timer.ElapsedMilliseconds}ms");

db.StringSet(key, "new value2", flags: CommandFlags.FireAndForget);
var idleTime2 = db.KeyIdleTime(key);
Assert.True(idleTime2 < idleTime);
Assert.True(idleTime2 < idleTime, $"Second check: {idleTime2} should be < {idleTime}; elapsed: {timer.ElapsedMilliseconds}ms");

db.KeyDelete(key);
var idleTime3 = db.KeyIdleTime(key);
Expand All @@ -32,18 +46,20 @@ public async Task IdleTime()
[Fact]
public async Task TouchIdleTime()
{
SkipOnWindowsRelease("WSL exacerbates replication time");
await using var conn = Create(require: RedisFeatures.v3_2_1);

RedisKey key = Me();
var db = conn.GetDatabase();
db.KeyDelete(key, CommandFlags.FireAndForget);
db.StringSet(key, "new value", flags: CommandFlags.FireAndForget);
var timer = Stopwatch.StartNew();
await Task.Delay(2000).ForAwait();
var idleTime = db.KeyIdleTime(key);
Assert.True(idleTime > TimeSpan.Zero, "First check");
Assert.True(idleTime > TimeSpan.Zero, $"First check: {idleTime} should be > 0; elapsed: {timer.ElapsedMilliseconds}ms");

Assert.True(db.KeyTouch(key), "Second check");
Assert.True(db.KeyTouch(key), $"Second check: should be True; elapsed: {timer.ElapsedMilliseconds}ms");
var idleTime1 = db.KeyIdleTime(key);
Assert.True(idleTime1 < idleTime, "Third check");
Assert.True(idleTime1 < idleTime, $"Third check: {idleTime1} should be < {idleTime}; elapsed: {timer.ElapsedMilliseconds}ms");
}
}
1 change: 1 addition & 0 deletions tests/StackExchange.Redis.Tests/RoleTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public class Roles(ITestOutputHelper output, SharedConnectionFixture fixture) :
[InlineData(false)]
public async Task PrimaryRole(bool allowAdmin) // should work with or without admin now
{
SkipOnWindowsRelease();
await using var conn = Create(allowAdmin: allowAdmin);
var servers = conn.GetServers();
Log("Server list:");
Expand Down
21 changes: 21 additions & 0 deletions tests/StackExchange.Redis.Tests/SentinelTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public class SentinelTests(ITestOutputHelper output) : SentinelBase(output)
[Fact]
public async Task PrimaryConnectTest()
{
SkipOnWindowsRelease();
var connectionString = $"{TestConfig.Current.SentinelServer},serviceName={ServiceOptions.ServiceName},allowAdmin=true";

var conn = ConnectionMultiplexer.Connect(connectionString);
Expand Down Expand Up @@ -49,6 +50,7 @@ public async Task PrimaryConnectTest()
[Fact]
public async Task PrimaryConnectAsyncTest()
{
SkipOnWindowsRelease();
var connectionString = $"{TestConfig.Current.SentinelServer},serviceName={ServiceOptions.ServiceName},allowAdmin=true";
var conn = await ConnectionMultiplexer.ConnectAsync(connectionString);

Expand Down Expand Up @@ -86,6 +88,7 @@ public async Task PrimaryConnectAsyncTest()
[RunPerProtocol]
public async Task SentinelConnectTest()
{
SkipOnWindowsRelease();
var options = ServiceOptions.Clone();
options.EndPoints.Add(TestConfig.Current.SentinelServer, TestConfig.Current.SentinelPortA);
await using var conn = ConnectionMultiplexer.SentinelConnect(options);
Expand All @@ -98,6 +101,7 @@ public async Task SentinelConnectTest()
[Fact]
public async Task SentinelRepeatConnectTest()
{
SkipOnWindowsRelease();
var options = ConfigurationOptions.Parse($"{TestConfig.Current.SentinelServer}:{TestConfig.Current.SentinelPortA}");
options.ServiceName = ServiceName;
options.AllowAdmin = true;
Expand Down Expand Up @@ -130,6 +134,7 @@ public async Task SentinelRepeatConnectTest()
[Fact]
public async Task SentinelConnectAsyncTest()
{
SkipOnWindowsRelease();
var options = ServiceOptions.Clone();
options.EndPoints.Add(TestConfig.Current.SentinelServer, TestConfig.Current.SentinelPortA);
var conn = await ConnectionMultiplexer.SentinelConnectAsync(options);
Expand All @@ -142,6 +147,7 @@ public async Task SentinelConnectAsyncTest()
[Fact]
public void SentinelRole()
{
SkipOnWindowsRelease();
foreach (var server in SentinelsServers)
{
var role = server.Role();
Expand All @@ -155,6 +161,7 @@ public void SentinelRole()
[Fact]
public async Task PingTest()
{
SkipOnWindowsRelease();
var test = await SentinelServerA.PingAsync();
Log("ping to sentinel {0}:{1} took {2} ms", TestConfig.Current.SentinelServer, TestConfig.Current.SentinelPortA, test.TotalMilliseconds);
test = await SentinelServerB.PingAsync();
Expand All @@ -166,6 +173,7 @@ public async Task PingTest()
[Fact]
public void SentinelGetPrimaryAddressByNameTest()
{
SkipOnWindowsRelease();
foreach (var server in SentinelsServers)
{
var primary = server.SentinelMaster(ServiceName);
Expand All @@ -182,6 +190,7 @@ public void SentinelGetPrimaryAddressByNameTest()
[Fact]
public async Task SentinelGetPrimaryAddressByNameAsyncTest()
{
SkipOnWindowsRelease();
foreach (var server in SentinelsServers)
{
var primary = server.SentinelMaster(ServiceName);
Expand All @@ -198,6 +207,7 @@ public async Task SentinelGetPrimaryAddressByNameAsyncTest()
[Fact]
public void SentinelGetMasterAddressByNameNegativeTest()
{
SkipOnWindowsRelease();
foreach (var server in SentinelsServers)
{
var endpoint = server.SentinelGetMasterAddressByName("FakeServiceName");
Expand All @@ -208,6 +218,7 @@ public void SentinelGetMasterAddressByNameNegativeTest()
[Fact]
public async Task SentinelGetMasterAddressByNameAsyncNegativeTest()
{
SkipOnWindowsRelease();
foreach (var server in SentinelsServers)
{
var endpoint = await server.SentinelGetMasterAddressByNameAsync("FakeServiceName").ForAwait();
Expand All @@ -218,6 +229,7 @@ public async Task SentinelGetMasterAddressByNameAsyncNegativeTest()
[Fact]
public void SentinelPrimaryTest()
{
SkipOnWindowsRelease();
foreach (var server in SentinelsServers)
{
var dict = server.SentinelMaster(ServiceName).ToDictionary();
Expand All @@ -233,6 +245,7 @@ public void SentinelPrimaryTest()
[Fact]
public async Task SentinelPrimaryAsyncTest()
{
SkipOnWindowsRelease();
foreach (var server in SentinelsServers)
{
var results = await server.SentinelMasterAsync(ServiceName).ForAwait();
Expand All @@ -248,6 +261,7 @@ public async Task SentinelPrimaryAsyncTest()
[Fact]
public void SentinelSentinelsTest()
{
SkipOnWindowsRelease();
var sentinels = SentinelServerA.SentinelSentinels(ServiceName);

var expected = new List<string?>
Expand Down Expand Up @@ -305,6 +319,7 @@ public void SentinelSentinelsTest()
[Fact]
public async Task SentinelSentinelsAsyncTest()
{
SkipOnWindowsRelease();
var sentinels = await SentinelServerA.SentinelSentinelsAsync(ServiceName).ForAwait();
var expected = new List<string?>
{
Expand Down Expand Up @@ -363,6 +378,7 @@ public async Task SentinelSentinelsAsyncTest()
[Fact]
public void SentinelPrimariesTest()
{
SkipOnWindowsRelease();
var primaryConfigs = SentinelServerA.SentinelMasters();
Assert.Single(primaryConfigs);
Assert.True(primaryConfigs[0].ToDictionary().ContainsKey("name"), "replicaConfigs contains 'name'");
Expand All @@ -380,6 +396,7 @@ public void SentinelPrimariesTest()
[Fact]
public async Task SentinelPrimariesAsyncTest()
{
SkipOnWindowsRelease();
var primaryConfigs = await SentinelServerA.SentinelMastersAsync().ForAwait();
Assert.Single(primaryConfigs);
Assert.True(primaryConfigs[0].ToDictionary().ContainsKey("name"), "replicaConfigs contains 'name'");
Expand All @@ -397,6 +414,7 @@ public async Task SentinelPrimariesAsyncTest()
[Fact]
public async Task SentinelReplicasTest()
{
SkipOnWindowsRelease();
// Give previous test run a moment to reset when multi-framework failover is in play.
await UntilConditionAsync(TimeSpan.FromSeconds(5), () => SentinelServerA.SentinelReplicas(ServiceName).Length > 0);

Expand All @@ -417,6 +435,7 @@ public async Task SentinelReplicasTest()
[Fact]
public async Task SentinelReplicasAsyncTest()
{
SkipOnWindowsRelease();
// Give previous test run a moment to reset when multi-framework failover is in play.
await UntilConditionAsync(TimeSpan.FromSeconds(5), () => SentinelServerA.SentinelReplicas(ServiceName).Length > 0);

Expand All @@ -436,6 +455,7 @@ public async Task SentinelReplicasAsyncTest()
[Fact]
public async Task SentinelGetSentinelAddressesTest()
{
SkipOnWindowsRelease();
var addresses = await SentinelServerA.SentinelGetSentinelAddressesAsync(ServiceName).ForAwait();
Assert.Contains(SentinelServerB.EndPoint, addresses);
Assert.Contains(SentinelServerC.EndPoint, addresses);
Expand All @@ -452,6 +472,7 @@ public async Task SentinelGetSentinelAddressesTest()
[Fact]
public async Task ReadOnlyConnectionReplicasTest()
{
SkipOnWindowsRelease();
var replicas = SentinelServerA.SentinelGetReplicaAddresses(ServiceName);
if (replicas.Length == 0)
{
Expand Down
7 changes: 7 additions & 0 deletions tests/StackExchange.Redis.Tests/TestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Linq;
using System.Runtime;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using StackExchange.Redis.Configuration;
Expand Down Expand Up @@ -702,4 +703,10 @@ public ValueTask DisposeAsync()
return default;
}
}

[Conditional("RELEASE")]
protected void SkipOnWindowsRelease(string? message = null) // typically used for tests that are super brittle on the Windows CI
{
Assert.SkipWhen(RuntimeInformation.IsOSPlatform(OSPlatform.Windows), message ?? "skipping on Windows");
}
}
Loading