Skip to content

Commit 7012510

Browse files
committed
working unit tests and API shim
1 parent 757ec4a commit 7012510

5 files changed

Lines changed: 148 additions & 46 deletions

File tree

src/StackExchange.Redis/PhysicalConnection.Read.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
using System.Diagnostics;
55
using System.Diagnostics.CodeAnalysis;
66
using System.IO;
7+
using System.IO.Pipelines;
78
using System.Net;
8-
using System.Net.Sockets;
9+
using System.Runtime.CompilerServices;
910
using System.Threading;
1011
using System.Threading.Tasks;
11-
using Pipelines.Sockets.Unofficial;
1212
using RESPite.Buffers;
1313
using RESPite.Internal;
1414
using RESPite.Messages;
@@ -17,6 +17,8 @@ namespace StackExchange.Redis;
1717

1818
internal sealed partial class PhysicalConnection
1919
{
20+
internal static PhysicalConnection Dummy() => new(null!);
21+
2022
private volatile ReadStatus _readStatus = ReadStatus.NotStarted;
2123
internal ReadStatus GetReadStatus() => _readStatus;
2224

src/StackExchange.Redis/PhysicalConnection.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,21 @@ internal void GetBytes(out long sent, out long received)
8585
private Socket? _socket;
8686
internal Socket? VolatileSocket => Volatile.Read(ref _socket);
8787

88+
// used for dummy test connections
89+
public PhysicalConnection(
90+
ConnectionType connectionType = ConnectionType.Interactive,
91+
RedisProtocol protocol = RedisProtocol.Resp2,
92+
[CallerMemberName] string name = "")
93+
{
94+
lastWriteTickCount = lastReadTickCount = Environment.TickCount;
95+
lastBeatTickCount = 0;
96+
this.connectionType = connectionType;
97+
_protocol = protocol;
98+
_bridge = new WeakReference(null);
99+
_physicalName = name;
100+
101+
OnCreateEcho();
102+
}
88103
public PhysicalConnection(PhysicalBridge bridge)
89104
{
90105
lastWriteTickCount = lastReadTickCount = Environment.TickCount;
@@ -275,7 +290,7 @@ private enum ReadMode : byte
275290
private RedisProtocol _protocol; // note starts at **zero**, not RESP2
276291
public RedisProtocol? Protocol => _protocol == 0 ? null : _protocol;
277292

278-
internal void SetProtocol(RedisProtocol value)
293+
public void SetProtocol(RedisProtocol value)
279294
{
280295
_protocol = value;
281296
BridgeCouldBeNull?.SetProtocol(value);

src/StackExchange.Redis/ResultProcessor.cs

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -348,8 +348,50 @@ private bool HandleCommonError(Message message, RespReader reader, PhysicalBridg
348348

349349
protected virtual bool SetResultCore(PhysicalConnection connection, Message message, ref RespReader reader)
350350
{
351-
// temp hack so we can compile; this should be abstract
352-
return false;
351+
// spoof the old API from the new API; this is a transitional step only, and is inefficient
352+
var rawResult = AsRaw(ref reader, connection.Protocol is RedisProtocol.Resp3);
353+
return SetResultCore(connection, message, rawResult);
354+
}
355+
356+
private static RawResult AsRaw(ref RespReader reader, bool resp3)
357+
{
358+
var flags = RawResult.ResultFlags.HasValue;
359+
if (!reader.IsNull) flags |= RawResult.ResultFlags.NonNull;
360+
if (resp3) flags |= RawResult.ResultFlags.Resp3;
361+
var type = Type(reader.Prefix);
362+
if (reader.IsAggregate)
363+
{
364+
var inner = reader.ReadPastArray((ref value) => AsRaw(ref value, resp3), false) ?? [];
365+
return new RawResult(type, new Sequence<RawResult>(inner), flags);
366+
}
367+
368+
if (reader.IsScalar)
369+
{
370+
ReadOnlySequence<byte> blob = new(reader.ReadByteArray() ?? []);
371+
return new RawResult(type, blob, flags);
372+
}
373+
374+
return default;
375+
376+
static ResultType Type(RespPrefix prefix) => prefix switch
377+
{
378+
RespPrefix.Array => ResultType.Array,
379+
RespPrefix.Attribute => ResultType.Attribute,
380+
RespPrefix.BigInteger => ResultType.BigInteger,
381+
RespPrefix.Boolean => ResultType.Boolean,
382+
RespPrefix.BulkError => ResultType.BlobError,
383+
RespPrefix.BulkString => ResultType.BulkString,
384+
RespPrefix.SimpleString => ResultType.SimpleString,
385+
RespPrefix.Map => ResultType.Map,
386+
RespPrefix.Set => ResultType.Set,
387+
RespPrefix.Double => ResultType.Double,
388+
RespPrefix.Integer => ResultType.Integer,
389+
RespPrefix.SimpleError => ResultType.Error,
390+
RespPrefix.Null => ResultType.Null,
391+
RespPrefix.VerbatimString => ResultType.VerbatimString,
392+
RespPrefix.Push=> ResultType.Push,
393+
_ => throw new ArgumentOutOfRangeException(nameof(prefix), prefix, null),
394+
};
353395
}
354396

355397
// temp hack so we can compile; this should be removed
@@ -1733,7 +1775,13 @@ private sealed class Int64ArrayProcessor : ResultProcessor<long[]>
17331775
{
17341776
protected override bool SetResultCore(PhysicalConnection connection, Message message, RawResult result)
17351777
{
1736-
if (result.Resp2TypeArray == ResultType.Array && !result.IsNull)
1778+
if (result.IsNull)
1779+
{
1780+
SetResult(message, null!);
1781+
return true;
1782+
}
1783+
1784+
if (result.Resp2TypeArray == ResultType.Array)
17371785
{
17381786
var arr = result.ToArray((in RawResult x) => (long)x.AsRedisValue())!;
17391787
SetResult(message, arr);

tests/StackExchange.Redis.Tests/Helpers/SharedConnectionFixture.cs

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,8 @@ namespace StackExchange.Redis.Tests;
1717

1818
public class SharedConnectionFixture : IDisposable
1919
{
20-
public bool IsEnabled { get; }
21-
22-
private readonly ConnectionMultiplexer _actualConnection;
23-
public string Configuration { get; }
24-
25-
public SharedConnectionFixture()
26-
{
27-
IsEnabled = TestConfig.Current.UseSharedConnection;
28-
Configuration = TestBase.GetDefaultConfiguration();
29-
_actualConnection = TestBase.CreateDefault(
30-
output: null,
31-
clientName: nameof(SharedConnectionFixture),
32-
configuration: Configuration,
33-
allowAdmin: true);
34-
_actualConnection.InternalError += OnInternalError;
35-
_actualConnection.ConnectionFailed += OnConnectionFailed;
36-
}
20+
public bool IsEnabled { get; } = TestConfig.Current.UseSharedConnection;
21+
public string Configuration { get; } = TestBase.GetDefaultConfiguration();
3722

3823
private NonDisposingConnection? resp2, resp3;
3924
internal IInternalConnectionMultiplexer GetConnection(TestBase obj, RedisProtocol protocol, [CallerMemberName] string caller = "")
@@ -273,11 +258,16 @@ public void Teardown(TextWriter output)
273258
}
274259
// Assert.True(false, $"There were {privateFailCount} private ambient exceptions.");
275260
}
261+
TearDown(resp2, output);
262+
TearDown(resp3, output);
263+
}
276264

277-
if (_actualConnection != null)
265+
private void TearDown(IInternalConnectionMultiplexer? connection, TextWriter output)
266+
{
267+
if (connection is { } conn)
278268
{
279-
TestBase.Log(output, "Connection Counts: " + _actualConnection.GetCounters().ToString());
280-
foreach (var ep in _actualConnection.GetServerSnapshot())
269+
TestBase.Log(output, "Connection Counts: " + conn.GetCounters().ToString());
270+
foreach (var ep in conn.GetServerSnapshot())
281271
{
282272
var interactive = ep.GetBridge(ConnectionType.Interactive);
283273
TestBase.Log(output, $" {Format.ToString(interactive)}: {interactive?.GetStatus()}");

tests/StackExchange.Redis.Tests/ResultProcessorUnitTests.cs

Lines changed: 67 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using System;
22
using System.Buffers;
3+
using System.Diagnostics.CodeAnalysis;
4+
using System.Runtime.CompilerServices;
35
using System.Text;
46
using RESPite.Messages;
57
using Xunit;
@@ -8,39 +10,84 @@ namespace StackExchange.Redis.Tests;
810

911
public class ResultProcessorUnitTests(ITestOutputHelper log)
1012
{
11-
public void Log(string message) => log?.WriteLine(message);
12-
13-
private protected static Message DummyMessage<T>()
14-
=> Message.Create(0, default, RedisCommand.UNKNOWN);
15-
1613
[Theory]
1714
[InlineData(":1\r\n", 1)]
1815
[InlineData("+1\r\n", 1)]
1916
[InlineData("$1\r\n1\r\n", 1)]
20-
public void Int32(string resp, int value)
21-
{
22-
var result = Execute(resp, ResultProcessor.Int32);
23-
Assert.Equal(value, result);
24-
}
17+
[InlineData(":-42\r\n", -42)]
18+
[InlineData("+-42\r\n", -42)]
19+
[InlineData("$3\r\n-42\r\n", -42)]
20+
public void Int32(string resp, int value) => Assert.Equal(value, Execute(resp, ResultProcessor.Int32));
21+
22+
[Theory]
23+
[InlineData("+OK\r\n")]
24+
[InlineData("$4\r\nPONG\r\n")]
25+
public void FailingInt32(string resp) => ExecuteUnexpected(resp, ResultProcessor.Int32);
2526

2627
[Theory]
2728
[InlineData(":1\r\n", 1)]
2829
[InlineData("+1\r\n", 1)]
2930
[InlineData("$1\r\n1\r\n", 1)]
30-
public void Int64(string resp, int value)
31+
[InlineData(":-42\r\n", -42)]
32+
[InlineData("+-42\r\n", -42)]
33+
[InlineData("$3\r\n-42\r\n", -42)]
34+
public void Int64(string resp, long value) => Assert.Equal(value, Execute(resp, ResultProcessor.Int64));
35+
36+
[Theory]
37+
[InlineData("+OK\r\n")]
38+
[InlineData("$4\r\nPONG\r\n")]
39+
public void FailingInt64(string resp) => ExecuteUnexpected(resp, ResultProcessor.Int64);
40+
41+
[Theory]
42+
[InlineData("*-1\r\n", null)]
43+
[InlineData("*0\r\n", "")]
44+
[InlineData("*1\r\n+42\r\n", "42")]
45+
[InlineData("*2\r\n+42\r\n:78\r\n", "42,78")]
46+
public void Int64Array(string resp, string? value) => Assert.Equal(value, Join(Execute(resp, ResultProcessor.Int64Array)));
47+
48+
[return: NotNullIfNotNull(nameof(array))]
49+
protected static string? Join<T>(T[]? array, string separator = ",")
3150
{
32-
var result = Execute(resp, ResultProcessor.Int32);
33-
Assert.Equal(value, result);
51+
if (array is null) return null;
52+
return string.Join(separator, array);
3453
}
3554

36-
private protected static T? Execute<T>(string resp, ResultProcessor<T> processor)
55+
public void Log(string message) => log?.WriteLine(message);
56+
57+
private protected static Message DummyMessage<T>()
58+
=> Message.Create(0, default, RedisCommand.UNKNOWN);
59+
60+
private protected void ExecuteUnexpected<T>(
61+
string resp,
62+
ResultProcessor<T> processor,
63+
ConnectionType connectionType = ConnectionType.Interactive,
64+
RedisProtocol protocol = RedisProtocol.Resp2,
65+
[CallerMemberName] string caller = "")
66+
{
67+
Assert.False(TryExecute(resp, processor, out _, out var ex));
68+
if (ex is not null) Log(ex.Message);
69+
Assert.StartsWith("Unexpected response to UNKNOWN:", Assert.IsType<RedisConnectionException>(ex).Message);
70+
}
71+
private protected static T? Execute<T>(
72+
string resp,
73+
ResultProcessor<T> processor,
74+
ConnectionType connectionType = ConnectionType.Interactive,
75+
RedisProtocol protocol = RedisProtocol.Resp2,
76+
[CallerMemberName] string caller = "")
3777
{
3878
Assert.True(TryExecute<T>(resp, processor, out var value, out var ex));
3979
Assert.Null(ex);
4080
return value;
4181
}
4282

43-
private protected static bool TryExecute<T>(string resp, ResultProcessor<T> processor, out T? value, out Exception? exception)
83+
private protected static bool TryExecute<T>(
84+
string resp,
85+
ResultProcessor<T> processor,
86+
out T? value,
87+
out Exception? exception,
88+
ConnectionType connectionType = ConnectionType.Interactive,
89+
RedisProtocol protocol = RedisProtocol.Resp2,
90+
[CallerMemberName] string caller = "")
4491
{
4592
byte[]? lease = null;
4693
try
@@ -51,15 +98,15 @@ private protected static bool TryExecute<T>(string resp, ResultProcessor<T> proc
5198
? stackalloc byte[MAX_STACK]
5299
: (lease = ArrayPool<byte>.Shared.Rent(maxLen));
53100

54-
var msg = DummyMessage<int>();
101+
var msg = DummyMessage<T>();
55102
var box = SimpleResultBox<T>.Get();
56103
msg.SetSource(processor, box);
57104

58105
var reader = new RespReader(oversized.Slice(0, Encoding.UTF8.GetBytes(resp, oversized)));
59-
bool success = processor.SetResult(null!, msg, ref reader);
60-
exception = null;
61-
value = success ? box.GetResult(out exception, canRecycle: true) : default;
62-
return success;
106+
PhysicalConnection connection = new(connectionType, protocol, caller);
107+
Assert.True(processor.SetResult(connection, msg, ref reader));
108+
value = box.GetResult(out exception, canRecycle: true);
109+
return exception is null;
63110
}
64111
finally
65112
{

0 commit comments

Comments
 (0)