Commit 2a5c4f7a authored by Marc Gravell's avatar Marc Gravell

optional reusable connection fixture for sharing a muxer between tests

parent 5e6f36d2
...@@ -18,7 +18,7 @@ namespace StackExchange.Redis ...@@ -18,7 +18,7 @@ namespace StackExchange.Redis
/// <summary> /// <summary>
/// Represents an inter-related group of connections to redis servers /// Represents an inter-related group of connections to redis servers
/// </summary> /// </summary>
public sealed partial class ConnectionMultiplexer : IConnectionMultiplexer, IDisposable public sealed partial class ConnectionMultiplexer : IInternalConnectionMultiplexer // implies : IConnectionMultiplexer and : IDisposable
{ {
private const string timeoutHelpLink = "https://stackexchange.github.io/StackExchange.Redis/Timeouts"; private const string timeoutHelpLink = "https://stackexchange.github.io/StackExchange.Redis/Timeouts";
...@@ -36,10 +36,23 @@ public sealed partial class ConnectionMultiplexer : IConnectionMultiplexer, IDis ...@@ -36,10 +36,23 @@ public sealed partial class ConnectionMultiplexer : IConnectionMultiplexer, IDis
} }
#endif #endif
bool IInternalConnectionMultiplexer.AllowConnect
{
get => AllowConnect;
set => AllowConnect = value;
}
bool IInternalConnectionMultiplexer.IgnoreConnect
{
get => IgnoreConnect;
set => IgnoreConnect = value;
}
/// <summary> /// <summary>
/// For debugging: when not enabled, servers cannot connect /// For debugging: when not enabled, servers cannot connect
/// </summary> /// </summary>
internal volatile bool AllowConnect = true; internal volatile bool AllowConnect = true;
/// <summary> /// <summary>
/// For debugging: when not enabled, end-connect is silently ignored (to simulate a long-running connect) /// For debugging: when not enabled, end-connect is silently ignored (to simulate a long-running connect)
/// </summary> /// </summary>
...@@ -903,6 +916,8 @@ private static ConnectionMultiplexer ConnectImpl(object configuration, TextWrite ...@@ -903,6 +916,8 @@ private static ConnectionMultiplexer ConnectImpl(object configuration, TextWrite
private string failureMessage; private string failureMessage;
private readonly Hashtable servers = new Hashtable(); private readonly Hashtable servers = new Hashtable();
private volatile ServerSnapshot _serverSnapshot = ServerSnapshot.Empty; private volatile ServerSnapshot _serverSnapshot = ServerSnapshot.Empty;
ReadOnlySpan<ServerEndPoint> IInternalConnectionMultiplexer.GetServerSnapshot() => GetServerSnapshot();
internal ReadOnlySpan<ServerEndPoint> GetServerSnapshot() => _serverSnapshot.Span; internal ReadOnlySpan<ServerEndPoint> GetServerSnapshot() => _serverSnapshot.Span;
private sealed class ServerSnapshot private sealed class ServerSnapshot
{ {
......
...@@ -6,10 +6,19 @@ ...@@ -6,10 +6,19 @@
namespace StackExchange.Redis namespace StackExchange.Redis
{ {
internal interface IInternalConnectionMultiplexer : IConnectionMultiplexer
{
bool AllowConnect { get; set; }
bool IgnoreConnect { get; set; }
ReadOnlySpan<ServerEndPoint> GetServerSnapshot();
}
/// <summary> /// <summary>
/// Represents the abstract multiplexer API /// Represents the abstract multiplexer API
/// </summary> /// </summary>
public interface IConnectionMultiplexer public interface IConnectionMultiplexer : IDisposable
{ {
/// <summary> /// <summary>
/// Gets the client-name that will be used on all new connections /// Gets the client-name that will be used on all new connections
...@@ -220,11 +229,6 @@ public interface IConnectionMultiplexer ...@@ -220,11 +229,6 @@ public interface IConnectionMultiplexer
/// <param name="allowCommandsToComplete">Whether to allow in-queue commadns to complete first.</param> /// <param name="allowCommandsToComplete">Whether to allow in-queue commadns to complete first.</param>
Task CloseAsync(bool allowCommandsToComplete = true); Task CloseAsync(bool allowCommandsToComplete = true);
/// <summary>
/// Release all resources associated with this object
/// </summary>
void Dispose();
/// <summary> /// <summary>
/// Obtains the log of unusual busy patterns /// Obtains the log of unusual busy patterns
/// </summary> /// </summary>
...@@ -254,5 +258,12 @@ public interface IConnectionMultiplexer ...@@ -254,5 +258,12 @@ public interface IConnectionMultiplexer
/// </summary> /// </summary>
/// <param name="key">The key to get a the slot for.</param> /// <param name="key">The key to get a the slot for.</param>
int GetHashSlot(RedisKey key); int GetHashSlot(RedisKey key);
/// <summary>
/// Write the configuration of all servers to an output stream
/// </summary>
/// <param name="destination">The destination stream to write the export to.</param>
/// <param name="options">The options to use for this export.</param>
void ExportConfiguration(Stream destination, ExportOptions options = ExportOptions.All);
} }
} }
...@@ -3,9 +3,10 @@ ...@@ -3,9 +3,10 @@
namespace StackExchange.Redis.Tests namespace StackExchange.Redis.Tests
{ {
[Collection(SharedConnectionFixture.Key)]
public class AdhocTests : TestBase public class AdhocTests : TestBase
{ {
public AdhocTests(ITestOutputHelper output) : base (output) { } public AdhocTests(ITestOutputHelper output, SharedConnectionFixture fixture) : base (output, fixture) { }
[Fact] [Fact]
public void TestAdhocCommandsAPI() public void TestAdhocCommandsAPI()
......
...@@ -16,7 +16,7 @@ public async Task ParallelTransactionsWithConditions() ...@@ -16,7 +16,7 @@ public async Task ParallelTransactionsWithConditions()
{ {
const int Muxers = 4, Workers = 20, PerThread = 250; const int Muxers = 4, Workers = 20, PerThread = 250;
var muxers = new ConnectionMultiplexer[Muxers]; var muxers = new IConnectionMultiplexer[Muxers];
try try
{ {
for (int i = 0; i < Muxers; i++) for (int i = 0; i < Muxers; i++)
......
using System; using System;
using System.Diagnostics;
using System.Threading.Tasks; using System.Threading.Tasks;
using StackExchange.Redis.KeyspaceIsolation; using StackExchange.Redis.KeyspaceIsolation;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
using System.Diagnostics;
namespace StackExchange.Redis.Tests namespace StackExchange.Redis.Tests
{ {
[Collection(SharedConnectionFixture.Key)]
public class BasicOpsTests : TestBase public class BasicOpsTests : TestBase
{ {
public BasicOpsTests(ITestOutputHelper output) : base (output) { } public BasicOpsTests(ITestOutputHelper output, SharedConnectionFixture fixture) : base (output, fixture) { }
[Fact] [Fact]
public async Task PingOnce() public async Task PingOnce()
...@@ -35,7 +36,7 @@ public void RapidDispose() ...@@ -35,7 +36,7 @@ public void RapidDispose()
for (int i = 0; i < 10; i++) for (int i = 0; i < 10; i++)
{ {
using (var secondary = Create(fail: true)) using (var secondary = Create(fail: true, shared: false))
{ {
secondary.GetDatabase().StringIncrement(key, flags: CommandFlags.FireAndForget); secondary.GetDatabase().StringIncrement(key, flags: CommandFlags.FireAndForget);
} }
...@@ -398,6 +399,33 @@ private void Incr(IDatabase database, RedisKey key, int delta, ref int total) ...@@ -398,6 +399,33 @@ private void Incr(IDatabase database, RedisKey key, int delta, ref int total)
total += delta; total += delta;
} }
[Fact]
public void ShouldUseSharedMuxer()
{
if (SharedFixtureAvailable)
{
using (var a = Create())
{
Assert.IsNotType<ConnectionMultiplexer>(a);
using (var b = Create())
{
Assert.Same(a, b);
}
}
}
else
{
using (var a = Create())
{
Assert.IsType<ConnectionMultiplexer>(a);
using (var b = Create())
{
Assert.NotSame(a, b);
}
}
}
}
[Fact] [Fact]
public async Task Delete() public async Task Delete()
{ {
......
...@@ -6,9 +6,10 @@ ...@@ -6,9 +6,10 @@
namespace StackExchange.Redis.Tests namespace StackExchange.Redis.Tests
{ {
[Collection(SharedConnectionFixture.Key)]
public class Batches : TestBase public class Batches : TestBase
{ {
public Batches(ITestOutputHelper output) : base(output) { } public Batches(ITestOutputHelper output, SharedConnectionFixture fixture) : base(output, fixture) { }
[Fact] [Fact]
public void TestBatchNotSent() public void TestBatchNotSent()
......
...@@ -3,9 +3,10 @@ ...@@ -3,9 +3,10 @@
namespace StackExchange.Redis.Tests namespace StackExchange.Redis.Tests
{ {
[Collection(SharedConnectionFixture.Key)]
public class Bits : TestBase public class Bits : TestBase
{ {
public Bits(ITestOutputHelper output) : base (output) { } public Bits(ITestOutputHelper output, SharedConnectionFixture fixture) : base (output, fixture) { }
[Fact] [Fact]
public void BasicOps() public void BasicOps()
......
...@@ -4,9 +4,10 @@ ...@@ -4,9 +4,10 @@
namespace StackExchange.Redis.Tests namespace StackExchange.Redis.Tests
{ {
[Collection(SharedConnectionFixture.Key)]
public class Constraints : TestBase public class Constraints : TestBase
{ {
public Constraints(ITestOutputHelper output) : base(output) { } public Constraints(ITestOutputHelper output, SharedConnectionFixture fixture) : base(output, fixture) { }
[Fact] [Fact]
public void ValueEquals() public void ValueEquals()
......
...@@ -4,9 +4,10 @@ ...@@ -4,9 +4,10 @@
namespace StackExchange.Redis.Tests namespace StackExchange.Redis.Tests
{ {
[Collection(SharedConnectionFixture.Key)]
public class Databases : TestBase public class Databases : TestBase
{ {
public Databases(ITestOutputHelper output) : base (output) { } public Databases(ITestOutputHelper output, SharedConnectionFixture fixture) : base (output, fixture) { }
[Fact] [Fact]
public async Task CountKeys() public async Task CountKeys()
......
...@@ -5,9 +5,10 @@ ...@@ -5,9 +5,10 @@
namespace StackExchange.Redis.Tests namespace StackExchange.Redis.Tests
{ {
[Collection(SharedConnectionFixture.Key)]
public class ExecuteTests : TestBase public class ExecuteTests : TestBase
{ {
public ExecuteTests(ITestOutputHelper output) : base(output) { } public ExecuteTests(ITestOutputHelper output, SharedConnectionFixture fixture) : base(output, fixture) { }
[Fact] [Fact]
public async Task DBExecute() public async Task DBExecute()
......
...@@ -5,9 +5,10 @@ ...@@ -5,9 +5,10 @@
namespace StackExchange.Redis.Tests namespace StackExchange.Redis.Tests
{ {
[Collection(SharedConnectionFixture.Key)]
public class Expiry : TestBase public class Expiry : TestBase
{ {
public Expiry(ITestOutputHelper output) : base (output) { } public Expiry(ITestOutputHelper output, SharedConnectionFixture fixture) : base (output, fixture) { }
private static string[] GetMap(bool disablePTimes) => disablePTimes ? (new[] { "pexpire", "pexpireat", "pttl" }) : null; private static string[] GetMap(bool disablePTimes) => disablePTimes ? (new[] { "pexpire", "pexpireat", "pttl" }) : null;
......
...@@ -199,8 +199,8 @@ public async Task SubscriptionsSurviveMasterSwitchAsync() ...@@ -199,8 +199,8 @@ public async Task SubscriptionsSurviveMasterSwitchAsync()
Skip.Inconclusive("TODO: Fix race in broadcast reconfig a zero latency."); Skip.Inconclusive("TODO: Fix race in broadcast reconfig a zero latency.");
} }
using (var a = Create(allowAdmin: true)) using (var a = Create(allowAdmin: true, shared: false))
using (var b = Create(allowAdmin: true)) using (var b = Create(allowAdmin: true, shared: false))
{ {
RedisChannel channel = Me(); RedisChannel channel = Me();
var subA = a.GetSubscriber(); var subA = a.GetSubscriber();
......
...@@ -5,9 +5,10 @@ ...@@ -5,9 +5,10 @@
namespace StackExchange.Redis.Tests namespace StackExchange.Redis.Tests
{ {
[Collection(SharedConnectionFixture.Key)]
public class FloatingPoint : TestBase public class FloatingPoint : TestBase
{ {
public FloatingPoint(ITestOutputHelper output) : base (output) { } public FloatingPoint(ITestOutputHelper output, SharedConnectionFixture fixture) : base (output, fixture) { }
private static bool Within(double x, double y, double delta) private static bool Within(double x, double y, double delta)
{ {
......
...@@ -5,9 +5,10 @@ ...@@ -5,9 +5,10 @@
namespace StackExchange.Redis.Tests namespace StackExchange.Redis.Tests
{ {
[Collection(SharedConnectionFixture.Key)]
public class GeoTests : TestBase public class GeoTests : TestBase
{ {
public GeoTests(ITestOutputHelper output) : base (output) { } public GeoTests(ITestOutputHelper output, SharedConnectionFixture fixture) : base (output, fixture) { }
public static GeoEntry public static GeoEntry
palermo = new GeoEntry(13.361389, 38.115556, "Palermo"), palermo = new GeoEntry(13.361389, 38.115556, "Palermo"),
......
...@@ -9,9 +9,10 @@ ...@@ -9,9 +9,10 @@
namespace StackExchange.Redis.Tests namespace StackExchange.Redis.Tests
{ {
[Collection(SharedConnectionFixture.Key)]
public class Hashes : TestBase // https://redis.io/commands#hash public class Hashes : TestBase // https://redis.io/commands#hash
{ {
public Hashes(ITestOutputHelper output) : base(output) { } public Hashes(ITestOutputHelper output, SharedConnectionFixture fixture) : base(output, fixture) { }
[Fact] [Fact]
public async Task TestIncrBy() public async Task TestIncrBy()
......
...@@ -23,7 +23,7 @@ public static void IfNoConfig(string prop, List<string> values) ...@@ -23,7 +23,7 @@ public static void IfNoConfig(string prop, List<string> values)
} }
} }
public static void IfMissingFeature(ConnectionMultiplexer conn, string feature, Func<RedisFeatures, bool> check) public static void IfMissingFeature(IConnectionMultiplexer conn, string feature, Func<RedisFeatures, bool> check)
{ {
var features = conn.GetServer(conn.GetEndPoints()[0]).Features; var features = conn.GetServer(conn.GetEndPoints()[0]).Features;
if (!check(features)) if (!check(features))
...@@ -35,7 +35,7 @@ public static void IfMissingFeature(ConnectionMultiplexer conn, string feature, ...@@ -35,7 +35,7 @@ public static void IfMissingFeature(ConnectionMultiplexer conn, string feature,
} }
} }
internal static void IfMissingDatabase(ConnectionMultiplexer conn, int dbId) internal static void IfMissingDatabase(IConnectionMultiplexer conn, int dbId)
{ {
var dbCount = conn.GetServer(conn.GetEndPoints()[0]).DatabaseCount; var dbCount = conn.GetServer(conn.GetEndPoints()[0]).DatabaseCount;
if (dbId >= dbCount) throw new SkipTestException($"Database '{dbId}' is not supported on this server."); if (dbId >= dbCount) throw new SkipTestException($"Database '{dbId}' is not supported on this server.");
......
...@@ -12,7 +12,7 @@ public static class TestConfig ...@@ -12,7 +12,7 @@ public static class TestConfig
public static Config Current { get; } public static Config Current { get; }
private static int _db = 17; private static int _db = 17;
public static int GetDedicatedDB(ConnectionMultiplexer conn = null) public static int GetDedicatedDB(IConnectionMultiplexer conn = null)
{ {
int db = Interlocked.Increment(ref _db); int db = Interlocked.Increment(ref _db);
if (conn != null) Skip.IfMissingDatabase(conn, db); if (conn != null) Skip.IfMissingDatabase(conn, db);
......
...@@ -3,9 +3,10 @@ ...@@ -3,9 +3,10 @@
namespace StackExchange.Redis.Tests namespace StackExchange.Redis.Tests
{ {
[Collection(SharedConnectionFixture.Key)]
public class HyperLogLog : TestBase public class HyperLogLog : TestBase
{ {
public HyperLogLog(ITestOutputHelper output) : base (output) { } public HyperLogLog(ITestOutputHelper output, SharedConnectionFixture fixture) : base (output, fixture) { }
[Fact] [Fact]
public void SingleKeyLength() public void SingleKeyLength()
......
...@@ -7,9 +7,10 @@ ...@@ -7,9 +7,10 @@
namespace StackExchange.Redis.Tests namespace StackExchange.Redis.Tests
{ {
[Collection(SharedConnectionFixture.Key)]
public class Keys : TestBase public class Keys : TestBase
{ {
public Keys(ITestOutputHelper output) : base (output) { } public Keys(ITestOutputHelper output, SharedConnectionFixture fixture) : base (output, fixture) { }
[Fact] [Fact]
public void TestScan() public void TestScan()
......
...@@ -3,9 +3,10 @@ ...@@ -3,9 +3,10 @@
namespace StackExchange.Redis.Tests namespace StackExchange.Redis.Tests
{ {
[Collection(SharedConnectionFixture.Key)]
public class Lex : TestBase public class Lex : TestBase
{ {
public Lex(ITestOutputHelper output) : base(output) { } public Lex(ITestOutputHelper output, SharedConnectionFixture fixture) : base(output, fixture) { }
[Fact] [Fact]
public void QueryRangeAndLengthByLex() public void QueryRangeAndLengthByLex()
......
...@@ -4,9 +4,10 @@ ...@@ -4,9 +4,10 @@
namespace StackExchange.Redis.Tests namespace StackExchange.Redis.Tests
{ {
[Collection(SharedConnectionFixture.Key)]
public class Lists : TestBase public class Lists : TestBase
{ {
public Lists(ITestOutputHelper output) : base(output) { } public Lists(ITestOutputHelper output, SharedConnectionFixture fixture) : base(output, fixture) { }
[Fact] [Fact]
public void Ranges() public void Ranges()
......
...@@ -76,7 +76,7 @@ public void TestOpCountByVersionLocal_UpLevel() ...@@ -76,7 +76,7 @@ public void TestOpCountByVersionLocal_UpLevel()
} }
} }
private void TestLockOpCountByVersion(ConnectionMultiplexer conn, int expectedOps, bool existFirst) private void TestLockOpCountByVersion(IConnectionMultiplexer conn, int expectedOps, bool existFirst)
{ {
const int LockDuration = 30; const int LockDuration = 30;
RedisKey Key = Me(); RedisKey Key = Me();
...@@ -103,7 +103,7 @@ private void TestLockOpCountByVersion(ConnectionMultiplexer conn, int expectedOp ...@@ -103,7 +103,7 @@ private void TestLockOpCountByVersion(ConnectionMultiplexer conn, int expectedOp
// note we get a ping from GetCounters // note we get a ping from GetCounters
} }
private ConnectionMultiplexer Create(TestMode mode) private IConnectionMultiplexer Create(TestMode mode)
{ {
switch (mode) switch (mode)
{ {
......
...@@ -4,9 +4,10 @@ ...@@ -4,9 +4,10 @@
namespace StackExchange.Redis.Tests namespace StackExchange.Redis.Tests
{ {
[Collection(SharedConnectionFixture.Key)]
public class MultiAdd : TestBase public class MultiAdd : TestBase
{ {
public MultiAdd(ITestOutputHelper output) : base(output) { } public MultiAdd(ITestOutputHelper output, SharedConnectionFixture fixture) : base(output, fixture) { }
[Fact] [Fact]
public void AddSortedSetEveryWay() public void AddSortedSetEveryWay()
......
...@@ -7,9 +7,10 @@ ...@@ -7,9 +7,10 @@
namespace StackExchange.Redis.Tests namespace StackExchange.Redis.Tests
{ {
[Collection(SharedConnectionFixture.Key)]
public class PreserveOrder : TestBase public class PreserveOrder : TestBase
{ {
public PreserveOrder(ITestOutputHelper output) : base (output) { } public PreserveOrder(ITestOutputHelper output, SharedConnectionFixture fixture) : base (output, fixture) { }
[Fact] [Fact]
public void Execute() public void Execute()
......
...@@ -73,7 +73,7 @@ public void Simple() ...@@ -73,7 +73,7 @@ public void Simple()
} }
} }
private static void AssertProfiledCommandValues(IProfiledCommand command, ConnectionMultiplexer conn, int dbId) private static void AssertProfiledCommandValues(IProfiledCommand command, IConnectionMultiplexer conn, int dbId)
{ {
Assert.Equal(dbId, command.Db); Assert.Equal(dbId, command.Db);
Assert.Equal(conn.GetEndPoints()[0], command.EndPoint); Assert.Equal(conn.GetEndPoints()[0], command.EndPoint);
...@@ -275,7 +275,7 @@ public void ProfilingMD_Ex1() ...@@ -275,7 +275,7 @@ public void ProfilingMD_Ex1()
{ {
using (var c = Create()) using (var c = Create())
{ {
ConnectionMultiplexer conn = c; IConnectionMultiplexer conn = c;
var session = new ProfilingSession(); var session = new ProfilingSession();
var prefix = Me(); var prefix = Me();
...@@ -317,7 +317,7 @@ public void ProfilingMD_Ex2() ...@@ -317,7 +317,7 @@ public void ProfilingMD_Ex2()
{ {
using (var c = Create()) using (var c = Create())
{ {
ConnectionMultiplexer conn = c; IConnectionMultiplexer conn = c;
var profiler = new PerThreadProfiler(); var profiler = new PerThreadProfiler();
var prefix = Me(); var prefix = Me();
...@@ -362,7 +362,7 @@ public async Task ProfilingMD_Ex2_Async() ...@@ -362,7 +362,7 @@ public async Task ProfilingMD_Ex2_Async()
{ {
using (var c = Create()) using (var c = Create())
{ {
ConnectionMultiplexer conn = c; IConnectionMultiplexer conn = c;
var profiler = new AsyncLocalProfiler(); var profiler = new AsyncLocalProfiler();
var prefix = Me(); var prefix = Me();
......
...@@ -10,9 +10,10 @@ ...@@ -10,9 +10,10 @@
namespace StackExchange.Redis.Tests namespace StackExchange.Redis.Tests
{ {
[Collection(SharedConnectionFixture.Key)]
public class PubSub : TestBase public class PubSub : TestBase
{ {
public PubSub(ITestOutputHelper output) : base(output) { } public PubSub(ITestOutputHelper output, SharedConnectionFixture fixture) : base(output, fixture) { }
[Fact] [Fact]
public async Task ExplicitPublishMode() public async Task ExplicitPublishMode()
...@@ -172,7 +173,7 @@ public async Task TestBasicPubSubFireAndForget() ...@@ -172,7 +173,7 @@ public async Task TestBasicPubSubFireAndForget()
} }
} }
private static async Task PingAsync(ConnectionMultiplexer muxer, IServer pub, ISubscriber sub, int times = 1) private static async Task PingAsync(IConnectionMultiplexer muxer, IServer pub, ISubscriber sub, int times = 1)
{ {
while (times-- > 0) while (times-- > 0)
{ {
...@@ -558,8 +559,8 @@ public async Task PubSubGetAllCorrectOrder_OnMessage_Async() ...@@ -558,8 +559,8 @@ public async Task PubSubGetAllCorrectOrder_OnMessage_Async()
public async Task TestPublishWithSubscribers() public async Task TestPublishWithSubscribers()
{ {
var channel = Me(); var channel = Me();
using (var muxerA = Create()) using (var muxerA = Create(shared: false))
using (var muxerB = Create()) using (var muxerB = Create(shared: false))
using (var conn = Create()) using (var conn = Create())
{ {
var listenA = muxerA.GetSubscriber(); var listenA = muxerA.GetSubscriber();
...@@ -578,8 +579,8 @@ public async Task TestPublishWithSubscribers() ...@@ -578,8 +579,8 @@ public async Task TestPublishWithSubscribers()
public async Task TestMultipleSubscribersGetMessage() public async Task TestMultipleSubscribersGetMessage()
{ {
var channel = Me(); var channel = Me();
using (var muxerA = Create()) using (var muxerA = Create(shared: false))
using (var muxerB = Create()) using (var muxerB = Create(shared: false))
using (var conn = Create()) using (var conn = Create())
{ {
var listenA = muxerA.GetSubscriber(); var listenA = muxerA.GetSubscriber();
......
...@@ -4,9 +4,10 @@ ...@@ -4,9 +4,10 @@
namespace StackExchange.Redis.Tests namespace StackExchange.Redis.Tests
{ {
[Collection(SharedConnectionFixture.Key)]
public class PubSubCommand : TestBase public class PubSubCommand : TestBase
{ {
public PubSubCommand(ITestOutputHelper output) : base (output) { } public PubSubCommand(ITestOutputHelper output, SharedConnectionFixture fixture) : base (output, fixture) { }
[Fact] [Fact]
public void SubscriberCount() public void SubscriberCount()
......
...@@ -6,9 +6,10 @@ ...@@ -6,9 +6,10 @@
namespace StackExchange.Redis.Tests namespace StackExchange.Redis.Tests
{ {
[Collection(SharedConnectionFixture.Key)]
public class Scans : TestBase public class Scans : TestBase
{ {
public Scans(ITestOutputHelper output) : base (output) { } public Scans(ITestOutputHelper output, SharedConnectionFixture fixture) : base (output, fixture) { }
[Theory] [Theory]
[InlineData(true)] [InlineData(true)]
...@@ -339,7 +340,7 @@ public void HashScanThresholds() ...@@ -339,7 +340,7 @@ public void HashScanThresholds()
} }
} }
private bool GotCursors(ConnectionMultiplexer conn, RedisKey key, int count) private bool GotCursors(IConnectionMultiplexer conn, RedisKey key, int count)
{ {
var db = conn.GetDatabase(); var db = conn.GetDatabase();
db.KeyDelete(key, CommandFlags.FireAndForget); db.KeyDelete(key, CommandFlags.FireAndForget);
......
...@@ -8,11 +8,12 @@ ...@@ -8,11 +8,12 @@
namespace StackExchange.Redis.Tests namespace StackExchange.Redis.Tests
{ {
[Collection(SharedConnectionFixture.Key)]
public class Scripting : TestBase public class Scripting : TestBase
{ {
public Scripting(ITestOutputHelper output) : base (output) { } public Scripting(ITestOutputHelper output, SharedConnectionFixture fixture) : base (output, fixture) { }
private ConnectionMultiplexer GetScriptConn(bool allowAdmin = false) private IConnectionMultiplexer GetScriptConn(bool allowAdmin = false)
{ {
int syncTimeout = 5000; int syncTimeout = 5000;
if (Debugger.IsAttached) syncTimeout = 500000; if (Debugger.IsAttached) syncTimeout = 500000;
...@@ -596,7 +597,7 @@ public void LuaScriptWithKeys() ...@@ -596,7 +597,7 @@ public void LuaScriptWithKeys()
var p = new { key = (RedisKey)key, value = 123 }; var p = new { key = (RedisKey)key, value = 123 };
script.Evaluate(db, p, flags: CommandFlags.FireAndForget); script.Evaluate(db, p);
var val = db.StringGet(key); var val = db.StringGet(key);
Assert.Equal(123, (int)val); Assert.Equal(123, (int)val);
...@@ -804,7 +805,7 @@ public void IServerLuaScriptConvenienceMethods() ...@@ -804,7 +805,7 @@ public void IServerLuaScriptConvenienceMethods()
var prepared = server.ScriptLoad(script); var prepared = server.ScriptLoad(script);
db.ScriptEvaluate(prepared, new { key = (RedisKey)key, value = "value3" }, flags: CommandFlags.FireAndForget); db.ScriptEvaluate(prepared, new { key = (RedisKey)key, value = "value3" });
var val = db.StringGet(key); var val = db.StringGet(key);
Assert.Equal("value3", val); Assert.Equal("value3", val);
} }
...@@ -842,7 +843,7 @@ public void LuaScriptWithWrappedDatabase() ...@@ -842,7 +843,7 @@ public void LuaScriptWithWrappedDatabase()
db.KeyDelete(key, CommandFlags.FireAndForget); db.KeyDelete(key, CommandFlags.FireAndForget);
var prepared = LuaScript.Prepare(Script); var prepared = LuaScript.Prepare(Script);
wrappedDb.ScriptEvaluate(prepared, new { key = (RedisKey)key, value = 123 }, flags: CommandFlags.FireAndForget); wrappedDb.ScriptEvaluate(prepared, new { key = (RedisKey)key, value = 123 });
var val1 = wrappedDb.StringGet(key); var val1 = wrappedDb.StringGet(key);
Assert.Equal(123, (int)val1); Assert.Equal(123, (int)val1);
......
...@@ -6,9 +6,10 @@ ...@@ -6,9 +6,10 @@
namespace StackExchange.Redis.Tests namespace StackExchange.Redis.Tests
{ {
[Collection(SharedConnectionFixture.Key)]
public class Sets : TestBase public class Sets : TestBase
{ {
public Sets(ITestOutputHelper output) : base (output) { } public Sets(ITestOutputHelper output, SharedConnectionFixture fixture) : base (output, fixture) { }
[Fact] [Fact]
public void SScan() public void SScan()
......
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using StackExchange.Redis.Profiling;
using Xunit;
namespace StackExchange.Redis.Tests
{
public class SharedConnectionFixture : IDisposable
{
public bool IsEnabled => true; // set to false to disable globally
public const string Key = "Shared Muxer";
private readonly ConnectionMultiplexer _actualConnection;
internal IInternalConnectionMultiplexer Connection { get; }
public string Configuration { get; }
public SharedConnectionFixture()
{
Configuration = TestBase.GetDefaultConfiguration();
_actualConnection = TestBase.CreateDefault(
output: null,
clientName: nameof(SharedConnectionFixture),
configuration: Configuration,
allowAdmin: true
);
_actualConnection.InternalError += OnInternalError;
_actualConnection.ConnectionFailed += OnConnectionFailed;
Connection = new NonDisposingConnection(_actualConnection);
}
private class NonDisposingConnection : IInternalConnectionMultiplexer
{
public bool AllowConnect
{
get => _inner.AllowConnect;
set => _inner.AllowConnect = value;
}
public bool IgnoreConnect
{
get => _inner.IgnoreConnect;
set => _inner.IgnoreConnect = value;
}
public ReadOnlySpan<ServerEndPoint> GetServerSnapshot() => _inner.GetServerSnapshot();
private readonly IInternalConnectionMultiplexer _inner;
public NonDisposingConnection(IInternalConnectionMultiplexer inner) => _inner = inner;
public string ClientName => _inner.ClientName;
public string Configuration => _inner.Configuration;
public int TimeoutMilliseconds => _inner.TimeoutMilliseconds;
public long OperationCount => _inner.OperationCount;
#pragma warning disable CS0618
public bool PreserveAsyncOrder { get => _inner.PreserveAsyncOrder; set => _inner.PreserveAsyncOrder = value; }
#pragma warning restore CS0618
public bool IsConnected => _inner.IsConnected;
public bool IsConnecting => _inner.IsConnecting;
public bool IncludeDetailInExceptions { get => _inner.IncludeDetailInExceptions; set => _inner.IncludeDetailInExceptions = value; }
public int StormLogThreshold { get => _inner.StormLogThreshold; set => _inner.StormLogThreshold = value; }
public event EventHandler<RedisErrorEventArgs> ErrorMessage
{
add
{
_inner.ErrorMessage += value;
}
remove
{
_inner.ErrorMessage -= value;
}
}
public event EventHandler<ConnectionFailedEventArgs> ConnectionFailed
{
add
{
_inner.ConnectionFailed += value;
}
remove
{
_inner.ConnectionFailed -= value;
}
}
public event EventHandler<InternalErrorEventArgs> InternalError
{
add
{
_inner.InternalError += value;
}
remove
{
_inner.InternalError -= value;
}
}
public event EventHandler<ConnectionFailedEventArgs> ConnectionRestored
{
add
{
_inner.ConnectionRestored += value;
}
remove
{
_inner.ConnectionRestored -= value;
}
}
public event EventHandler<EndPointEventArgs> ConfigurationChanged
{
add
{
_inner.ConfigurationChanged += value;
}
remove
{
_inner.ConfigurationChanged -= value;
}
}
public event EventHandler<EndPointEventArgs> ConfigurationChangedBroadcast
{
add
{
_inner.ConfigurationChangedBroadcast += value;
}
remove
{
_inner.ConfigurationChangedBroadcast -= value;
}
}
public event EventHandler<HashSlotMovedEventArgs> HashSlotMoved
{
add
{
_inner.HashSlotMoved += value;
}
remove
{
_inner.HashSlotMoved -= value;
}
}
public void Close(bool allowCommandsToComplete = true)
{
_inner.Close(allowCommandsToComplete);
}
public Task CloseAsync(bool allowCommandsToComplete = true)
{
return _inner.CloseAsync(allowCommandsToComplete);
}
public bool Configure(TextWriter log = null)
{
return _inner.Configure(log);
}
public Task<bool> ConfigureAsync(TextWriter log = null)
{
return _inner.ConfigureAsync(log);
}
public void Dispose() { } // DO NOT call _inner.Dispose();
public ServerCounters GetCounters()
{
return _inner.GetCounters();
}
public IDatabase GetDatabase(int db = -1, object asyncState = null)
{
return _inner.GetDatabase(db, asyncState);
}
public EndPoint[] GetEndPoints(bool configuredOnly = false)
{
return _inner.GetEndPoints(configuredOnly);
}
public int GetHashSlot(RedisKey key)
{
return _inner.GetHashSlot(key);
}
public IServer GetServer(string host, int port, object asyncState = null)
{
return _inner.GetServer(host, port, asyncState);
}
public IServer GetServer(string hostAndPort, object asyncState = null)
{
return _inner.GetServer(hostAndPort, asyncState);
}
public IServer GetServer(IPAddress host, int port)
{
return _inner.GetServer(host, port);
}
public IServer GetServer(EndPoint endpoint, object asyncState = null)
{
return _inner.GetServer(endpoint, asyncState);
}
public string GetStatus()
{
return _inner.GetStatus();
}
public void GetStatus(TextWriter log)
{
_inner.GetStatus(log);
}
public string GetStormLog()
{
return _inner.GetStormLog();
}
public ISubscriber GetSubscriber(object asyncState = null)
{
return _inner.GetSubscriber(asyncState);
}
public int HashSlot(RedisKey key)
{
return _inner.HashSlot(key);
}
public long PublishReconfigure(CommandFlags flags = CommandFlags.None)
{
return _inner.PublishReconfigure(flags);
}
public Task<long> PublishReconfigureAsync(CommandFlags flags = CommandFlags.None)
{
return _inner.PublishReconfigureAsync(flags);
}
public void RegisterProfiler(Func<ProfilingSession> profilingSessionProvider)
{
_inner.RegisterProfiler(profilingSessionProvider);
}
public void ResetStormLog()
{
_inner.ResetStormLog();
}
public void Wait(Task task)
{
_inner.Wait(task);
}
public T Wait<T>(Task<T> task)
{
return _inner.Wait(task);
}
public void WaitAll(params Task[] tasks)
{
_inner.WaitAll(tasks);
}
public void ExportConfiguration(Stream destination, ExportOptions options = ExportOptions.All)
=> _inner.ExportConfiguration(destination, options);
}
public void Dispose() => _actualConnection.Dispose();
protected void OnInternalError(object sender, InternalErrorEventArgs e)
{
Interlocked.Increment(ref privateFailCount);
lock (privateExceptions)
{
privateExceptions.Add(TestBase.Time() + ": Internal error: " + e.Origin + ", " + EndPointCollection.ToString(e.EndPoint) + "/" + e.ConnectionType);
}
}
protected void OnConnectionFailed(object sender, ConnectionFailedEventArgs e)
{
Interlocked.Increment(ref privateFailCount);
lock (privateExceptions)
{
privateExceptions.Add($"{TestBase.Time()}: Connection failed ({e.FailureType}): {EndPointCollection.ToString(e.EndPoint)}/{e.ConnectionType}: {e.Exception}");
}
}
private readonly List<string> privateExceptions = new List<string>();
private int privateFailCount;
public void Teardown(TextWriter output)
{
var privateFailCount = Interlocked.Exchange(ref this.privateFailCount, 0);
if (privateFailCount != 0)
{
lock (privateExceptions)
{
foreach (var item in privateExceptions.Take(5))
{
TestBase.LogNoTime(output, item);
}
privateExceptions.Clear();
}
Assert.True(false, $"There were {privateFailCount} private ambient exceptions.");
}
TestBase.Log(output, $"Service Counts: (Scheduler) Queue: {SocketManager.Shared?.SchedulerPool?.TotalServicedByQueue.ToString()}, Pool: {SocketManager.Shared?.SchedulerPool?.TotalServicedByPool.ToString()}, (Completion) Queue: {SocketManager.Shared?.CompletionPool?.TotalServicedByQueue.ToString()}, Pool: {SocketManager.Shared?.CompletionPool?.TotalServicedByPool.ToString()}");
}
}
// https://stackoverflow.com/questions/13829737/xunit-net-run-code-once-before-and-after-all-tests
[CollectionDefinition(SharedConnectionFixture.Key)]
public class ConnectionCollection : ICollectionFixture<SharedConnectionFixture>
{
// This class has no code, and is never created. Its purpose is simply
// to be the place to apply [CollectionDefinition] and all the
// ICollectionFixture<> interfaces.
}
}
...@@ -6,9 +6,10 @@ ...@@ -6,9 +6,10 @@
namespace StackExchange.Redis.Tests namespace StackExchange.Redis.Tests
{ {
[Collection(SharedConnectionFixture.Key)]
public class Streams : TestBase public class Streams : TestBase
{ {
public Streams(ITestOutputHelper output) : base(output) { } public Streams(ITestOutputHelper output, SharedConnectionFixture fixture) : base(output, fixture) { }
[Fact] [Fact]
public void IsStreamType() public void IsStreamType()
......
...@@ -7,9 +7,10 @@ ...@@ -7,9 +7,10 @@
namespace StackExchange.Redis.Tests namespace StackExchange.Redis.Tests
{ {
[Collection(SharedConnectionFixture.Key)]
public class Strings : TestBase // https://redis.io/commands#string public class Strings : TestBase // https://redis.io/commands#string
{ {
public Strings(ITestOutputHelper output) : base(output) { } public Strings(ITestOutputHelper output, SharedConnectionFixture fixture) : base(output, fixture) { }
[Fact] [Fact]
public async Task Append() public async Task Append()
......
using StackExchange.Redis.Tests.Helpers; using System;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
...@@ -7,9 +6,9 @@ ...@@ -7,9 +6,9 @@
using System.Net; using System.Net;
using System.Runtime; using System.Runtime;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using StackExchange.Redis.Tests.Helpers;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
...@@ -20,27 +19,35 @@ public abstract class TestBase : IDisposable ...@@ -20,27 +19,35 @@ public abstract class TestBase : IDisposable
private ITestOutputHelper Output { get; } private ITestOutputHelper Output { get; }
protected TextWriterOutputHelper Writer { get; } protected TextWriterOutputHelper Writer { get; }
protected static bool RunningInCI { get; } = Environment.GetEnvironmentVariable("APPVEYOR") != null; protected static bool RunningInCI { get; } = Environment.GetEnvironmentVariable("APPVEYOR") != null;
protected virtual string GetConfiguration() => TestConfig.Current.MasterServerAndPort; protected virtual string GetConfiguration() => GetDefaultConfiguration();
internal static string GetDefaultConfiguration() => TestConfig.Current.MasterServerAndPort;
protected TestBase(ITestOutputHelper output) private readonly SharedConnectionFixture _fixture;
protected bool SharedFixtureAvailable => _fixture != null && _fixture.IsEnabled;
protected TestBase(ITestOutputHelper output, SharedConnectionFixture fixture = null)
{ {
Output = output; Output = output;
Output.WriteFrameworkVersion(); Output.WriteFrameworkVersion();
Writer = new TextWriterOutputHelper(output, TestConfig.Current.LogToConsole); Writer = new TextWriterOutputHelper(output, TestConfig.Current.LogToConsole);
_fixture = fixture;
ClearAmbientFailures(); ClearAmbientFailures();
} }
protected void LogNoTime(string message) protected void LogNoTime(string message) => LogNoTime(Writer, message);
internal static void LogNoTime(TextWriter output, string message)
{ {
Output.WriteLine(message); output.WriteLine(message);
if (TestConfig.Current.LogToConsole) if (TestConfig.Current.LogToConsole)
{ {
Console.WriteLine(message); Console.WriteLine(message);
} }
} }
protected void Log(string message) protected void Log(string message) => Log(Writer, message);
public static void Log(TextWriter output, string message)
{ {
Output.WriteLine(Time() + ": " + message); output?.WriteLine(Time() + ": " + message);
if (TestConfig.Current.LogToConsole) if (TestConfig.Current.LogToConsole)
{ {
Console.WriteLine(message); Console.WriteLine(message);
...@@ -65,6 +72,7 @@ protected void CollectGarbage() ...@@ -65,6 +72,7 @@ protected void CollectGarbage()
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly")]
public void Dispose() public void Dispose()
{ {
_fixture?.Teardown(Writer);
Teardown(); Teardown();
} }
...@@ -175,7 +183,7 @@ public void Teardown() ...@@ -175,7 +183,7 @@ public void Teardown()
Log($"Service Counts: (Scheduler) Queue: {SocketManager.Shared?.SchedulerPool?.TotalServicedByQueue.ToString()}, Pool: {SocketManager.Shared?.SchedulerPool?.TotalServicedByPool.ToString()}, (Completion) Queue: {SocketManager.Shared?.CompletionPool?.TotalServicedByQueue.ToString()}, Pool: {SocketManager.Shared?.CompletionPool?.TotalServicedByPool.ToString()}"); Log($"Service Counts: (Scheduler) Queue: {SocketManager.Shared?.SchedulerPool?.TotalServicedByQueue.ToString()}, Pool: {SocketManager.Shared?.SchedulerPool?.TotalServicedByPool.ToString()}, (Completion) Queue: {SocketManager.Shared?.CompletionPool?.TotalServicedByQueue.ToString()}, Pool: {SocketManager.Shared?.CompletionPool?.TotalServicedByPool.ToString()}");
} }
protected IServer GetServer(ConnectionMultiplexer muxer) protected IServer GetServer(IConnectionMultiplexer muxer)
{ {
EndPoint[] endpoints = muxer.GetEndPoints(); EndPoint[] endpoints = muxer.GetEndPoints();
IServer result = null; IServer result = null;
...@@ -190,7 +198,7 @@ protected IServer GetServer(ConnectionMultiplexer muxer) ...@@ -190,7 +198,7 @@ protected IServer GetServer(ConnectionMultiplexer muxer)
return result; return result;
} }
protected IServer GetAnyMaster(ConnectionMultiplexer muxer) protected IServer GetAnyMaster(IConnectionMultiplexer muxer)
{ {
foreach (var endpoint in muxer.GetEndPoints()) foreach (var endpoint in muxer.GetEndPoints())
{ {
...@@ -200,13 +208,54 @@ protected IServer GetAnyMaster(ConnectionMultiplexer muxer) ...@@ -200,13 +208,54 @@ protected IServer GetAnyMaster(ConnectionMultiplexer muxer)
throw new InvalidOperationException("Requires a master endpoint (found none)"); throw new InvalidOperationException("Requires a master endpoint (found none)");
} }
protected virtual ConnectionMultiplexer Create( internal virtual IInternalConnectionMultiplexer Create(
string clientName = null, int? syncTimeout = null, bool? allowAdmin = null, int? keepAlive = null,
int? connectTimeout = null, string password = null, string tieBreaker = null, TextWriter log = null,
bool fail = true, string[] disabledCommands = null, string[] enabledCommands = null,
bool checkConnect = true, string failMessage = null,
string channelPrefix = null, Proxy? proxy = null,
string configuration = null, bool logTransactionData = true,
bool shared = true,
[CallerMemberName] string caller = null)
{
if (Output == null)
{
Assert.True(false, "Failure: Be sure to call the TestBase constuctor like this: BasicOpsTests(ITestOutputHelper output) : base(output) { }");
}
if (shared && _fixture != null && _fixture.IsEnabled && enabledCommands == null && disabledCommands == null && fail && channelPrefix == null && proxy == null
&& configuration == null && password == null && tieBreaker == null && (allowAdmin == null || allowAdmin == true) && expectedFailCount == 0)
{
configuration = GetConfiguration();
if (configuration == _fixture.Configuration)
{ // only if the
return _fixture.Connection;
}
}
var muxer = CreateDefault(
Writer,
clientName, syncTimeout, allowAdmin, keepAlive,
connectTimeout, password, tieBreaker, log,
fail, disabledCommands, enabledCommands,
checkConnect, failMessage,
channelPrefix, proxy,
configuration ?? GetConfiguration(),
logTransactionData, caller);
muxer.InternalError += OnInternalError;
muxer.ConnectionFailed += OnConnectionFailed;
return muxer;
}
public static ConnectionMultiplexer CreateDefault(
TextWriter output,
string clientName = null, int? syncTimeout = null, bool? allowAdmin = null, int? keepAlive = null, string clientName = null, int? syncTimeout = null, bool? allowAdmin = null, int? keepAlive = null,
int? connectTimeout = null, string password = null, string tieBreaker = null, TextWriter log = null, int? connectTimeout = null, string password = null, string tieBreaker = null, TextWriter log = null,
bool fail = true, string[] disabledCommands = null, string[] enabledCommands = null, bool fail = true, string[] disabledCommands = null, string[] enabledCommands = null,
bool checkConnect = true, string failMessage = null, bool checkConnect = true, string failMessage = null,
string channelPrefix = null, Proxy? proxy = null, string channelPrefix = null, Proxy? proxy = null,
string configuration = null, bool logTransactionData = true, string configuration = null, bool logTransactionData = true,
[CallerMemberName] string caller = null) [CallerMemberName] string caller = null)
{ {
StringWriter localLog = null; StringWriter localLog = null;
...@@ -216,7 +265,6 @@ protected IServer GetAnyMaster(ConnectionMultiplexer muxer) ...@@ -216,7 +265,6 @@ protected IServer GetAnyMaster(ConnectionMultiplexer muxer)
} }
try try
{ {
configuration = configuration ?? GetConfiguration();
var config = ConfigurationOptions.Parse(configuration); var config = ConfigurationOptions.Parse(configuration);
if (disabledCommands != null && disabledCommands.Length != 0) if (disabledCommands != null && disabledCommands.Length != 0)
{ {
...@@ -258,11 +306,10 @@ protected IServer GetAnyMaster(ConnectionMultiplexer muxer) ...@@ -258,11 +306,10 @@ protected IServer GetAnyMaster(ConnectionMultiplexer muxer)
throw new TimeoutException("Connect timeout"); throw new TimeoutException("Connect timeout");
} }
watch.Stop(); watch.Stop();
if (Output == null) if (output != null)
{ {
Assert.True(false, "Failure: Be sure to call the TestBase constuctor like this: BasicOpsTests(ITestOutputHelper output) : base(output) { }"); Log(output, "Connect took: " + watch.ElapsedMilliseconds + "ms");
} }
Log("Connect took: " + watch.ElapsedMilliseconds + "ms");
var muxer = task.Result; var muxer = task.Result;
if (checkConnect && (muxer == null || !muxer.IsConnected)) if (checkConnect && (muxer == null || !muxer.IsConnected))
{ {
...@@ -270,30 +317,30 @@ protected IServer GetAnyMaster(ConnectionMultiplexer muxer) ...@@ -270,30 +317,30 @@ protected IServer GetAnyMaster(ConnectionMultiplexer muxer)
Assert.False(fail, failMessage + "Server is not available"); Assert.False(fail, failMessage + "Server is not available");
Skip.Inconclusive(failMessage + "Server is not available"); Skip.Inconclusive(failMessage + "Server is not available");
} }
if (output != null)
muxer.InternalError += OnInternalError; {
muxer.ConnectionFailed += OnConnectionFailed;
muxer.MessageFaulted += (msg, ex, origin) => muxer.MessageFaulted += (msg, ex, origin) =>
{ {
Writer?.WriteLine($"Faulted from '{origin}': '{msg}' - '{(ex == null ? "(null)" : ex.Message)}'"); output?.WriteLine($"Faulted from '{origin}': '{msg}' - '{(ex == null ? "(null)" : ex.Message)}'");
if (ex != null && ex.Data.Contains("got")) if (ex != null && ex.Data.Contains("got"))
{ {
Writer?.WriteLine($"Got: '{ex.Data["got"]}'"); output?.WriteLine($"Got: '{ex.Data["got"]}'");
} }
}; };
muxer.Connecting += (e, t) => Writer.WriteLine($"Connecting to {Format.ToString(e)} as {t}"); muxer.Connecting += (e, t) => output?.WriteLine($"Connecting to {Format.ToString(e)} as {t}");
if (logTransactionData) if (logTransactionData)
{ {
muxer.TransactionLog += msg => Writer.WriteLine("tran: " + msg); muxer.TransactionLog += msg => output?.WriteLine("tran: " + msg);
}
muxer.InfoMessage += msg => output?.WriteLine(msg);
muxer.Resurrecting += (e, t) => output?.WriteLine($"Resurrecting {Format.ToString(e)} as {t}");
muxer.Closing += complete => output?.WriteLine(complete ? "Closed" : "Closing...");
} }
muxer.InfoMessage += msg => Writer.WriteLine(msg);
muxer.Resurrecting += (e, t) => Writer.WriteLine($"Resurrecting {Format.ToString(e)} as {t}");
muxer.Closing += complete => Writer.WriteLine(complete ? "Closed" : "Closing...");
return muxer; return muxer;
} }
catch catch
{ {
if (localLog != null) Output?.WriteLine(localLog.ToString()); if (localLog != null) output?.WriteLine(localLog.ToString());
throw; throw;
} }
} }
......
...@@ -10,9 +10,10 @@ ...@@ -10,9 +10,10 @@
namespace StackExchange.Redis.Tests namespace StackExchange.Redis.Tests
{ {
[Collection(SharedConnectionFixture.Key)]
public class Transactions : TestBase public class Transactions : TestBase
{ {
public Transactions(ITestOutputHelper output) : base(output) { } public Transactions(ITestOutputHelper output, SharedConnectionFixture fixture) : base(output, fixture) { }
[Fact] [Fact]
public void BasicEmptyTran() public void BasicEmptyTran()
......
...@@ -5,9 +5,10 @@ ...@@ -5,9 +5,10 @@
namespace StackExchange.Redis.Tests namespace StackExchange.Redis.Tests
{ {
[Collection(SharedConnectionFixture.Key)]
public class WithKeyPrefixTests : TestBase public class WithKeyPrefixTests : TestBase
{ {
public WithKeyPrefixTests(ITestOutputHelper output) : base(output) { } public WithKeyPrefixTests(ITestOutputHelper output, SharedConnectionFixture fixture) : base(output, fixture) { }
[Fact] [Fact]
public void BlankPrefixYieldsSame_Bytes() public void BlankPrefixYieldsSame_Bytes()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment