Skip to content

Commit 7d69d69

Browse files
committed
Cleanup RedisDataDecoder and fully implement ByteToMessageDecoder conformance
1 parent c928204 commit 7d69d69

File tree

3 files changed

+85
-7
lines changed

3 files changed

+85
-7
lines changed

Sources/NIORedis/Coders/RedisDataDecoder.swift

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import NIO
55
///
66
/// See: https://redis.io/topics/protocol
77
final class RedisDataDecoder: ByteToMessageDecoder {
8+
/// `ByteToMessageDecoder`
9+
public typealias InboundOut = RedisData
10+
811
/// See `ByteToMessageDecoder.cumulationBuffer`
912
var cumulationBuffer: ByteBuffer?
1013

@@ -16,14 +19,12 @@ final class RedisDataDecoder: ByteToMessageDecoder {
1619
case .notYetParsed:
1720
return .needMoreData
1821

19-
case .parsed:
22+
case .parsed(let redisData):
23+
ctx.fireChannelRead(wrapInboundOut(redisData))
24+
buffer.moveReaderIndex(forwardBy: position)
2025
return .continue
2126
}
2227
}
23-
24-
private let encoding = String.Encoding.utf8
25-
26-
public typealias InboundOut = Int
2728
}
2829

2930
// MARK: RESP Parsing
@@ -104,7 +105,7 @@ extension RedisDataDecoder {
104105
// Move the tip of the message position for recursive parsing to just after the newline
105106
position += expectedNewlinePosition + 1
106107

107-
return String(bytes: bytes[ ..<(expectedNewlinePosition - 1) ], encoding: encoding)
108+
return String(bytes: bytes[ ..<(expectedNewlinePosition - 1) ], encoding: .utf8)
108109
}
109110

110111
/// See https://redis.io/topics/protocol#resp-integers
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import NIO
2+
@testable import NIORedis
3+
import XCTest
4+
5+
final class RedisDataDecoderByteToMessageDecoderTests: XCTestCase {
6+
private let decoder = RedisDataDecoder()
7+
private let allocator = ByteBufferAllocator()
8+
9+
func testDecoding_partial_needsMoreData() throws {
10+
XCTAssertEqual(try decodeTest("+OK\r"), .needMoreData)
11+
XCTAssertEqual(try decodeTest("$2\r\n"), .needMoreData)
12+
XCTAssertEqual(try decodeTest("*2\r\n:1\r\n"), .needMoreData)
13+
XCTAssertEqual(try decodeTest("*2\r\n*1\r\n"), .needMoreData)
14+
XCTAssertEqual(try decodeTest("-ERR test\r"), .needMoreData)
15+
XCTAssertEqual(try decodeTest(":2"), .needMoreData)
16+
}
17+
18+
func testDecoding_badMessage_throws() {
19+
do {
20+
_ = try decodeTest("&3\r\n").0
21+
XCTFail("Failed to properly throw error")
22+
} catch { XCTAssertTrue(error is RedisError) }
23+
}
24+
25+
private static let completeMessages = [
26+
"+OK\r\n",
27+
"$2\r\naa\r\n",
28+
"*2\r\n:1\r\n:2\r\n",
29+
"*2\r\n*1\r\n:1\r\n:2\r\n",
30+
"-ERR test\r\n",
31+
":2\r\n"
32+
]
33+
34+
func testDecoding_complete_continues() throws {
35+
for message in RedisDataDecoderByteToMessageDecoderTests.completeMessages {
36+
XCTAssertEqual(try decodeTest(message), .continue)
37+
}
38+
}
39+
40+
func testDecoding_complete_movesReaderIndex() throws {
41+
for message in RedisDataDecoderByteToMessageDecoderTests.completeMessages {
42+
let messageByteSize = message.convertedToData()
43+
XCTAssertEqual(try decodeTest(message).1, messageByteSize.count)
44+
}
45+
}
46+
47+
private func decodeTest(_ input: String) throws -> DecodingState {
48+
var buffer = allocator.buffer(capacity: 256)
49+
return try decodeTest(input, buffer: &buffer)
50+
}
51+
52+
private func decodeTest(_ input: String) throws -> (DecodingState, Int) {
53+
var buffer = allocator.buffer(capacity: 256)
54+
return (try decodeTest(input, buffer: &buffer), buffer.readerIndex)
55+
}
56+
57+
private func decodeTest(_ input: String, buffer: inout ByteBuffer) throws -> DecodingState {
58+
let embeddedChannel = EmbeddedChannel()
59+
defer { _ = try? embeddedChannel.finish() }
60+
try embeddedChannel.pipeline.add(handler: decoder).wait()
61+
let context = try embeddedChannel.pipeline.context(handler: decoder).wait()
62+
63+
buffer.write(string: input)
64+
65+
return try decoder.decode(ctx: context, buffer: &buffer)
66+
}
67+
}
68+
69+
extension RedisDataDecoderByteToMessageDecoderTests {
70+
static var allTests = [
71+
("testDecoding_partial_needsMoreData", testDecoding_partial_needsMoreData),
72+
("testDecoding_badMessage_throws", testDecoding_badMessage_throws),
73+
("testDecoding_complete_continues", testDecoding_complete_continues),
74+
("testDecoding_complete_movesReaderIndex", testDecoding_complete_movesReaderIndex),
75+
]
76+
}

Tests/NIORedisTests/XCTestManifests.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import XCTest
44
public func allTests() -> [XCTestCaseEntry] {
55
return [
66
testCase(NIORedisTests.allTests),
7-
testCase(RedisDataDecoderParsingTests.allTests)
7+
testCase(RedisDataDecoderParsingTests.allTests),
8+
testCase(RedisDataDecoderByteToMessageDecoderTests.allTests),
89
]
910
}
1011
#endif

0 commit comments

Comments
 (0)