Commit 56fea247 authored by Nick Craver's avatar Nick Craver

Tests: consolidate Booksleeve suite

Long overdue collapsing of the two suites
parent f80acb1b
...@@ -4,49 +4,51 @@ ...@@ -4,49 +4,51 @@
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
namespace StackExchange.Redis.Tests.Booksleeve namespace StackExchange.Redis.Tests
{ {
public class Batches : BookSleeveTestBase public class Batches : TestBase
{ {
public Batches(ITestOutputHelper output) : base(output) { } public Batches(ITestOutputHelper output) : base(output) { }
[Fact] [Fact]
public void TestBatchNotSent() public void TestBatchNotSent()
{ {
using (var muxer = GetUnsecuredConnection()) using (var muxer = Create())
{ {
var conn = muxer.GetDatabase(); var conn = muxer.GetDatabase();
conn.KeyDeleteAsync("batch"); var key = Me();
conn.StringSetAsync("batch", "batch-not-sent"); conn.KeyDeleteAsync(key);
conn.StringSetAsync(key, "batch-not-sent");
var tasks = new List<Task>(); var tasks = new List<Task>();
var batch = conn.CreateBatch(); var batch = conn.CreateBatch();
tasks.Add(batch.KeyDeleteAsync("batch")); tasks.Add(batch.KeyDeleteAsync(key));
tasks.Add(batch.SetAddAsync("batch", "a")); tasks.Add(batch.SetAddAsync(key, "a"));
tasks.Add(batch.SetAddAsync("batch", "b")); tasks.Add(batch.SetAddAsync(key, "b"));
tasks.Add(batch.SetAddAsync("batch", "c")); tasks.Add(batch.SetAddAsync(key, "c"));
Assert.Equal("batch-not-sent", conn.StringGet("batch")); Assert.Equal("batch-not-sent", conn.StringGet(key));
} }
} }
[Fact] [Fact]
public void TestBatchSent() public void TestBatchSent()
{ {
using (var muxer = GetUnsecuredConnection()) using (var muxer = Create())
{ {
var conn = muxer.GetDatabase(); var conn = muxer.GetDatabase();
conn.KeyDeleteAsync("batch"); var key = Me();
conn.StringSetAsync("batch", "batch-sent"); conn.KeyDeleteAsync(key);
conn.StringSetAsync(key, "batch-sent");
var tasks = new List<Task>(); var tasks = new List<Task>();
var batch = conn.CreateBatch(); var batch = conn.CreateBatch();
tasks.Add(batch.KeyDeleteAsync("batch")); tasks.Add(batch.KeyDeleteAsync(key));
tasks.Add(batch.SetAddAsync("batch", "a")); tasks.Add(batch.SetAddAsync(key, "a"));
tasks.Add(batch.SetAddAsync("batch", "b")); tasks.Add(batch.SetAddAsync(key, "b"));
tasks.Add(batch.SetAddAsync("batch", "c")); tasks.Add(batch.SetAddAsync(key, "c"));
batch.Execute(); batch.Execute();
var result = conn.SetMembersAsync("batch"); var result = conn.SetMembersAsync(key);
tasks.Add(result); tasks.Add(result);
Task.WhenAll(tasks.ToArray()); Task.WhenAll(tasks.ToArray());
......
using StackExchange.Redis.Tests.Helpers;
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Xunit.Abstractions;
namespace StackExchange.Redis.Tests.Booksleeve
{
public class BookSleeveTestBase
{
public ITestOutputHelper Output { get; }
public BookSleeveTestBase(ITestOutputHelper output)
{
Output = output;
Output.WriteFrameworkVersion();
}
static BookSleeveTestBase()
{
TaskScheduler.UnobservedTaskException += (sender, args) =>
{
Trace.WriteLine(args.Exception, "UnobservedTaskException");
args.SetObserved();
};
}
protected void Log(string message)
{
Output.WriteLine(message);
if (TestConfig.Current.LogToConsole)
{
Console.WriteLine(message);
}
}
protected void Log(string message, params object[] args)
{
Output.WriteLine(message, args);
if (TestConfig.Current.LogToConsole)
{
Console.WriteLine(message, args);
}
}
protected static string Me([CallerFilePath] string filePath = null, [CallerMemberName] string caller = null) => TestBase.Me(filePath, caller);
internal static IServer GetServer(ConnectionMultiplexer conn) => conn.GetServer(conn.GetEndPoints()[0]);
internal static ConnectionMultiplexer GetRemoteConnection(bool open = true, bool allowAdmin = false, bool waitForOpen = false, int syncTimeout = 5000, int ioTimeout = 5000)
{
return GetConnection(TestConfig.Current.RemoteServer, TestConfig.Current.RemotePort, open, allowAdmin, waitForOpen, syncTimeout, ioTimeout);
}
private static ConnectionMultiplexer GetConnection(string host, int port, bool open = true, bool allowAdmin = false, bool waitForOpen = false, int syncTimeout = 5000, int ioTimeout = 5000)
{
var options = new ConfigurationOptions
{
EndPoints = { { host, port } },
AllowAdmin = allowAdmin,
SyncTimeout = syncTimeout,
ResponseTimeout = ioTimeout
};
var conn = ConnectionMultiplexer.Connect(options);
conn.InternalError += (s, args) => Trace.WriteLine(args.Exception.Message, args.Origin);
if (open && waitForOpen)
{
conn.GetDatabase().Ping();
}
return conn;
}
internal static ConnectionMultiplexer GetUnsecuredConnection(bool open = true, bool allowAdmin = false, bool waitForOpen = false, int syncTimeout = 5000, int ioTimeout = 5000)
{
return GetConnection(TestConfig.Current.MasterServer, TestConfig.Current.MasterPort, open, allowAdmin, waitForOpen, syncTimeout, ioTimeout);
}
internal static ConnectionMultiplexer GetSecuredConnection()
{
Skip.IfNoConfig(nameof(TestConfig.Config.SecureServer), TestConfig.Current.SecureServer);
var options = new ConfigurationOptions
{
EndPoints = { { TestConfig.Current.SecureServer, TestConfig.Current.SecurePort } },
Password = "changeme",
SyncTimeout = 6000,
};
var conn = ConnectionMultiplexer.Connect(options);
conn.InternalError += (s, args) => Trace.WriteLine(args.Exception.Message, args.Origin);
return conn;
}
}
}
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;
using System.Security.Authentication;
namespace StackExchange.Redis.Tests.Booksleeve
{
public class Config : BookSleeveTestBase
{
public Config(ITestOutputHelper output) : base(output) { }
[Fact]
public void CanOpenUnsecuredConnection()
{
using (var conn = GetUnsecuredConnection(false))
{
var server = GetServer(conn);
server.Ping();
}
}
[Fact]
public void CanOpenSecuredConnection()
{
using (var conn = GetSecuredConnection())
{
var server = GetServer(conn);
server.Ping();
}
}
[Fact]
public void CanNotOpenNonsenseConnection_IP()
{
Assert.Throws<RedisConnectionException>(() =>
{
var log = new StringWriter();
try
{
using (var conn = ConnectionMultiplexer.Connect(TestConfig.Current.MasterServer + ":6500")) { }
}
finally
{
Log(log.ToString());
}
});
}
[Fact]
public async Task CanNotOpenNonsenseConnection_DNS()
{
var ex = await Assert.ThrowsAsync<RedisConnectionException>(async () =>
{
var log = new StringWriter();
try
{
using (var conn = await ConnectionMultiplexer.ConnectAsync($"doesnot.exist.ds.{Guid.NewGuid():N}.com:6500", log).ForAwait())
{
}
}
finally
{
Log(log.ToString());
}
}).ForAwait();
Log(ex.ToString());
}
[Fact]
public void CreateDisconnectedNonsenseConnection_IP()
{
var log = new StringWriter();
try
{
using (var conn = ConnectionMultiplexer.Connect(TestConfig.Current.MasterServer + ":6500,abortConnect=false"))
{
Assert.False(conn.GetServer(conn.GetEndPoints().Single()).IsConnected);
Assert.False(conn.GetDatabase().IsConnected(default(RedisKey)));
}
}
finally
{
Log(log.ToString());
}
}
[Fact]
public void CreateDisconnectedNonsenseConnection_DNS()
{
var log = new StringWriter();
try
{
using (var conn = ConnectionMultiplexer.Connect($"doesnot.exist.ds.{Guid.NewGuid():N}.com:6500, abortConnect=false", log))
{
Assert.False(conn.GetServer(conn.GetEndPoints().Single()).IsConnected);
Assert.False(conn.GetDatabase().IsConnected(default(RedisKey)));
}
}
finally
{
Log(log.ToString());
}
}
[Fact]
public void SslProtocols_SingleValue()
{
var options = ConfigurationOptions.Parse("myhost,sslProtocols=Tls11");
Assert.Equal(SslProtocols.Tls11, options.SslProtocols.Value);
}
[Fact]
public void SslProtocols_MultipleValues()
{
var options = ConfigurationOptions.Parse("myhost,sslProtocols=Tls11|Tls12");
Assert.Equal(SslProtocols.Tls11 | SslProtocols.Tls12, options.SslProtocols.Value);
}
[Fact]
public void SslProtocols_UsingIntegerValue()
{
// The below scenario is for cases where the *targeted*
// .NET framework version (e.g. .NET 4.0) doesn't define an enum value (e.g. Tls11)
// but the OS has been patched with support
const int integerValue = (int)(SslProtocols.Tls11 | SslProtocols.Tls12);
var options = ConfigurationOptions.Parse("myhost,sslProtocols=" + integerValue);
Assert.Equal(SslProtocols.Tls11 | SslProtocols.Tls12, options.SslProtocols.Value);
}
[Fact]
public void SslProtocols_InvalidValue()
{
Assert.Throws<ArgumentOutOfRangeException>(() => ConfigurationOptions.Parse("myhost,sslProtocols=InvalidSslProtocol"));
}
[Fact]
public void ConfigurationOptionsDefaultForAzure()
{
var options = ConfigurationOptions.Parse("contoso.redis.cache.windows.net");
Assert.True(options.DefaultVersion.Equals(new Version(3, 0, 0)));
Assert.False(options.AbortOnConnectFail);
}
[Fact]
public void ConfigurationOptionsForAzureWhenSpecified()
{
var options = ConfigurationOptions.Parse("contoso.redis.cache.windows.net,abortConnect=true, version=2.1.1");
Assert.True(options.DefaultVersion.Equals(new Version(2, 1, 1)));
Assert.True(options.AbortOnConnectFail);
}
[Fact]
public void ConfigurationOptionsDefaultForAzureChina()
{
// added a few upper case chars to validate comparison
var options = ConfigurationOptions.Parse("contoso.REDIS.CACHE.chinacloudapi.cn");
Assert.True(options.DefaultVersion.Equals(new Version(3, 0, 0)));
Assert.False(options.AbortOnConnectFail);
}
[Fact]
public void ConfigurationOptionsDefaultForAzureGermany()
{
var options = ConfigurationOptions.Parse("contoso.redis.cache.cloudapi.de");
Assert.True(options.DefaultVersion.Equals(new Version(3, 0, 0)));
Assert.False(options.AbortOnConnectFail);
}
[Fact]
public void ConfigurationOptionsDefaultForAzureUSGov()
{
var options = ConfigurationOptions.Parse("contoso.redis.cache.usgovcloudapi.net");
Assert.True(options.DefaultVersion.Equals(new Version(3, 0, 0)));
Assert.False(options.AbortOnConnectFail);
}
[Fact]
public void ConfigurationOptionsDefaultForNonAzure()
{
var options = ConfigurationOptions.Parse("redis.contoso.com");
Assert.True(options.DefaultVersion.Equals(new Version(2, 0, 0)));
Assert.True(options.AbortOnConnectFail);
}
[Fact]
public void ConfigurationOptionsDefaultWhenNoEndpointsSpecifiedYet()
{
var options = new ConfigurationOptions();
Assert.True(options.DefaultVersion.Equals(new Version(2, 0, 0)));
Assert.True(options.AbortOnConnectFail);
}
[Fact]
public void ConfigurationOptionsSyncTimeout()
{
// Default check
var options = new ConfigurationOptions();
Assert.Equal(5000, options.SyncTimeout);
options = ConfigurationOptions.Parse("syncTimeout=20");
Assert.Equal(20, options.SyncTimeout);
}
}
}
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;
namespace StackExchange.Redis.Tests.Booksleeve
{
public class PubSub : BookSleeveTestBase // https://redis.io/commands#pubsub
{
public PubSub(ITestOutputHelper output) : base(output) { }
[Fact]
public void TestPublishWithNoSubscribers()
{
using (var muxer = GetUnsecuredConnection())
{
var conn = muxer.GetSubscriber();
Assert.Equal(0, conn.Publish(Me() + "channel", "message"));
}
}
[FactLongRunning]
public void TestMassivePublishWithWithoutFlush_Local()
{
using (var muxer = GetUnsecuredConnection(waitForOpen: true))
{
var conn = muxer.GetSubscriber();
TestMassivePublish(conn, Me(), "local");
}
}
[FactLongRunning]
public void TestMassivePublishWithWithoutFlush_Remote()
{
using (var muxer = GetRemoteConnection(waitForOpen: true))
{
var conn = muxer.GetSubscriber();
TestMassivePublish(conn, Me(), "remote");
}
}
private void TestMassivePublish(ISubscriber conn, string channel, string caption)
{
const int loop = 10000;
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
var tasks = new Task[loop];
var withFAF = Stopwatch.StartNew();
for (int i = 0; i < loop; i++)
{
conn.Publish(channel, "bar", CommandFlags.FireAndForget);
}
withFAF.Stop();
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
var withAsync = Stopwatch.StartNew();
for (int i = 0; i < loop; i++)
{
tasks[i] = conn.PublishAsync(channel, "bar");
}
conn.WaitAll(tasks);
withAsync.Stop();
Log("{2}: {0}ms (F+F) vs {1}ms (async)",
withFAF.ElapsedMilliseconds, withAsync.ElapsedMilliseconds, caption);
// We've made async so far, this test isn't really valid anymore
// So let's check they're at least within a few seconds.
Assert.True(withFAF.ElapsedMilliseconds < withAsync.ElapsedMilliseconds + 3000, caption);
}
[FactLongRunning]
public async Task PubSubGetAllAnyOrder()
{
using (var muxer = GetRemoteConnection(waitForOpen: true,
syncTimeout: 20000))
{
var sub = muxer.GetSubscriber();
RedisChannel channel = Me();
const int count = 1000;
var syncLock = new object();
var data = new HashSet<int>();
await sub.SubscribeAsync(channel, (_, val) =>
{
bool pulse;
lock (data)
{
data.Add(int.Parse(Encoding.UTF8.GetString(val)));
pulse = data.Count == count;
if ((data.Count % 100) == 99) Log(data.Count.ToString());
}
if (pulse)
{
lock (syncLock)
{
Monitor.PulseAll(syncLock);
}
}
}).ForAwait();
lock (syncLock)
{
for (int i = 0; i < count; i++)
{
sub.Publish(channel, i.ToString(), CommandFlags.FireAndForget);
}
sub.Ping();
if (!Monitor.Wait(syncLock, 20000))
{
throw new TimeoutException("Items: " + data.Count);
}
for (int i = 0; i < count; i++)
{
Assert.Contains(i, data);
}
}
}
}
[Fact]
public async Task PubSubGetAllCorrectOrder()
{
using (var muxer = GetRemoteConnection(waitForOpen: true,
syncTimeout: 20000))
{
var sub = muxer.GetSubscriber();
RedisChannel channel = Me();
const int count = 1000;
var syncLock = new object();
var data = new List<int>(count);
var subChannel = await sub.SubscribeAsync(channel).ForAwait();
await sub.PingAsync().ForAwait();
async Task RunLoop()
{
while (!subChannel.IsCompleted)
{
var work = await subChannel.ReadAsync().ForAwait();
int i = int.Parse(Encoding.UTF8.GetString(work.Message));
lock (data)
{
data.Add(i);
if (data.Count == count) break;
if ((data.Count % 100) == 99) Log(data.Count.ToString());
}
}
lock (syncLock)
{
Monitor.PulseAll(syncLock);
}
}
lock (syncLock)
{
Task.Run(RunLoop);
for (int i = 0; i < count; i++)
{
sub.Publish(channel, i.ToString(), CommandFlags.FireAndForget);
}
if (!Monitor.Wait(syncLock, 20000))
{
throw new TimeoutException("Items: " + data.Count);
}
subChannel.Unsubscribe();
sub.Ping();
muxer.GetDatabase().Ping();
for (int i = 0; i < count; i++)
{
Assert.Equal(i, data[i]);
}
}
Assert.True(subChannel.IsCompleted);
await Assert.ThrowsAsync<ChannelClosedException>(async delegate
{
var final = await subChannel.ReadAsync().ForAwait();
}).ForAwait();
}
}
[Fact]
public async Task PubSubGetAllCorrectOrder_OnMessage_Sync()
{
using (var muxer = GetRemoteConnection(waitForOpen: true,
syncTimeout: 20000))
{
var sub = muxer.GetSubscriber();
RedisChannel channel = Me();
const int count = 1000;
var syncLock = new object();
var data = new List<int>(count);
var subChannel = await sub.SubscribeAsync(channel).ForAwait();
subChannel.OnMessage(msg =>
{
int i = int.Parse(Encoding.UTF8.GetString(msg.Message));
bool pulse = false;
lock (data)
{
data.Add(i);
if (data.Count == count) pulse = true;
if ((data.Count % 100) == 99) Log(data.Count.ToString());
}
if (pulse)
{
lock (syncLock)
{
Monitor.PulseAll(syncLock);
}
}
});
await sub.PingAsync().ForAwait();
lock (syncLock)
{
for (int i = 0; i < count; i++)
{
sub.Publish(channel, i.ToString(), CommandFlags.FireAndForget);
}
if (!Monitor.Wait(syncLock, 20000))
{
throw new TimeoutException("Items: " + data.Count);
}
subChannel.Unsubscribe();
sub.Ping();
muxer.GetDatabase().Ping();
for (int i = 0; i < count; i++)
{
Assert.Equal(i, data[i]);
}
}
Assert.True(subChannel.IsCompleted);
await Assert.ThrowsAsync<ChannelClosedException>(async delegate
{
var final = await subChannel.ReadAsync().ForAwait();
}).ForAwait();
}
}
[Fact]
public async Task PubSubGetAllCorrectOrder_OnMessage_Async()
{
using (var muxer = GetRemoteConnection(waitForOpen: true,
syncTimeout: 20000))
{
var sub = muxer.GetSubscriber();
RedisChannel channel = Me();
const int count = 1000;
var syncLock = new object();
var data = new List<int>(count);
var subChannel = await sub.SubscribeAsync(channel).ForAwait();
subChannel.OnMessage(msg =>
{
int i = int.Parse(Encoding.UTF8.GetString(msg.Message));
bool pulse = false;
lock (data)
{
data.Add(i);
if (data.Count == count) pulse = true;
if ((data.Count % 100) == 99) Log(data.Count.ToString());
}
if (pulse)
{
lock (syncLock)
{
Monitor.PulseAll(syncLock);
}
}
return i % 2 == 0 ? null : Task.CompletedTask;
});
await sub.PingAsync().ForAwait();
lock (syncLock)
{
for (int i = 0; i < count; i++)
{
sub.Publish(channel, i.ToString(), CommandFlags.FireAndForget);
}
if (!Monitor.Wait(syncLock, 20000))
{
throw new TimeoutException("Items: " + data.Count);
}
subChannel.Unsubscribe();
sub.Ping();
muxer.GetDatabase().Ping();
for (int i = 0; i < count; i++)
{
Assert.Equal(i, data[i]);
}
}
Assert.True(subChannel.IsCompleted);
await Assert.ThrowsAsync<ChannelClosedException>(async delegate
{
var final = await subChannel.ReadAsync().ForAwait();
}).ForAwait();
}
}
[Fact]
public void TestPublishWithSubscribers()
{
var channel = Me();
using (var muxerA = GetUnsecuredConnection())
using (var muxerB = GetUnsecuredConnection())
using (var conn = GetUnsecuredConnection())
{
var listenA = muxerA.GetSubscriber();
var listenB = muxerB.GetSubscriber();
var t1 = listenA.SubscribeAsync(channel, delegate { });
var t2 = listenB.SubscribeAsync(channel, delegate { });
listenA.Wait(t1);
listenB.Wait(t2);
var pub = conn.GetSubscriber().PublishAsync(channel, "message");
Assert.Equal(2, conn.Wait(pub)); // delivery count
}
}
[Fact]
public async Task TestMultipleSubscribersGetMessage()
{
var channel = Me();
using (var muxerA = GetUnsecuredConnection())
using (var muxerB = GetUnsecuredConnection())
using (var conn = GetUnsecuredConnection())
{
var listenA = muxerA.GetSubscriber();
var listenB = muxerB.GetSubscriber();
conn.GetDatabase().Ping();
var pub = conn.GetSubscriber();
int gotA = 0, gotB = 0;
var tA = listenA.SubscribeAsync(channel, (s, msg) => { if (msg == "message") Interlocked.Increment(ref gotA); });
var tB = listenB.SubscribeAsync(channel, (s, msg) => { if (msg == "message") Interlocked.Increment(ref gotB); });
listenA.Wait(tA);
listenB.Wait(tB);
Assert.Equal(2, pub.Publish(channel, "message"));
await AllowReasonableTimeToPublishAndProcess().ForAwait();
Assert.Equal(1, Interlocked.CompareExchange(ref gotA, 0, 0));
Assert.Equal(1, Interlocked.CompareExchange(ref gotB, 0, 0));
// and unsubscibe...
tA = listenA.UnsubscribeAsync(channel);
listenA.Wait(tA);
Assert.Equal(1, pub.Publish(channel, "message"));
await AllowReasonableTimeToPublishAndProcess().ForAwait();
Assert.Equal(1, Interlocked.CompareExchange(ref gotA, 0, 0));
Assert.Equal(2, Interlocked.CompareExchange(ref gotB, 0, 0));
}
}
[Fact]
public async Task Issue38()
{ // https://code.google.com/p/booksleeve/issues/detail?id=38
using (var pub = GetUnsecuredConnection(waitForOpen: true))
{
var sub = pub.GetSubscriber();
int count = 0;
var prefix = Me();
void handler(RedisChannel channel, RedisValue payload) => Interlocked.Increment(ref count);
var a0 = sub.SubscribeAsync(prefix + "foo", handler);
var a1 = sub.SubscribeAsync(prefix + "bar", handler);
var b0 = sub.SubscribeAsync(prefix + "f*o", handler);
var b1 = sub.SubscribeAsync(prefix + "b*r", handler);
sub.WaitAll(a0, a1, b0, b1);
var c = sub.PublishAsync(prefix + "foo", "foo");
var d = sub.PublishAsync(prefix + "f@o", "f@o");
var e = sub.PublishAsync(prefix + "bar", "bar");
var f = sub.PublishAsync(prefix + "b@r", "b@r");
pub.WaitAll(c, d, e, f);
long total = c.Result + d.Result + e.Result + f.Result;
await AllowReasonableTimeToPublishAndProcess().ForAwait();
Assert.Equal(6, total); // sent
Assert.Equal(6, Interlocked.CompareExchange(ref count, 0, 0)); // received
}
}
internal static Task AllowReasonableTimeToPublishAndProcess() => Task.Delay(100);
[Fact]
public async Task TestPartialSubscriberGetMessage()
{
using (var muxerA = GetUnsecuredConnection())
using (var muxerB = GetUnsecuredConnection())
using (var conn = GetUnsecuredConnection())
{
int gotA = 0, gotB = 0;
var listenA = muxerA.GetSubscriber();
var listenB = muxerB.GetSubscriber();
var pub = conn.GetSubscriber();
var prefix = Me();
var tA = listenA.SubscribeAsync(prefix + "channel", (s, msg) => { if (s == prefix + "channel" && msg == "message") Interlocked.Increment(ref gotA); });
var tB = listenB.SubscribeAsync(prefix + "chann*", (s, msg) => { if (s == prefix + "channel" && msg == "message") Interlocked.Increment(ref gotB); });
listenA.Wait(tA);
listenB.Wait(tB);
Assert.Equal(2, pub.Publish(prefix + "channel", "message"));
await AllowReasonableTimeToPublishAndProcess().ForAwait();
Assert.Equal(1, Interlocked.CompareExchange(ref gotA, 0, 0));
Assert.Equal(1, Interlocked.CompareExchange(ref gotB, 0, 0));
// and unsubscibe...
tB = listenB.UnsubscribeAsync(prefix + "chann*", null);
listenB.Wait(tB);
Assert.Equal(1, pub.Publish(prefix + "channel", "message"));
await AllowReasonableTimeToPublishAndProcess().ForAwait();
Assert.Equal(2, Interlocked.CompareExchange(ref gotA, 0, 0));
Assert.Equal(1, Interlocked.CompareExchange(ref gotB, 0, 0));
}
}
[Fact]
public async Task TestSubscribeUnsubscribeAndSubscribeAgain()
{
using (var pubMuxer = GetUnsecuredConnection())
using (var subMuxer = GetUnsecuredConnection())
{
var prefix = Me();
var pub = pubMuxer.GetSubscriber();
var sub = subMuxer.GetSubscriber();
int x = 0, y = 0;
var t1 = sub.SubscribeAsync(prefix + "abc", delegate { Interlocked.Increment(ref x); });
var t2 = sub.SubscribeAsync(prefix + "ab*", delegate { Interlocked.Increment(ref y); });
sub.WaitAll(t1, t2);
pub.Publish(prefix + "abc", "");
await AllowReasonableTimeToPublishAndProcess().ForAwait();
Assert.Equal(1, Volatile.Read(ref x));
Assert.Equal(1, Volatile.Read(ref y));
t1 = sub.UnsubscribeAsync(prefix + "abc", null);
t2 = sub.UnsubscribeAsync(prefix + "ab*", null);
sub.WaitAll(t1, t2);
pub.Publish(prefix + "abc", "");
Assert.Equal(1, Volatile.Read(ref x));
Assert.Equal(1, Volatile.Read(ref y));
t1 = sub.SubscribeAsync(prefix + "abc", delegate { Interlocked.Increment(ref x); });
t2 = sub.SubscribeAsync(prefix + "ab*", delegate { Interlocked.Increment(ref y); });
sub.WaitAll(t1, t2);
pub.Publish(prefix + "abc", "");
await AllowReasonableTimeToPublishAndProcess().ForAwait();
Assert.Equal(2, Volatile.Read(ref x));
Assert.Equal(2, Volatile.Read(ref y));
}
}
}
}
using System;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Xunit;
using Xunit.Abstractions;
namespace StackExchange.Redis.Tests.Booksleeve
{
public class Scripting : BookSleeveTestBase
{
public Scripting(ITestOutputHelper output) : base(output) { }
private static ConnectionMultiplexer GetScriptConn(bool allowAdmin = false)
{
int syncTimeout = 5000;
if (Debugger.IsAttached) syncTimeout = 500000;
var muxer = GetUnsecuredConnection(waitForOpen: true, allowAdmin: allowAdmin, syncTimeout: syncTimeout);
Skip.IfMissingFeature(muxer, nameof(RedisFeatures.Scripting), r => r.Scripting);
return muxer;
}
[Fact]
public void ClientScripting()
{
using (var conn = GetScriptConn())
{
var result = conn.GetDatabase().ScriptEvaluate("return redis.call('info','server')", null, null);
}
}
[Fact]
public void BasicScripting()
{
using (var muxer = GetScriptConn())
{
var conn = muxer.GetDatabase();
var noCache = conn.ScriptEvaluateAsync("return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}",
new RedisKey[] { "key1", "key2" }, new RedisValue[] { "first", "second" });
var cache = conn.ScriptEvaluateAsync("return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}",
new RedisKey[] { "key1", "key2" }, new RedisValue[] { "first", "second" });
var results = (string[])conn.Wait(noCache);
Assert.Equal(4, results.Length);
Assert.Equal("key1", results[0]);
Assert.Equal("key2", results[1]);
Assert.Equal("first", results[2]);
Assert.Equal("second", results[3]);
results = (string[])conn.Wait(cache);
Assert.Equal(4, results.Length);
Assert.Equal("key1", results[0]);
Assert.Equal("key2", results[1]);
Assert.Equal("first", results[2]);
Assert.Equal("second", results[3]);
}
}
[Fact]
public void KeysScripting()
{
using (var muxer = GetScriptConn())
{
var conn = muxer.GetDatabase();
var key = Me();
conn.StringSet(key, "bar");
var result = (string)conn.ScriptEvaluate("return redis.call('get', KEYS[1])", new RedisKey[] { key }, null);
Assert.Equal("bar", result);
}
}
[Fact]
public void TestRandomThingFromForum()
{
const string script = @"local currentVal = tonumber(redis.call('GET', KEYS[1]));
if (currentVal <= 0 ) then return 1 elseif (currentVal - (tonumber(ARGV[1])) < 0 ) then return 0 end;
return redis.call('INCRBY', KEYS[1], -tonumber(ARGV[1]));";
using (var muxer = GetScriptConn())
{
var prefix = Me();
var conn = muxer.GetDatabase();
conn.StringSetAsync(prefix + "A", "0");
conn.StringSetAsync(prefix + "B", "5");
conn.StringSetAsync(prefix + "C", "10");
var a = conn.ScriptEvaluateAsync(script, new RedisKey[] { prefix + "A" }, new RedisValue[] { 6 });
var b = conn.ScriptEvaluateAsync(script, new RedisKey[] { prefix + "B" }, new RedisValue[] { 6 });
var c = conn.ScriptEvaluateAsync(script, new RedisKey[] { prefix + "C" }, new RedisValue[] { 6 });
var vals = conn.StringGetAsync(new RedisKey[] { prefix + "A", prefix + "B", prefix + "C" });
Assert.Equal(1, (long)conn.Wait(a)); // exit code when current val is non-positive
Assert.Equal(0, (long)conn.Wait(b)); // exit code when result would be negative
Assert.Equal(4, (long)conn.Wait(c)); // 10 - 6 = 4
Assert.Equal("0", conn.Wait(vals)[0]);
Assert.Equal("5", conn.Wait(vals)[1]);
Assert.Equal("4", conn.Wait(vals)[2]);
}
}
[Fact]
public void HackyGetPerf()
{
using (var muxer = GetScriptConn())
{
var key = Me();
var conn = muxer.GetDatabase();
conn.StringSetAsync(key + "foo", "bar");
var result = (long)conn.ScriptEvaluate(@"
redis.call('psetex', KEYS[1], 60000, 'timing')
for i = 1,100000 do
redis.call('set', 'ignore','abc')
end
local timeTaken = 60000 - redis.call('pttl', KEYS[1])
redis.call('del', KEYS[1])
return timeTaken
", new RedisKey[] { key }, null);
Log(result.ToString());
Assert.True(result > 0);
}
}
[Fact]
public void MultiIncrWithoutReplies()
{
using (var muxer = GetScriptConn())
{
var conn = muxer.GetDatabase();
var prefix = Me();
// prime some initial values
conn.KeyDeleteAsync(new RedisKey[] { prefix + "a", prefix + "b", prefix + "c" });
conn.StringIncrementAsync(prefix + "b");
conn.StringIncrementAsync(prefix + "c");
conn.StringIncrementAsync(prefix + "c");
// run the script, passing "a", "b", "c", "c" to
// increment a & b by 1, c twice
var result = conn.ScriptEvaluateAsync(
"for i,key in ipairs(KEYS) do redis.call('incr', key) end",
new RedisKey[] { prefix + "a", prefix + "b", prefix + "c", prefix + "c" }, // <== aka "KEYS" in the script
null); // <== aka "ARGV" in the script
// check the incremented values
var a = conn.StringGetAsync(prefix + "a");
var b = conn.StringGetAsync(prefix + "b");
var c = conn.StringGetAsync(prefix + "c");
Assert.True(conn.Wait(result).IsNull, "result");
Assert.Equal(1, (long)conn.Wait(a));
Assert.Equal(2, (long)conn.Wait(b));
Assert.Equal(4, (long)conn.Wait(c));
}
}
[Fact]
public void MultiIncrByWithoutReplies()
{
using (var muxer = GetScriptConn())
{
var conn = muxer.GetDatabase();
var prefix = Me();
// prime some initial values
conn.KeyDeleteAsync(new RedisKey[] { prefix + "a", prefix + "b", prefix + "c" });
conn.StringIncrementAsync(prefix + "b");
conn.StringIncrementAsync(prefix + "c");
conn.StringIncrementAsync(prefix + "c");
//run the script, passing "a", "b", "c" and 1,2,3
// increment a &b by 1, c twice
var result = conn.ScriptEvaluateAsync(
"for i,key in ipairs(KEYS) do redis.call('incrby', key, ARGV[i]) end",
new RedisKey[] { prefix + "a", prefix + "b", prefix + "c" }, // <== aka "KEYS" in the script
new RedisValue[] { 1, 1, 2 }); // <== aka "ARGV" in the script
// check the incremented values
var a = conn.StringGetAsync(prefix + "a");
var b = conn.StringGetAsync(prefix + "b");
var c = conn.StringGetAsync(prefix + "c");
Assert.True(conn.Wait(result).IsNull, "result");
Assert.Equal(1, (long)conn.Wait(a));
Assert.Equal(2, (long)conn.Wait(b));
Assert.Equal(4, (long)conn.Wait(c));
}
}
[Fact]
public void DisableStringInference()
{
using (var muxer = GetScriptConn())
{
var conn = muxer.GetDatabase();
var key = Me();
conn.StringSet(key, "bar");
var result = (byte[])conn.ScriptEvaluate("return redis.call('get', KEYS[1])", new RedisKey[] { key });
Assert.Equal("bar", Encoding.UTF8.GetString(result));
}
}
[Fact]
public void FlushDetection()
{ // we don't expect this to handle everything; we just expect it to be predictable
using (var muxer = GetScriptConn(allowAdmin: true))
{
var conn = muxer.GetDatabase();
var key = Me();
conn.StringSet(key, "bar");
var result = (string)conn.ScriptEvaluate("return redis.call('get', KEYS[1])", new RedisKey[] { key }, null);
Assert.Equal("bar", result);
// now cause all kinds of problems
GetServer(muxer).ScriptFlush();
//expect this one to <strike>fail</strike> just work fine (self-fix)
conn.ScriptEvaluate("return redis.call('get', KEYS[1])", new RedisKey[] { key }, null);
result = (string)conn.ScriptEvaluate("return redis.call('get', KEYS[1])", new RedisKey[] { key }, null);
Assert.Equal("bar", result);
}
}
[Fact]
public void PrepareScript()
{
string[] scripts = { "return redis.call('get', KEYS[1])", "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" };
using (var muxer = GetScriptConn(allowAdmin: true))
{
var server = GetServer(muxer);
server.ScriptFlush();
// when vanilla
server.ScriptLoad(scripts[0]);
server.ScriptLoad(scripts[1]);
//when known to exist
server.ScriptLoad(scripts[0]);
server.ScriptLoad(scripts[1]);
}
using (var muxer = GetScriptConn())
{
var server = GetServer(muxer);
//when vanilla
server.ScriptLoad(scripts[0]);
server.ScriptLoad(scripts[1]);
//when known to exist
server.ScriptLoad(scripts[0]);
server.ScriptLoad(scripts[1]);
//when known to exist
server.ScriptLoad(scripts[0]);
server.ScriptLoad(scripts[1]);
}
}
[Fact]
public void NonAsciiScripts()
{
using (var muxer = GetScriptConn())
{
const string evil = "return '僕'";
var conn = muxer.GetDatabase();
GetServer(muxer).ScriptLoad(evil);
var result = (string)conn.ScriptEvaluate(evil, null, null);
Assert.Equal("僕", result);
}
}
[Fact]
public void ScriptThrowsError()
{
Assert.Throws<RedisServerException>(() =>
{
using (var muxer = GetScriptConn())
{
var conn = muxer.GetDatabase();
var result = conn.ScriptEvaluateAsync("return redis.error_reply('oops')", null, null);
try
{
conn.Wait(result);
}
catch (AggregateException ex)
{
throw ex.InnerExceptions[0];
}
}
});
}
[Fact]
public void ScriptThrowsErrorInsideTransaction()
{
using (var muxer = GetScriptConn())
{
var key = Me();
var conn = muxer.GetDatabase();
conn.KeyDeleteAsync(key);
var beforeTran = (string)conn.StringGet(key);
Assert.Null(beforeTran);
var tran = conn.CreateTransaction();
{
var a = tran.StringIncrementAsync(key);
var b = tran.ScriptEvaluateAsync("return redis.error_reply('oops')", null, null);
var c = tran.StringIncrementAsync(key);
var complete = tran.ExecuteAsync();
Assert.True(tran.Wait(complete));
Assert.True(a.IsCompleted);
Assert.True(c.IsCompleted);
Assert.Equal(1L, a.Result);
Assert.Equal(2L, c.Result);
Assert.True(b.IsFaulted);
Assert.Single(b.Exception.InnerExceptions);
var ex = b.Exception.InnerExceptions.Single();
Assert.IsType<RedisServerException>(ex);
Assert.Equal("oops", ex.Message);
}
var afterTran = conn.StringGetAsync(key);
Assert.Equal(2L, (long)conn.Wait(afterTran));
}
}
[Fact]
public void ChangeDbInScript()
{
using (var muxer = GetScriptConn())
{
var key = Me();
muxer.GetDatabase(1).StringSet(key, "db 1");
muxer.GetDatabase(2).StringSet(key, "db 2");
Log("Key: " + key);
var conn = muxer.GetDatabase(2);
var evalResult = conn.ScriptEvaluateAsync(@"redis.call('select', 1)
return redis.call('get','" + key +"')", null, null);
var getResult = conn.StringGetAsync(key);
Assert.Equal("db 1", (string)conn.Wait(evalResult));
// now, our connection thought it was in db 2, but the script changed to db 1
Assert.Equal("db 2", conn.Wait(getResult));
}
}
[Fact]
public void ChangeDbInTranScript()
{
using (var muxer = GetScriptConn())
{
var key = Me();
muxer.GetDatabase(1).StringSet(key, "db 1");
muxer.GetDatabase(2).StringSet(key, "db 2");
var conn = muxer.GetDatabase(2);
var tran = conn.CreateTransaction();
var evalResult = tran.ScriptEvaluateAsync(@"redis.call('select', 1)
return redis.call('get','" + key + "')", null, null);
var getResult = tran.StringGetAsync(key);
Assert.True(tran.Execute());
Assert.Equal("db 1", (string)conn.Wait(evalResult));
// now, our connection thought it was in db 2, but the script changed to db 1
Assert.Equal("db 2", conn.Wait(getResult));
}
}
}
}
using System; using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Security.Authentication;
using System.Threading.Tasks; using System.Threading.Tasks;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
...@@ -12,6 +12,105 @@ public class Config : TestBase ...@@ -12,6 +12,105 @@ public class Config : TestBase
{ {
public Config(ITestOutputHelper output) : base (output) { } public Config(ITestOutputHelper output) : base (output) { }
[Fact]
public void SslProtocols_SingleValue()
{
var options = ConfigurationOptions.Parse("myhost,sslProtocols=Tls11");
Assert.Equal(SslProtocols.Tls11, options.SslProtocols.Value);
}
[Fact]
public void SslProtocols_MultipleValues()
{
var options = ConfigurationOptions.Parse("myhost,sslProtocols=Tls11|Tls12");
Assert.Equal(SslProtocols.Tls11 | SslProtocols.Tls12, options.SslProtocols.Value);
}
[Fact]
public void SslProtocols_UsingIntegerValue()
{
// The below scenario is for cases where the *targeted*
// .NET framework version (e.g. .NET 4.0) doesn't define an enum value (e.g. Tls11)
// but the OS has been patched with support
const int integerValue = (int)(SslProtocols.Tls11 | SslProtocols.Tls12);
var options = ConfigurationOptions.Parse("myhost,sslProtocols=" + integerValue);
Assert.Equal(SslProtocols.Tls11 | SslProtocols.Tls12, options.SslProtocols.Value);
}
[Fact]
public void SslProtocols_InvalidValue()
{
Assert.Throws<ArgumentOutOfRangeException>(() => ConfigurationOptions.Parse("myhost,sslProtocols=InvalidSslProtocol"));
}
[Fact]
public void ConfigurationOptionsDefaultForAzure()
{
var options = ConfigurationOptions.Parse("contoso.redis.cache.windows.net");
Assert.True(options.DefaultVersion.Equals(new Version(3, 0, 0)));
Assert.False(options.AbortOnConnectFail);
}
[Fact]
public void ConfigurationOptionsForAzureWhenSpecified()
{
var options = ConfigurationOptions.Parse("contoso.redis.cache.windows.net,abortConnect=true, version=2.1.1");
Assert.True(options.DefaultVersion.Equals(new Version(2, 1, 1)));
Assert.True(options.AbortOnConnectFail);
}
[Fact]
public void ConfigurationOptionsDefaultForAzureChina()
{
// added a few upper case chars to validate comparison
var options = ConfigurationOptions.Parse("contoso.REDIS.CACHE.chinacloudapi.cn");
Assert.True(options.DefaultVersion.Equals(new Version(3, 0, 0)));
Assert.False(options.AbortOnConnectFail);
}
[Fact]
public void ConfigurationOptionsDefaultForAzureGermany()
{
var options = ConfigurationOptions.Parse("contoso.redis.cache.cloudapi.de");
Assert.True(options.DefaultVersion.Equals(new Version(3, 0, 0)));
Assert.False(options.AbortOnConnectFail);
}
[Fact]
public void ConfigurationOptionsDefaultForAzureUSGov()
{
var options = ConfigurationOptions.Parse("contoso.redis.cache.usgovcloudapi.net");
Assert.True(options.DefaultVersion.Equals(new Version(3, 0, 0)));
Assert.False(options.AbortOnConnectFail);
}
[Fact]
public void ConfigurationOptionsDefaultForNonAzure()
{
var options = ConfigurationOptions.Parse("redis.contoso.com");
Assert.True(options.DefaultVersion.Equals(new Version(2, 0, 0)));
Assert.True(options.AbortOnConnectFail);
}
[Fact]
public void ConfigurationOptionsDefaultWhenNoEndpointsSpecifiedYet()
{
var options = new ConfigurationOptions();
Assert.True(options.DefaultVersion.Equals(new Version(2, 0, 0)));
Assert.True(options.AbortOnConnectFail);
}
[Fact]
public void ConfigurationOptionsSyncTimeout()
{
// Default check
var options = new ConfigurationOptions();
Assert.Equal(5000, options.SyncTimeout);
options = ConfigurationOptions.Parse("syncTimeout=20");
Assert.Equal(20, options.SyncTimeout);
}
[Fact] [Fact]
public void TalkToNonsenseServer() public void TalkToNonsenseServer()
{ {
......
using System.Diagnostics; using System;
using System.Diagnostics;
using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Xunit; using Xunit;
...@@ -10,7 +12,6 @@ public class ConnectToUnexistingHost : TestBase ...@@ -10,7 +12,6 @@ public class ConnectToUnexistingHost : TestBase
{ {
public ConnectToUnexistingHost(ITestOutputHelper output) : base (output) { } public ConnectToUnexistingHost(ITestOutputHelper output) : base (output) { }
#if DEBUG
[Fact] [Fact]
public async Task FailsWithinTimeout() public async Task FailsWithinTimeout()
{ {
...@@ -39,6 +40,45 @@ public async Task FailsWithinTimeout() ...@@ -39,6 +40,45 @@ public async Task FailsWithinTimeout()
Assert.True(elapsed < 9000, "Connect should fail within ConnectTimeout, ElapsedMs: " + elapsed); Assert.True(elapsed < 9000, "Connect should fail within ConnectTimeout, ElapsedMs: " + elapsed);
} }
} }
#endif
[Fact]
public void CanNotOpenNonsenseConnection_IP()
{
var ex = Assert.Throws<RedisConnectionException>(() =>
{
using (var conn = ConnectionMultiplexer.Connect(TestConfig.Current.MasterServer + ":6500", Writer)) { }
});
Log(ex.ToString());
}
[Fact]
public async Task CanNotOpenNonsenseConnection_DNS()
{
var ex = await Assert.ThrowsAsync<RedisConnectionException>(async () =>
{
using (var conn = await ConnectionMultiplexer.ConnectAsync($"doesnot.exist.ds.{Guid.NewGuid():N}.com:6500", Writer).ForAwait()) { }
}).ForAwait();
Log(ex.ToString());
}
[Fact]
public void CreateDisconnectedNonsenseConnection_IP()
{
using (var conn = ConnectionMultiplexer.Connect(TestConfig.Current.MasterServer + ":6500,abortConnect=false", Writer))
{
Assert.False(conn.GetServer(conn.GetEndPoints().Single()).IsConnected);
Assert.False(conn.GetDatabase().IsConnected(default(RedisKey)));
}
}
[Fact]
public void CreateDisconnectedNonsenseConnection_DNS()
{
using (var conn = ConnectionMultiplexer.Connect($"doesnot.exist.ds.{Guid.NewGuid():N}.com:6500, abortConnect=false", Writer))
{
Assert.False(conn.GetServer(conn.GetEndPoints().Single()).IsConnected);
Assert.False(conn.GetDatabase().IsConnected(default(RedisKey)));
}
}
} }
} }
using System.Runtime.CompilerServices; using System.Threading.Tasks;
using System.Threading.Tasks;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
namespace StackExchange.Redis.Tests.Booksleeve namespace StackExchange.Redis.Tests
{ {
public class Constraints : BookSleeveTestBase public class Constraints : TestBase
{ {
public Constraints(ITestOutputHelper output) : base(output) { } public Constraints(ITestOutputHelper output) : base(output) { }
...@@ -20,7 +19,7 @@ public void ValueEquals() ...@@ -20,7 +19,7 @@ public void ValueEquals()
[Fact] [Fact]
public void TestManualIncr() public void TestManualIncr()
{ {
using (var muxer = GetUnsecuredConnection(syncTimeout: 120000)) // big timeout while debugging using (var muxer = Create(syncTimeout: 120000)) // big timeout while debugging
{ {
var key = Me(); var key = Me();
var conn = muxer.GetDatabase(); var conn = muxer.GetDatabase();
......
...@@ -5,16 +5,13 @@ ...@@ -5,16 +5,13 @@
// a specific target and scoped to a namespace, type, member, etc. // a specific target and scoped to a namespace, type, member, etc.
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Redundancy", "RCS1163:Unused parameter.", Justification = "<Pending>", Scope = "member", Target = "~M:StackExchange.Redis.Tests.ConnectionFailedErrors.SSLCertificateValidationError(System.Boolean)")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Redundancy", "RCS1163:Unused parameter.", Justification = "<Pending>", Scope = "member", Target = "~M:StackExchange.Redis.Tests.ConnectionFailedErrors.SSLCertificateValidationError(System.Boolean)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Redundancy", "RCS1163:Unused parameter.", Justification = "<Pending>", Scope = "member", Target = "~M:StackExchange.Redis.Tests.Booksleeve.PubSub.TestMultipleSubscribersGetMessage")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Redundancy", "RCS1163:Unused parameter.", Justification = "<Pending>", Scope = "member", Target = "~M:StackExchange.Redis.Tests.PubSub.ExplicitPublishMode")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Redundancy", "RCS1163:Unused parameter.", Justification = "<Pending>", Scope = "member", Target = "~M:StackExchange.Redis.Tests.PubSub.ExplicitPublishMode")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Redundancy", "RCS1163:Unused parameter.", Justification = "<Pending>", Scope = "member", Target = "~M:StackExchange.Redis.Tests.PubSubNonParallel.SubscriptionsSurviveMasterSwitch(System.Boolean)")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Redundancy", "RCS1163:Unused parameter.", Justification = "<Pending>", Scope = "member", Target = "~M:StackExchange.Redis.Tests.PubSubNonParallel.SubscriptionsSurviveMasterSwitch(System.Boolean)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Redundancy", "RCS1163:Unused parameter.", Justification = "<Pending>", Scope = "member", Target = "~M:StackExchange.Redis.Tests.PubSub.TestBasicPubSubFireAndForget(System.Boolean)")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Redundancy", "RCS1163:Unused parameter.", Justification = "<Pending>", Scope = "member", Target = "~M:StackExchange.Redis.Tests.PubSub.TestBasicPubSubFireAndForget(System.Boolean)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Redundancy", "RCS1163:Unused parameter.", Justification = "<Pending>", Scope = "member", Target = "~M:StackExchange.Redis.Tests.Booksleeve.PubSub.Issue38")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Redundancy", "RCS1163:Unused parameter.", Justification = "<Pending>", Scope = "member", Target = "~M:StackExchange.Redis.Tests.SSL.ConnectToSSLServer(System.Boolean,System.Boolean)")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Redundancy", "RCS1163:Unused parameter.", Justification = "<Pending>", Scope = "member", Target = "~M:StackExchange.Redis.Tests.SSL.ConnectToSSLServer(System.Boolean,System.Boolean)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Redundancy", "RCS1163:Unused parameter.", Justification = "<Pending>", Scope = "member", Target = "~M:StackExchange.Redis.Tests.PubSub.TestPatternPubSub(System.Boolean)")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Redundancy", "RCS1163:Unused parameter.", Justification = "<Pending>", Scope = "member", Target = "~M:StackExchange.Redis.Tests.PubSub.TestPatternPubSub(System.Boolean)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Redundancy", "RCS1163:Unused parameter.", Justification = "<Pending>", Scope = "member", Target = "~M:StackExchange.Redis.Tests.PubSub.TestBasicPubSub(System.Boolean,System.String,System.Boolean)")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Redundancy", "RCS1163:Unused parameter.", Justification = "<Pending>", Scope = "member", Target = "~M:StackExchange.Redis.Tests.PubSub.TestBasicPubSub(System.Boolean,System.String,System.Boolean)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Redundancy", "RCS1163:Unused parameter.", Justification = "<Pending>", Scope = "member", Target = "~M:StackExchange.Redis.Tests.SSL.ConnectToSSLServer(System.Boolean,System.Boolean)")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Redundancy", "RCS1163:Unused parameter.", Justification = "<Pending>", Scope = "member", Target = "~M:StackExchange.Redis.Tests.SSL.ConnectToSSLServer(System.Boolean,System.Boolean)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Redundancy", "RCS1163:Unused parameter.", Justification = "<Pending>", Scope = "member", Target = "~M:StackExchange.Redis.Tests.Booksleeve.PubSub.PubSubOrder~System.Threading.Tasks.Task")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Redundancy", "RCS1163:Unused parameter.", Justification = "<Pending>", Scope = "member", Target = "~M:StackExchange.Redis.Tests.PreserveOrder.Execute(System.Boolean)")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Redundancy", "RCS1163:Unused parameter.", Justification = "<Pending>", Scope = "member", Target = "~M:StackExchange.Redis.Tests.PreserveOrder.Execute(System.Boolean)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "xUnit1004:Test methods should not be skipped", Justification = "<Pending>", Scope = "member", Target = "~M:StackExchange.Redis.Tests.ConnectionShutdown.ShutdownRaisesConnectionFailedAndRestore")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "xUnit1004:Test methods should not be skipped", Justification = "<Pending>", Scope = "member", Target = "~M:StackExchange.Redis.Tests.ConnectionShutdown.ShutdownRaisesConnectionFailedAndRestore")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "xUnit1004:Test methods should not be skipped", Justification = "<Pending>", Scope = "member", Target = "~M:StackExchange.Redis.Tests.Issues.BgSaveResponse.ShouldntThrowException(StackExchange.Redis.SaveType)")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "xUnit1004:Test methods should not be skipped", Justification = "<Pending>", Scope = "member", Target = "~M:StackExchange.Redis.Tests.Issues.BgSaveResponse.ShouldntThrowException(StackExchange.Redis.SaveType)")]
......
...@@ -7,16 +7,16 @@ ...@@ -7,16 +7,16 @@
using Xunit.Abstractions; using Xunit.Abstractions;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace StackExchange.Redis.Tests.Booksleeve namespace StackExchange.Redis.Tests
{ {
public class Hashes : BookSleeveTestBase // https://redis.io/commands#hash public class Hashes : TestBase // https://redis.io/commands#hash
{ {
public Hashes(ITestOutputHelper output) : base(output) { } public Hashes(ITestOutputHelper output) : base(output) { }
[Fact] [Fact]
public async Task TestIncrBy() public async Task TestIncrBy()
{ {
using (var muxer = GetUnsecuredConnection()) using (var muxer = Create())
{ {
var conn = muxer.GetDatabase(); var conn = muxer.GetDatabase();
var key = Me(); var key = Me();
...@@ -34,7 +34,7 @@ public async Task TestIncrBy() ...@@ -34,7 +34,7 @@ public async Task TestIncrBy()
[Fact] [Fact]
public void Scan() public void Scan()
{ {
using (var muxer = GetUnsecuredConnection(waitForOpen: true)) using (var muxer = Create())
{ {
Skip.IfMissingFeature(muxer, nameof(RedisFeatures.Scan), r => r.Scan); Skip.IfMissingFeature(muxer, nameof(RedisFeatures.Scan), r => r.Scan);
var conn = muxer.GetDatabase(); var conn = muxer.GetDatabase();
...@@ -74,7 +74,7 @@ public void Scan() ...@@ -74,7 +74,7 @@ public void Scan()
[Fact] [Fact]
public void TestIncrementOnHashThatDoesntExist() public void TestIncrementOnHashThatDoesntExist()
{ {
using (var muxer = GetUnsecuredConnection()) using (var muxer = Create())
{ {
var conn = muxer.GetDatabase(); var conn = muxer.GetDatabase();
conn.KeyDeleteAsync("keynotexist"); conn.KeyDeleteAsync("keynotexist");
...@@ -88,7 +88,7 @@ public void TestIncrementOnHashThatDoesntExist() ...@@ -88,7 +88,7 @@ public void TestIncrementOnHashThatDoesntExist()
[Fact] [Fact]
public async Task TestIncrByFloat() public async Task TestIncrByFloat()
{ {
using (var muxer = GetUnsecuredConnection(waitForOpen: true)) using (var muxer = Create())
{ {
Skip.IfMissingFeature(muxer, nameof(RedisFeatures.IncrementFloat), r => r.IncrementFloat); Skip.IfMissingFeature(muxer, nameof(RedisFeatures.IncrementFloat), r => r.IncrementFloat);
var conn = muxer.GetDatabase(); var conn = muxer.GetDatabase();
...@@ -105,7 +105,7 @@ public async Task TestIncrByFloat() ...@@ -105,7 +105,7 @@ public async Task TestIncrByFloat()
[Fact] [Fact]
public async Task TestGetAll() public async Task TestGetAll()
{ {
using (var muxer = GetUnsecuredConnection()) using (var muxer = Create())
{ {
var conn = muxer.GetDatabase(); var conn = muxer.GetDatabase();
var key = Me(); var key = Me();
...@@ -137,7 +137,7 @@ public async Task TestGetAll() ...@@ -137,7 +137,7 @@ public async Task TestGetAll()
[Fact] [Fact]
public async Task TestGet() public async Task TestGet()
{ {
using (var muxer = GetUnsecuredConnection()) using (var muxer = Create())
{ {
var key = Me(); var key = Me();
var conn = muxer.GetDatabase(); var conn = muxer.GetDatabase();
...@@ -167,7 +167,7 @@ public async Task TestGet() ...@@ -167,7 +167,7 @@ public async Task TestGet()
[Fact] [Fact]
public async Task TestSet() // https://redis.io/commands/hset public async Task TestSet() // https://redis.io/commands/hset
{ {
using (var muxer = GetUnsecuredConnection()) using (var muxer = Create())
{ {
var conn = muxer.GetDatabase(); var conn = muxer.GetDatabase();
var hashkey = Me(); var hashkey = Me();
...@@ -206,7 +206,7 @@ public async Task TestGet() ...@@ -206,7 +206,7 @@ public async Task TestGet()
[Fact] [Fact]
public async Task TestSetNotExists() // https://redis.io/commands/hsetnx public async Task TestSetNotExists() // https://redis.io/commands/hsetnx
{ {
using (var muxer = GetUnsecuredConnection()) using (var muxer = Create())
{ {
var conn = muxer.GetDatabase(); var conn = muxer.GetDatabase();
var hashkey = Me(); var hashkey = Me();
...@@ -237,7 +237,7 @@ public async Task TestGet() ...@@ -237,7 +237,7 @@ public async Task TestGet()
[Fact] [Fact]
public async Task TestDelSingle() // https://redis.io/commands/hdel public async Task TestDelSingle() // https://redis.io/commands/hdel
{ {
using (var muxer = GetUnsecuredConnection()) using (var muxer = Create())
{ {
var conn = muxer.GetDatabase(); var conn = muxer.GetDatabase();
var hashkey = Me(); var hashkey = Me();
...@@ -258,7 +258,7 @@ public async Task TestGet() ...@@ -258,7 +258,7 @@ public async Task TestGet()
[Fact] [Fact]
public void TestDelMulti() // https://redis.io/commands/hdel public void TestDelMulti() // https://redis.io/commands/hdel
{ {
using (var muxer = GetUnsecuredConnection()) using (var muxer = Create())
{ {
var conn = muxer.GetDatabase(); var conn = muxer.GetDatabase();
var hashkey = Me(); var hashkey = Me();
...@@ -296,7 +296,7 @@ public async Task TestGet() ...@@ -296,7 +296,7 @@ public async Task TestGet()
[Fact] [Fact]
public void TestDelMultiInsideTransaction() // https://redis.io/commands/hdel public void TestDelMultiInsideTransaction() // https://redis.io/commands/hdel
{ {
using (var outer = GetUnsecuredConnection()) using (var outer = Create())
{ {
var conn = outer.GetDatabase().CreateTransaction(); var conn = outer.GetDatabase().CreateTransaction();
{ {
...@@ -333,7 +333,7 @@ public async Task TestGet() ...@@ -333,7 +333,7 @@ public async Task TestGet()
[Fact] [Fact]
public async Task TestExists() // https://redis.io/commands/hexists public async Task TestExists() // https://redis.io/commands/hexists
{ {
using (var muxer = GetUnsecuredConnection()) using (var muxer = Create())
{ {
var conn = muxer.GetDatabase(); var conn = muxer.GetDatabase();
var hashkey = Me(); var hashkey = Me();
...@@ -353,7 +353,7 @@ public async Task TestGet() ...@@ -353,7 +353,7 @@ public async Task TestGet()
[Fact] [Fact]
public async Task TestHashKeys() // https://redis.io/commands/hkeys public async Task TestHashKeys() // https://redis.io/commands/hkeys
{ {
using (var muxer = GetUnsecuredConnection()) using (var muxer = Create())
{ {
var conn = muxer.GetDatabase(); var conn = muxer.GetDatabase();
var hashKey = Me(); var hashKey = Me();
...@@ -378,7 +378,7 @@ public async Task TestGet() ...@@ -378,7 +378,7 @@ public async Task TestGet()
[Fact] [Fact]
public async Task TestHashValues() // https://redis.io/commands/hvals public async Task TestHashValues() // https://redis.io/commands/hvals
{ {
using (var muxer = GetUnsecuredConnection()) using (var muxer = Create())
{ {
var conn = muxer.GetDatabase(); var conn = muxer.GetDatabase();
var hashkey = Me(); var hashkey = Me();
...@@ -403,7 +403,7 @@ public async Task TestGet() ...@@ -403,7 +403,7 @@ public async Task TestGet()
[Fact] [Fact]
public async Task TestHashLength() // https://redis.io/commands/hlen public async Task TestHashLength() // https://redis.io/commands/hlen
{ {
using (var muxer = GetUnsecuredConnection()) using (var muxer = Create())
{ {
var conn = muxer.GetDatabase(); var conn = muxer.GetDatabase();
var hashkey = Me(); var hashkey = Me();
...@@ -424,7 +424,7 @@ public async Task TestGet() ...@@ -424,7 +424,7 @@ public async Task TestGet()
[Fact] [Fact]
public async Task TestGetMulti() // https://redis.io/commands/hmget public async Task TestGetMulti() // https://redis.io/commands/hmget
{ {
using (var muxer = GetUnsecuredConnection()) using (var muxer = Create())
{ {
var conn = muxer.GetDatabase(); var conn = muxer.GetDatabase();
var hashkey = Me(); var hashkey = Me();
...@@ -459,7 +459,7 @@ public async Task TestGet() ...@@ -459,7 +459,7 @@ public async Task TestGet()
[Fact] [Fact]
public async Task TestGetPairs() // https://redis.io/commands/hgetall public async Task TestGetPairs() // https://redis.io/commands/hgetall
{ {
using (var muxer = GetUnsecuredConnection()) using (var muxer = Create())
{ {
var conn = muxer.GetDatabase(); var conn = muxer.GetDatabase();
var hashkey = Me(); var hashkey = Me();
...@@ -483,7 +483,7 @@ public async Task TestGet() ...@@ -483,7 +483,7 @@ public async Task TestGet()
[Fact] [Fact]
public async Task TestSetPairs() // https://redis.io/commands/hmset public async Task TestSetPairs() // https://redis.io/commands/hmset
{ {
using (var muxer = GetUnsecuredConnection()) using (var muxer = Create())
{ {
var conn = muxer.GetDatabase(); var conn = muxer.GetDatabase();
var hashkey = Me(); var hashkey = Me();
......
...@@ -75,6 +75,7 @@ public class Config ...@@ -75,6 +75,7 @@ public class Config
public string RemoteServer { get; set; } = "127.0.0.1"; public string RemoteServer { get; set; } = "127.0.0.1";
public int RemotePort { get; set; } = 6379; public int RemotePort { get; set; } = 6379;
public string RemoteServerAndPort => RemoteServer + ":" + RemotePort.ToString();
public string SentinelServer { get; set; } = "127.0.0.1"; public string SentinelServer { get; set; } = "127.0.0.1";
public int SentinelPort { get; set; } = 26379; public int SentinelPort { get; set; } = 26379;
......
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
namespace StackExchange.Redis.Tests.Booksleeve.Issues namespace StackExchange.Redis.Tests.Issues
{ {
public class Issue10 : BookSleeveTestBase public class Issue10 : TestBase
{ {
public Issue10(ITestOutputHelper output) : base(output) { } public Issue10(ITestOutputHelper output) : base(output) { }
[Fact] [Fact]
public void Execute() public void Execute()
{ {
using (var muxer = GetUnsecuredConnection()) using (var muxer = Create())
{ {
var key = Me(); var key = Me();
var conn = muxer.GetDatabase(); var conn = muxer.GetDatabase();
......
...@@ -4,16 +4,16 @@ ...@@ -4,16 +4,16 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Xunit.Abstractions; using Xunit.Abstractions;
namespace StackExchange.Redis.Tests.Booksleeve.Issues namespace StackExchange.Redis.Tests.Issues
{ {
public class Massive_Delete : BookSleeveTestBase public class Massive_Delete : TestBase
{ {
public Massive_Delete(ITestOutputHelper output) : base(output) { } public Massive_Delete(ITestOutputHelper output) : base(output) { }
private void Prep(int db, string key) private void Prep(int db, string key)
{ {
var prefix = Me(); var prefix = Me();
using (var muxer = GetUnsecuredConnection(allowAdmin: true)) using (var muxer = Create(allowAdmin: true))
{ {
Skip.IfMissingDatabase(muxer, db); Skip.IfMissingDatabase(muxer, db);
GetServer(muxer).FlushDatabase(db); GetServer(muxer).FlushDatabase(db);
...@@ -36,7 +36,7 @@ public async Task ExecuteMassiveDelete() ...@@ -36,7 +36,7 @@ public async Task ExecuteMassiveDelete()
var key = Me(); var key = Me();
Prep(dbId, key); Prep(dbId, key);
var watch = Stopwatch.StartNew(); var watch = Stopwatch.StartNew();
using (var muxer = GetUnsecuredConnection()) using (var muxer = Create())
using (var throttle = new SemaphoreSlim(1)) using (var throttle = new SemaphoreSlim(1))
{ {
var conn = muxer.GetDatabase(dbId); var conn = muxer.GetDatabase(dbId);
......
...@@ -3,9 +3,9 @@ ...@@ -3,9 +3,9 @@
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
namespace StackExchange.Redis.Tests.Booksleeve.Issues namespace StackExchange.Redis.Tests.Issues
{ {
public class SO10504853 : BookSleeveTestBase public class SO10504853 : TestBase
{ {
public SO10504853(ITestOutputHelper output) : base(output) { } public SO10504853(ITestOutputHelper output) : base(output) { }
...@@ -13,7 +13,7 @@ public class SO10504853 : BookSleeveTestBase ...@@ -13,7 +13,7 @@ public class SO10504853 : BookSleeveTestBase
public void LoopLotsOfTrivialStuff() public void LoopLotsOfTrivialStuff()
{ {
Trace.WriteLine("### init"); Trace.WriteLine("### init");
using (var muxer = GetUnsecuredConnection()) using (var muxer = Create())
{ {
var conn = muxer.GetDatabase(); var conn = muxer.GetDatabase();
conn.KeyDelete("lots-trivial"); conn.KeyDelete("lots-trivial");
...@@ -22,14 +22,14 @@ public void LoopLotsOfTrivialStuff() ...@@ -22,14 +22,14 @@ public void LoopLotsOfTrivialStuff()
for (int i = 0; i < COUNT; i++) for (int i = 0; i < COUNT; i++)
{ {
Trace.WriteLine("### incr:" + i); Trace.WriteLine("### incr:" + i);
using (var muxer = GetUnsecuredConnection()) using (var muxer = Create())
{ {
var conn = muxer.GetDatabase(); var conn = muxer.GetDatabase();
Assert.Equal(i + 1, conn.StringIncrement("lots-trivial")); Assert.Equal(i + 1, conn.StringIncrement("lots-trivial"));
} }
} }
Trace.WriteLine("### close"); Trace.WriteLine("### close");
using (var muxer = GetUnsecuredConnection()) using (var muxer = Create())
{ {
var conn = muxer.GetDatabase(); var conn = muxer.GetDatabase();
Assert.Equal(COUNT, (long)conn.StringGet("lots-trivial")); Assert.Equal(COUNT, (long)conn.StringGet("lots-trivial"));
...@@ -39,7 +39,7 @@ public void LoopLotsOfTrivialStuff() ...@@ -39,7 +39,7 @@ public void LoopLotsOfTrivialStuff()
[Fact] [Fact]
public void ExecuteWithEmptyStartingPoint() public void ExecuteWithEmptyStartingPoint()
{ {
using (var muxer = GetUnsecuredConnection()) using (var muxer = Create())
{ {
var conn = muxer.GetDatabase(); var conn = muxer.GetDatabase();
var task = new { priority = 3 }; var task = new { priority = 3 };
...@@ -62,7 +62,7 @@ public void ExecuteWithNonHashStartingPoint() ...@@ -62,7 +62,7 @@ public void ExecuteWithNonHashStartingPoint()
{ {
Assert.Throws<RedisServerException>(() => Assert.Throws<RedisServerException>(() =>
{ {
using (var muxer = GetUnsecuredConnection()) using (var muxer = Create())
{ {
var conn = muxer.GetDatabase(); var conn = muxer.GetDatabase();
var task = new { priority = 3 }; var task = new { priority = 3 };
......
...@@ -3,16 +3,16 @@ ...@@ -3,16 +3,16 @@
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
namespace StackExchange.Redis.Tests.Booksleeve.Issues namespace StackExchange.Redis.Tests.Issues
{ {
public class SO10825542 : BookSleeveTestBase public class SO10825542 : TestBase
{ {
public SO10825542(ITestOutputHelper output) : base(output) { } public SO10825542(ITestOutputHelper output) : base(output) { }
[Fact] [Fact]
public void Execute() public void Execute()
{ {
using (var muxer = GetUnsecuredConnection()) using (var muxer = Create())
{ {
var key = Me(); var key = Me();
......
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
namespace StackExchange.Redis.Tests.Booksleeve.Issues namespace StackExchange.Redis.Tests.Issues
{ {
public class SO11766033 : BookSleeveTestBase public class SO11766033 : TestBase
{ {
public SO11766033(ITestOutputHelper output) : base(output) { } public SO11766033(ITestOutputHelper output) : base(output) { }
[Fact] [Fact]
public void TestNullString() public void TestNullString()
{ {
using (var muxer = GetUnsecuredConnection(true)) using (var muxer = Create())
{ {
var redis = muxer.GetDatabase(); var redis = muxer.GetDatabase();
const string expectedTestValue = null; const string expectedTestValue = null;
...@@ -25,7 +25,7 @@ public void TestNullString() ...@@ -25,7 +25,7 @@ public void TestNullString()
[Fact] [Fact]
public void TestEmptyString() public void TestEmptyString()
{ {
using (var muxer = GetUnsecuredConnection(true)) using (var muxer = Create())
{ {
var redis = muxer.GetDatabase(); var redis = muxer.GetDatabase();
const string expectedTestValue = ""; const string expectedTestValue = "";
......
...@@ -4,9 +4,9 @@ ...@@ -4,9 +4,9 @@
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
namespace StackExchange.Redis.Tests.Booksleeve namespace StackExchange.Redis.Tests
{ {
public class Performance : BookSleeveTestBase public class Performance : TestBase
{ {
public Performance(ITestOutputHelper output) : base(output) { } public Performance(ITestOutputHelper output) : base(output) { }
...@@ -15,7 +15,7 @@ public void VerifyPerformanceImprovement() ...@@ -15,7 +15,7 @@ public void VerifyPerformanceImprovement()
{ {
int asyncTimer, sync, op = 0, asyncFaF, syncFaF; int asyncTimer, sync, op = 0, asyncFaF, syncFaF;
var key = Me(); var key = Me();
using (var muxer = GetUnsecuredConnection()) using (var muxer = Create())
{ {
// do these outside the timings, just to ensure the core methods are JITted etc // do these outside the timings, just to ensure the core methods are JITted etc
for (int db = 0; db < 5; db++) for (int db = 0; db < 5; db++)
...@@ -101,7 +101,7 @@ public void VerifyPerformanceImprovement() ...@@ -101,7 +101,7 @@ public void VerifyPerformanceImprovement()
[Fact] [Fact]
public async Task BasicStringGetPerf() public async Task BasicStringGetPerf()
{ {
using (var conn = GetUnsecuredConnection()) using (var conn = Create())
{ {
RedisKey key = Me(); RedisKey key = Me();
var db = conn.GetDatabase(); var db = conn.GetDatabase();
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks; using System.Threading.Tasks;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
...@@ -73,7 +76,7 @@ public async Task TestBasicPubSub(string channelPrefix, bool wildCard, string br ...@@ -73,7 +76,7 @@ public async Task TestBasicPubSub(string channelPrefix, bool wildCard, string br
} }
} }
} }
, handler2 = (channel, payload) => Interlocked.Increment(ref secondHandler); , handler2 = (_, __) => Interlocked.Increment(ref secondHandler);
sub.Subscribe(subChannel, handler1); sub.Subscribe(subChannel, handler1);
sub.Subscribe(subChannel, handler2); sub.Subscribe(subChannel, handler2);
...@@ -139,7 +142,7 @@ public async Task TestBasicPubSubFireAndForget() ...@@ -139,7 +142,7 @@ public async Task TestBasicPubSubFireAndForget()
} }
}, CommandFlags.FireAndForget); }, CommandFlags.FireAndForget);
sub.Subscribe(key, (channel, payload) => Interlocked.Increment(ref secondHandler), CommandFlags.FireAndForget); sub.Subscribe(key, (_, __) => Interlocked.Increment(ref secondHandler), CommandFlags.FireAndForget);
lock (received) lock (received)
{ {
...@@ -204,7 +207,7 @@ public async Task TestPatternPubSub() ...@@ -204,7 +207,7 @@ public async Task TestPatternPubSub()
} }
}); });
sub.Subscribe("a*c", (channel, payload) => Interlocked.Increment(ref secondHandler)); sub.Subscribe("a*c", (_, __) => Interlocked.Increment(ref secondHandler));
lock (received) lock (received)
{ {
Assert.Empty(received); Assert.Empty(received);
...@@ -233,6 +236,449 @@ public async Task TestPatternPubSub() ...@@ -233,6 +236,449 @@ public async Task TestPatternPubSub()
} }
} }
[Fact]
public void TestPublishWithNoSubscribers()
{
using (var muxer = Create())
{
var conn = muxer.GetSubscriber();
Assert.Equal(0, conn.Publish(Me() + "channel", "message"));
}
}
[FactLongRunning]
public void TestMassivePublishWithWithoutFlush_Local()
{
using (var muxer = Create())
{
var conn = muxer.GetSubscriber();
TestMassivePublish(conn, Me(), "local");
}
}
[FactLongRunning]
public void TestMassivePublishWithWithoutFlush_Remote()
{
using (var muxer = Create(configuration: TestConfig.Current.RemoteServerAndPort))
{
var conn = muxer.GetSubscriber();
TestMassivePublish(conn, Me(), "remote");
}
}
private void TestMassivePublish(ISubscriber conn, string channel, string caption)
{
const int loop = 10000;
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
var tasks = new Task[loop];
var withFAF = Stopwatch.StartNew();
for (int i = 0; i < loop; i++)
{
conn.Publish(channel, "bar", CommandFlags.FireAndForget);
}
withFAF.Stop();
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
var withAsync = Stopwatch.StartNew();
for (int i = 0; i < loop; i++)
{
tasks[i] = conn.PublishAsync(channel, "bar");
}
conn.WaitAll(tasks);
withAsync.Stop();
Log("{2}: {0}ms (F+F) vs {1}ms (async)",
withFAF.ElapsedMilliseconds, withAsync.ElapsedMilliseconds, caption);
// We've made async so far, this test isn't really valid anymore
// So let's check they're at least within a few seconds.
Assert.True(withFAF.ElapsedMilliseconds < withAsync.ElapsedMilliseconds + 3000, caption);
}
[FactLongRunning]
public async Task PubSubGetAllAnyOrder()
{
using (var muxer = Create(syncTimeout: 20000))
{
var sub = muxer.GetSubscriber();
RedisChannel channel = Me();
const int count = 1000;
var syncLock = new object();
var data = new HashSet<int>();
await sub.SubscribeAsync(channel, (_, val) =>
{
bool pulse;
lock (data)
{
data.Add(int.Parse(Encoding.UTF8.GetString(val)));
pulse = data.Count == count;
if ((data.Count % 100) == 99) Log(data.Count.ToString());
}
if (pulse)
{
lock (syncLock)
{
Monitor.PulseAll(syncLock);
}
}
}).ForAwait();
lock (syncLock)
{
for (int i = 0; i < count; i++)
{
sub.Publish(channel, i.ToString(), CommandFlags.FireAndForget);
}
sub.Ping();
if (!Monitor.Wait(syncLock, 20000))
{
throw new TimeoutException("Items: " + data.Count);
}
for (int i = 0; i < count; i++)
{
Assert.Contains(i, data);
}
}
}
}
[Fact]
public async Task PubSubGetAllCorrectOrder()
{
using (var muxer = Create(configuration: TestConfig.Current.RemoteServerAndPort, syncTimeout: 20000))
{
var sub = muxer.GetSubscriber();
RedisChannel channel = Me();
const int count = 1000;
var syncLock = new object();
var data = new List<int>(count);
var subChannel = await sub.SubscribeAsync(channel).ForAwait();
await sub.PingAsync().ForAwait();
async Task RunLoop()
{
while (!subChannel.IsCompleted)
{
var work = await subChannel.ReadAsync().ForAwait();
int i = int.Parse(Encoding.UTF8.GetString(work.Message));
lock (data)
{
data.Add(i);
if (data.Count == count) break;
if ((data.Count % 100) == 99) Log(data.Count.ToString());
}
}
lock (syncLock)
{
Monitor.PulseAll(syncLock);
}
}
lock (syncLock)
{
Task.Run(RunLoop);
for (int i = 0; i < count; i++)
{
sub.Publish(channel, i.ToString(), CommandFlags.FireAndForget);
}
if (!Monitor.Wait(syncLock, 20000))
{
throw new TimeoutException("Items: " + data.Count);
}
subChannel.Unsubscribe();
sub.Ping();
muxer.GetDatabase().Ping();
for (int i = 0; i < count; i++)
{
Assert.Equal(i, data[i]);
}
}
Assert.True(subChannel.IsCompleted);
await Assert.ThrowsAsync<ChannelClosedException>(async delegate
{
var final = await subChannel.ReadAsync().ForAwait();
}).ForAwait();
}
}
[Fact]
public async Task PubSubGetAllCorrectOrder_OnMessage_Sync()
{
using (var muxer = Create(configuration: TestConfig.Current.RemoteServerAndPort, syncTimeout: 20000))
{
var sub = muxer.GetSubscriber();
RedisChannel channel = Me();
const int count = 1000;
var syncLock = new object();
var data = new List<int>(count);
var subChannel = await sub.SubscribeAsync(channel).ForAwait();
subChannel.OnMessage(msg =>
{
int i = int.Parse(Encoding.UTF8.GetString(msg.Message));
bool pulse = false;
lock (data)
{
data.Add(i);
if (data.Count == count) pulse = true;
if ((data.Count % 100) == 99) Log(data.Count.ToString());
}
if (pulse)
{
lock (syncLock)
{
Monitor.PulseAll(syncLock);
}
}
});
await sub.PingAsync().ForAwait();
lock (syncLock)
{
for (int i = 0; i < count; i++)
{
sub.Publish(channel, i.ToString(), CommandFlags.FireAndForget);
}
if (!Monitor.Wait(syncLock, 20000))
{
throw new TimeoutException("Items: " + data.Count);
}
subChannel.Unsubscribe();
sub.Ping();
muxer.GetDatabase().Ping();
for (int i = 0; i < count; i++)
{
Assert.Equal(i, data[i]);
}
}
Assert.True(subChannel.IsCompleted);
await Assert.ThrowsAsync<ChannelClosedException>(async delegate
{
var final = await subChannel.ReadAsync().ForAwait();
}).ForAwait();
}
}
[Fact]
public async Task PubSubGetAllCorrectOrder_OnMessage_Async()
{
using (var muxer = Create(configuration: TestConfig.Current.RemoteServerAndPort, syncTimeout: 20000))
{
var sub = muxer.GetSubscriber();
RedisChannel channel = Me();
const int count = 1000;
var syncLock = new object();
var data = new List<int>(count);
var subChannel = await sub.SubscribeAsync(channel).ForAwait();
subChannel.OnMessage(msg =>
{
int i = int.Parse(Encoding.UTF8.GetString(msg.Message));
bool pulse = false;
lock (data)
{
data.Add(i);
if (data.Count == count) pulse = true;
if ((data.Count % 100) == 99) Log(data.Count.ToString());
}
if (pulse)
{
lock (syncLock)
{
Monitor.PulseAll(syncLock);
}
}
return i % 2 == 0 ? null : Task.CompletedTask;
});
await sub.PingAsync().ForAwait();
lock (syncLock)
{
for (int i = 0; i < count; i++)
{
sub.Publish(channel, i.ToString(), CommandFlags.FireAndForget);
}
if (!Monitor.Wait(syncLock, 20000))
{
throw new TimeoutException("Items: " + data.Count);
}
subChannel.Unsubscribe();
sub.Ping();
muxer.GetDatabase().Ping();
for (int i = 0; i < count; i++)
{
Assert.Equal(i, data[i]);
}
}
Assert.True(subChannel.IsCompleted);
await Assert.ThrowsAsync<ChannelClosedException>(async delegate
{
var final = await subChannel.ReadAsync().ForAwait();
}).ForAwait();
}
}
[Fact]
public void TestPublishWithSubscribers()
{
var channel = Me();
using (var muxerA = Create())
using (var muxerB = Create())
using (var conn = Create())
{
var listenA = muxerA.GetSubscriber();
var listenB = muxerB.GetSubscriber();
var t1 = listenA.SubscribeAsync(channel, delegate { });
var t2 = listenB.SubscribeAsync(channel, delegate { });
listenA.Wait(t1);
listenB.Wait(t2);
var pub = conn.GetSubscriber().PublishAsync(channel, "message");
Assert.Equal(2, conn.Wait(pub)); // delivery count
}
}
[Fact]
public async Task TestMultipleSubscribersGetMessage()
{
var channel = Me();
using (var muxerA = Create())
using (var muxerB = Create())
using (var conn = Create())
{
var listenA = muxerA.GetSubscriber();
var listenB = muxerB.GetSubscriber();
conn.GetDatabase().Ping();
var pub = conn.GetSubscriber();
int gotA = 0, gotB = 0;
var tA = listenA.SubscribeAsync(channel, (s, msg) => { if (msg == "message") Interlocked.Increment(ref gotA); });
var tB = listenB.SubscribeAsync(channel, (s, msg) => { if (msg == "message") Interlocked.Increment(ref gotB); });
listenA.Wait(tA);
listenB.Wait(tB);
Assert.Equal(2, pub.Publish(channel, "message"));
await AllowReasonableTimeToPublishAndProcess().ForAwait();
Assert.Equal(1, Interlocked.CompareExchange(ref gotA, 0, 0));
Assert.Equal(1, Interlocked.CompareExchange(ref gotB, 0, 0));
// and unsubscibe...
tA = listenA.UnsubscribeAsync(channel);
listenA.Wait(tA);
Assert.Equal(1, pub.Publish(channel, "message"));
await AllowReasonableTimeToPublishAndProcess().ForAwait();
Assert.Equal(1, Interlocked.CompareExchange(ref gotA, 0, 0));
Assert.Equal(2, Interlocked.CompareExchange(ref gotB, 0, 0));
}
}
[Fact]
public async Task Issue38()
{
// https://code.google.com/p/booksleeve/issues/detail?id=38
using (var pub = Create())
{
var sub = pub.GetSubscriber();
int count = 0;
var prefix = Me();
void handler(RedisChannel _, RedisValue __) => Interlocked.Increment(ref count);
var a0 = sub.SubscribeAsync(prefix + "foo", handler);
var a1 = sub.SubscribeAsync(prefix + "bar", handler);
var b0 = sub.SubscribeAsync(prefix + "f*o", handler);
var b1 = sub.SubscribeAsync(prefix + "b*r", handler);
sub.WaitAll(a0, a1, b0, b1);
var c = sub.PublishAsync(prefix + "foo", "foo");
var d = sub.PublishAsync(prefix + "f@o", "f@o");
var e = sub.PublishAsync(prefix + "bar", "bar");
var f = sub.PublishAsync(prefix + "b@r", "b@r");
pub.WaitAll(c, d, e, f);
long total = c.Result + d.Result + e.Result + f.Result;
await AllowReasonableTimeToPublishAndProcess().ForAwait();
Assert.Equal(6, total); // sent
Assert.Equal(6, Interlocked.CompareExchange(ref count, 0, 0)); // received
}
}
internal static Task AllowReasonableTimeToPublishAndProcess() => Task.Delay(100);
[Fact]
public async Task TestPartialSubscriberGetMessage()
{
using (var muxerA = Create())
using (var muxerB = Create())
using (var conn = Create())
{
int gotA = 0, gotB = 0;
var listenA = muxerA.GetSubscriber();
var listenB = muxerB.GetSubscriber();
var pub = conn.GetSubscriber();
var prefix = Me();
var tA = listenA.SubscribeAsync(prefix + "channel", (s, msg) => { if (s == prefix + "channel" && msg == "message") Interlocked.Increment(ref gotA); });
var tB = listenB.SubscribeAsync(prefix + "chann*", (s, msg) => { if (s == prefix + "channel" && msg == "message") Interlocked.Increment(ref gotB); });
listenA.Wait(tA);
listenB.Wait(tB);
Assert.Equal(2, pub.Publish(prefix + "channel", "message"));
await AllowReasonableTimeToPublishAndProcess().ForAwait();
Assert.Equal(1, Interlocked.CompareExchange(ref gotA, 0, 0));
Assert.Equal(1, Interlocked.CompareExchange(ref gotB, 0, 0));
// and unsubscibe...
tB = listenB.UnsubscribeAsync(prefix + "chann*", null);
listenB.Wait(tB);
Assert.Equal(1, pub.Publish(prefix + "channel", "message"));
await AllowReasonableTimeToPublishAndProcess().ForAwait();
Assert.Equal(2, Interlocked.CompareExchange(ref gotA, 0, 0));
Assert.Equal(1, Interlocked.CompareExchange(ref gotB, 0, 0));
}
}
[Fact]
public async Task TestSubscribeUnsubscribeAndSubscribeAgain()
{
using (var pubMuxer = Create())
using (var subMuxer = Create())
{
var prefix = Me();
var pub = pubMuxer.GetSubscriber();
var sub = subMuxer.GetSubscriber();
int x = 0, y = 0;
var t1 = sub.SubscribeAsync(prefix + "abc", delegate { Interlocked.Increment(ref x); });
var t2 = sub.SubscribeAsync(prefix + "ab*", delegate { Interlocked.Increment(ref y); });
sub.WaitAll(t1, t2);
pub.Publish(prefix + "abc", "");
await AllowReasonableTimeToPublishAndProcess().ForAwait();
Assert.Equal(1, Volatile.Read(ref x));
Assert.Equal(1, Volatile.Read(ref y));
t1 = sub.UnsubscribeAsync(prefix + "abc", null);
t2 = sub.UnsubscribeAsync(prefix + "ab*", null);
sub.WaitAll(t1, t2);
pub.Publish(prefix + "abc", "");
Assert.Equal(1, Volatile.Read(ref x));
Assert.Equal(1, Volatile.Read(ref y));
t1 = sub.SubscribeAsync(prefix + "abc", delegate { Interlocked.Increment(ref x); });
t2 = sub.SubscribeAsync(prefix + "ab*", delegate { Interlocked.Increment(ref y); });
sub.WaitAll(t1, t2);
pub.Publish(prefix + "abc", "");
await AllowReasonableTimeToPublishAndProcess().ForAwait();
Assert.Equal(2, Volatile.Read(ref x));
Assert.Equal(2, Volatile.Read(ref y));
}
}
#if DEBUG #if DEBUG
[Fact] [Fact]
public async Task SubscriptionsSurviveConnectionFailureAsync() public async Task SubscriptionsSurviveConnectionFailureAsync()
......
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Text;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
...@@ -10,6 +11,362 @@ public class Scripting : TestBase ...@@ -10,6 +11,362 @@ public class Scripting : TestBase
{ {
public Scripting(ITestOutputHelper output) : base (output) { } public Scripting(ITestOutputHelper output) : base (output) { }
private ConnectionMultiplexer GetScriptConn(bool allowAdmin = false)
{
int syncTimeout = 5000;
if (Debugger.IsAttached) syncTimeout = 500000;
var muxer = Create(allowAdmin: allowAdmin, syncTimeout: syncTimeout);
Skip.IfMissingFeature(muxer, nameof(RedisFeatures.Scripting), r => r.Scripting);
return muxer;
}
[Fact]
public void ClientScripting()
{
using (var conn = GetScriptConn())
{
var result = conn.GetDatabase().ScriptEvaluate("return redis.call('info','server')", null, null);
}
}
[Fact]
public void BasicScripting()
{
using (var muxer = GetScriptConn())
{
var conn = muxer.GetDatabase();
var noCache = conn.ScriptEvaluateAsync("return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}",
new RedisKey[] { "key1", "key2" }, new RedisValue[] { "first", "second" });
var cache = conn.ScriptEvaluateAsync("return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}",
new RedisKey[] { "key1", "key2" }, new RedisValue[] { "first", "second" });
var results = (string[])conn.Wait(noCache);
Assert.Equal(4, results.Length);
Assert.Equal("key1", results[0]);
Assert.Equal("key2", results[1]);
Assert.Equal("first", results[2]);
Assert.Equal("second", results[3]);
results = (string[])conn.Wait(cache);
Assert.Equal(4, results.Length);
Assert.Equal("key1", results[0]);
Assert.Equal("key2", results[1]);
Assert.Equal("first", results[2]);
Assert.Equal("second", results[3]);
}
}
[Fact]
public void KeysScripting()
{
using (var muxer = GetScriptConn())
{
var conn = muxer.GetDatabase();
var key = Me();
conn.StringSet(key, "bar");
var result = (string)conn.ScriptEvaluate("return redis.call('get', KEYS[1])", new RedisKey[] { key }, null);
Assert.Equal("bar", result);
}
}
[Fact]
public void TestRandomThingFromForum()
{
const string script = @"local currentVal = tonumber(redis.call('GET', KEYS[1]));
if (currentVal <= 0 ) then return 1 elseif (currentVal - (tonumber(ARGV[1])) < 0 ) then return 0 end;
return redis.call('INCRBY', KEYS[1], -tonumber(ARGV[1]));";
using (var muxer = GetScriptConn())
{
var prefix = Me();
var conn = muxer.GetDatabase();
conn.StringSetAsync(prefix + "A", "0");
conn.StringSetAsync(prefix + "B", "5");
conn.StringSetAsync(prefix + "C", "10");
var a = conn.ScriptEvaluateAsync(script, new RedisKey[] { prefix + "A" }, new RedisValue[] { 6 });
var b = conn.ScriptEvaluateAsync(script, new RedisKey[] { prefix + "B" }, new RedisValue[] { 6 });
var c = conn.ScriptEvaluateAsync(script, new RedisKey[] { prefix + "C" }, new RedisValue[] { 6 });
var vals = conn.StringGetAsync(new RedisKey[] { prefix + "A", prefix + "B", prefix + "C" });
Assert.Equal(1, (long)conn.Wait(a)); // exit code when current val is non-positive
Assert.Equal(0, (long)conn.Wait(b)); // exit code when result would be negative
Assert.Equal(4, (long)conn.Wait(c)); // 10 - 6 = 4
Assert.Equal("0", conn.Wait(vals)[0]);
Assert.Equal("5", conn.Wait(vals)[1]);
Assert.Equal("4", conn.Wait(vals)[2]);
}
}
[Fact]
public void HackyGetPerf()
{
using (var muxer = GetScriptConn())
{
var key = Me();
var conn = muxer.GetDatabase();
conn.StringSetAsync(key + "foo", "bar");
var result = (long)conn.ScriptEvaluate(@"
redis.call('psetex', KEYS[1], 60000, 'timing')
for i = 1,100000 do
redis.call('set', 'ignore','abc')
end
local timeTaken = 60000 - redis.call('pttl', KEYS[1])
redis.call('del', KEYS[1])
return timeTaken
", new RedisKey[] { key }, null);
Log(result.ToString());
Assert.True(result > 0);
}
}
[Fact]
public void MultiIncrWithoutReplies()
{
using (var muxer = GetScriptConn())
{
var conn = muxer.GetDatabase();
var prefix = Me();
// prime some initial values
conn.KeyDeleteAsync(new RedisKey[] { prefix + "a", prefix + "b", prefix + "c" });
conn.StringIncrementAsync(prefix + "b");
conn.StringIncrementAsync(prefix + "c");
conn.StringIncrementAsync(prefix + "c");
// run the script, passing "a", "b", "c", "c" to
// increment a & b by 1, c twice
var result = conn.ScriptEvaluateAsync(
"for i,key in ipairs(KEYS) do redis.call('incr', key) end",
new RedisKey[] { prefix + "a", prefix + "b", prefix + "c", prefix + "c" }, // <== aka "KEYS" in the script
null); // <== aka "ARGV" in the script
// check the incremented values
var a = conn.StringGetAsync(prefix + "a");
var b = conn.StringGetAsync(prefix + "b");
var c = conn.StringGetAsync(prefix + "c");
Assert.True(conn.Wait(result).IsNull, "result");
Assert.Equal(1, (long)conn.Wait(a));
Assert.Equal(2, (long)conn.Wait(b));
Assert.Equal(4, (long)conn.Wait(c));
}
}
[Fact]
public void MultiIncrByWithoutReplies()
{
using (var muxer = GetScriptConn())
{
var conn = muxer.GetDatabase();
var prefix = Me();
// prime some initial values
conn.KeyDeleteAsync(new RedisKey[] { prefix + "a", prefix + "b", prefix + "c" });
conn.StringIncrementAsync(prefix + "b");
conn.StringIncrementAsync(prefix + "c");
conn.StringIncrementAsync(prefix + "c");
//run the script, passing "a", "b", "c" and 1,2,3
// increment a &b by 1, c twice
var result = conn.ScriptEvaluateAsync(
"for i,key in ipairs(KEYS) do redis.call('incrby', key, ARGV[i]) end",
new RedisKey[] { prefix + "a", prefix + "b", prefix + "c" }, // <== aka "KEYS" in the script
new RedisValue[] { 1, 1, 2 }); // <== aka "ARGV" in the script
// check the incremented values
var a = conn.StringGetAsync(prefix + "a");
var b = conn.StringGetAsync(prefix + "b");
var c = conn.StringGetAsync(prefix + "c");
Assert.True(conn.Wait(result).IsNull, "result");
Assert.Equal(1, (long)conn.Wait(a));
Assert.Equal(2, (long)conn.Wait(b));
Assert.Equal(4, (long)conn.Wait(c));
}
}
[Fact]
public void DisableStringInference()
{
using (var muxer = GetScriptConn())
{
var conn = muxer.GetDatabase();
var key = Me();
conn.StringSet(key, "bar");
var result = (byte[])conn.ScriptEvaluate("return redis.call('get', KEYS[1])", new RedisKey[] { key });
Assert.Equal("bar", Encoding.UTF8.GetString(result));
}
}
[Fact]
public void FlushDetection()
{ // we don't expect this to handle everything; we just expect it to be predictable
using (var muxer = GetScriptConn(allowAdmin: true))
{
var conn = muxer.GetDatabase();
var key = Me();
conn.StringSet(key, "bar");
var result = (string)conn.ScriptEvaluate("return redis.call('get', KEYS[1])", new RedisKey[] { key }, null);
Assert.Equal("bar", result);
// now cause all kinds of problems
GetServer(muxer).ScriptFlush();
//expect this one to <strike>fail</strike> just work fine (self-fix)
conn.ScriptEvaluate("return redis.call('get', KEYS[1])", new RedisKey[] { key }, null);
result = (string)conn.ScriptEvaluate("return redis.call('get', KEYS[1])", new RedisKey[] { key }, null);
Assert.Equal("bar", result);
}
}
[Fact]
public void PrepareScript()
{
string[] scripts = { "return redis.call('get', KEYS[1])", "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" };
using (var muxer = GetScriptConn(allowAdmin: true))
{
var server = GetServer(muxer);
server.ScriptFlush();
// when vanilla
server.ScriptLoad(scripts[0]);
server.ScriptLoad(scripts[1]);
//when known to exist
server.ScriptLoad(scripts[0]);
server.ScriptLoad(scripts[1]);
}
using (var muxer = GetScriptConn())
{
var server = GetServer(muxer);
//when vanilla
server.ScriptLoad(scripts[0]);
server.ScriptLoad(scripts[1]);
//when known to exist
server.ScriptLoad(scripts[0]);
server.ScriptLoad(scripts[1]);
//when known to exist
server.ScriptLoad(scripts[0]);
server.ScriptLoad(scripts[1]);
}
}
[Fact]
public void NonAsciiScripts()
{
using (var muxer = GetScriptConn())
{
const string evil = "return '僕'";
var conn = muxer.GetDatabase();
GetServer(muxer).ScriptLoad(evil);
var result = (string)conn.ScriptEvaluate(evil, null, null);
Assert.Equal("僕", result);
}
}
[Fact]
public void ScriptThrowsError()
{
Assert.Throws<RedisServerException>(() =>
{
using (var muxer = GetScriptConn())
{
var conn = muxer.GetDatabase();
var result = conn.ScriptEvaluateAsync("return redis.error_reply('oops')", null, null);
try
{
conn.Wait(result);
}
catch (AggregateException ex)
{
throw ex.InnerExceptions[0];
}
}
});
}
[Fact]
public void ScriptThrowsErrorInsideTransaction()
{
using (var muxer = GetScriptConn())
{
var key = Me();
var conn = muxer.GetDatabase();
conn.KeyDeleteAsync(key);
var beforeTran = (string)conn.StringGet(key);
Assert.Null(beforeTran);
var tran = conn.CreateTransaction();
{
var a = tran.StringIncrementAsync(key);
var b = tran.ScriptEvaluateAsync("return redis.error_reply('oops')", null, null);
var c = tran.StringIncrementAsync(key);
var complete = tran.ExecuteAsync();
Assert.True(tran.Wait(complete));
Assert.True(a.IsCompleted);
Assert.True(c.IsCompleted);
Assert.Equal(1L, a.Result);
Assert.Equal(2L, c.Result);
Assert.True(b.IsFaulted);
Assert.Single(b.Exception.InnerExceptions);
var ex = b.Exception.InnerExceptions.Single();
Assert.IsType<RedisServerException>(ex);
Assert.Equal("oops", ex.Message);
}
var afterTran = conn.StringGetAsync(key);
Assert.Equal(2L, (long)conn.Wait(afterTran));
}
}
[Fact]
public void ChangeDbInScript()
{
using (var muxer = GetScriptConn())
{
var key = Me();
muxer.GetDatabase(1).StringSet(key, "db 1");
muxer.GetDatabase(2).StringSet(key, "db 2");
Log("Key: " + key);
var conn = muxer.GetDatabase(2);
var evalResult = conn.ScriptEvaluateAsync(@"redis.call('select', 1)
return redis.call('get','" + key + "')", null, null);
var getResult = conn.StringGetAsync(key);
Assert.Equal("db 1", (string)conn.Wait(evalResult));
// now, our connection thought it was in db 2, but the script changed to db 1
Assert.Equal("db 2", conn.Wait(getResult));
}
}
[Fact]
public void ChangeDbInTranScript()
{
using (var muxer = GetScriptConn())
{
var key = Me();
muxer.GetDatabase(1).StringSet(key, "db 1");
muxer.GetDatabase(2).StringSet(key, "db 2");
var conn = muxer.GetDatabase(2);
var tran = conn.CreateTransaction();
var evalResult = tran.ScriptEvaluateAsync(@"redis.call('select', 1)
return redis.call('get','" + key + "')", null, null);
var getResult = tran.StringGetAsync(key);
Assert.True(tran.Execute());
Assert.Equal("db 1", (string)conn.Wait(evalResult));
// now, our connection thought it was in db 2, but the script changed to db 1
Assert.Equal("db 2", conn.Wait(getResult));
}
}
[Fact] [Fact]
public void TestBasicScripting() public void TestBasicScripting()
{ {
......
...@@ -3,16 +3,16 @@ ...@@ -3,16 +3,16 @@
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
namespace StackExchange.Redis.Tests.Booksleeve namespace StackExchange.Redis.Tests
{ {
public class Strings : BookSleeveTestBase // https://redis.io/commands#string public class Strings : TestBase // https://redis.io/commands#string
{ {
public Strings(ITestOutputHelper output) : base(output) { } public Strings(ITestOutputHelper output) : base(output) { }
[Fact] [Fact]
public void Append() public void Append()
{ {
using (var muxer = GetUnsecuredConnection(waitForOpen: true)) using (var muxer = Create())
{ {
var conn = muxer.GetDatabase(); var conn = muxer.GetDatabase();
var server = GetServer(muxer); var server = GetServer(muxer);
...@@ -47,7 +47,7 @@ public void Append() ...@@ -47,7 +47,7 @@ public void Append()
[Fact] [Fact]
public void Set() public void Set()
{ {
using (var muxer = GetUnsecuredConnection()) using (var muxer = Create())
{ {
var conn = muxer.GetDatabase(); var conn = muxer.GetDatabase();
var key = Me(); var key = Me();
...@@ -67,7 +67,7 @@ public void Set() ...@@ -67,7 +67,7 @@ public void Set()
[Fact] [Fact]
public void SetNotExists() public void SetNotExists()
{ {
using (var muxer = GetUnsecuredConnection()) using (var muxer = Create())
{ {
var conn = muxer.GetDatabase(); var conn = muxer.GetDatabase();
var prefix = Me(); var prefix = Me();
...@@ -98,7 +98,7 @@ public void SetNotExists() ...@@ -98,7 +98,7 @@ public void SetNotExists()
[Fact] [Fact]
public void Ranges() public void Ranges()
{ {
using (var muxer = GetUnsecuredConnection(waitForOpen: true)) using (var muxer = Create())
{ {
Skip.IfMissingFeature(muxer, nameof(RedisFeatures.StringSetRange), r => r.StringSetRange); Skip.IfMissingFeature(muxer, nameof(RedisFeatures.StringSetRange), r => r.StringSetRange);
var conn = muxer.GetDatabase(); var conn = muxer.GetDatabase();
...@@ -119,7 +119,7 @@ public void Ranges() ...@@ -119,7 +119,7 @@ public void Ranges()
[Fact] [Fact]
public void IncrDecr() public void IncrDecr()
{ {
using (var muxer = GetUnsecuredConnection()) using (var muxer = Create())
{ {
var conn = muxer.GetDatabase(); var conn = muxer.GetDatabase();
var key = Me(); var key = Me();
...@@ -147,7 +147,7 @@ public void IncrDecr() ...@@ -147,7 +147,7 @@ public void IncrDecr()
[Fact] [Fact]
public void IncrDecrFloat() public void IncrDecrFloat()
{ {
using (var muxer = GetUnsecuredConnection(waitForOpen: true)) using (var muxer = Create())
{ {
Skip.IfMissingFeature(muxer, nameof(RedisFeatures.IncrementFloat), r => r.IncrementFloat); Skip.IfMissingFeature(muxer, nameof(RedisFeatures.IncrementFloat), r => r.IncrementFloat);
var conn = muxer.GetDatabase(); var conn = muxer.GetDatabase();
...@@ -177,7 +177,7 @@ public void IncrDecrFloat() ...@@ -177,7 +177,7 @@ public void IncrDecrFloat()
[Fact] [Fact]
public void GetRange() public void GetRange()
{ {
using (var muxer = GetUnsecuredConnection(waitForOpen: true)) using (var muxer = Create())
{ {
var conn = muxer.GetDatabase(); var conn = muxer.GetDatabase();
var key = Me(); var key = Me();
...@@ -195,7 +195,7 @@ public void GetRange() ...@@ -195,7 +195,7 @@ public void GetRange()
[Fact] [Fact]
public void BitCount() public void BitCount()
{ {
using (var muxer = GetUnsecuredConnection(waitForOpen: true)) using (var muxer = Create())
{ {
Skip.IfMissingFeature(muxer, nameof(RedisFeatures.BitwiseOperations), r => r.BitwiseOperations); Skip.IfMissingFeature(muxer, nameof(RedisFeatures.BitwiseOperations), r => r.BitwiseOperations);
...@@ -215,7 +215,7 @@ public void BitCount() ...@@ -215,7 +215,7 @@ public void BitCount()
[Fact] [Fact]
public void BitOp() public void BitOp()
{ {
using (var muxer = GetUnsecuredConnection(waitForOpen: true)) using (var muxer = Create())
{ {
Skip.IfMissingFeature(muxer, nameof(RedisFeatures.BitwiseOperations), r => r.BitwiseOperations); Skip.IfMissingFeature(muxer, nameof(RedisFeatures.BitwiseOperations), r => r.BitwiseOperations);
var conn = muxer.GetDatabase(); var conn = muxer.GetDatabase();
...@@ -252,7 +252,7 @@ public void BitOp() ...@@ -252,7 +252,7 @@ public void BitOp()
[Fact] [Fact]
public void RangeString() public void RangeString()
{ {
using (var muxer = GetUnsecuredConnection()) using (var muxer = Create())
{ {
var conn = muxer.GetDatabase(); var conn = muxer.GetDatabase();
var key = Me(); var key = Me();
......
...@@ -213,9 +213,10 @@ protected IServer GetAnyMaster(ConnectionMultiplexer muxer) ...@@ -213,9 +213,10 @@ protected IServer GetAnyMaster(ConnectionMultiplexer muxer)
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,
[CallerMemberName] string caller = null) [CallerMemberName] string caller = null)
{ {
string configuration = GetConfiguration(); 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)
{ {
......
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