From 66f9a509ed3cc56b6eb367515e421beca4a0af53 Mon Sep 17 00:00:00 2001 From: Johannes Weiss Date: Tue, 28 May 2019 11:28:59 +0100 Subject: [PATCH] use B2MD verifier (#52) Motivation: Use B2MDVerifier for the B2MDs in NIOExtras. Already found one bug, separetely fixed in #51. Modifications: Write a basic validation test for all B2MDs. Result: Better test coverage. --- Package.swift | 4 +- .../LengthFieldBasedFrameDecoder.swift | 3 +- .../FixedLengthFrameDecoderTest+XCTest.swift | 2 +- .../FixedLengthFrameDecoderTest.swift | 39 ++++----- ...gthFieldBasedFrameDecoderTest+XCTest.swift | 2 + .../LengthFieldBasedFrameDecoderTest.swift | 82 ++++++++++++++++++- .../LineBasedFrameDecoderTest+XCTest.swift | 1 + .../LineBasedFrameDecoderTest.swift | 27 ++++++ 8 files changed, 131 insertions(+), 29 deletions(-) diff --git a/Package.swift b/Package.swift index f1b8b39..8324770 100644 --- a/Package.swift +++ b/Package.swift @@ -25,7 +25,7 @@ var targets: [PackageDescription.Target] = [ linkerSettings: [ .linkedLibrary("z") ]), - .testTarget(name: "NIOExtrasTests", dependencies: ["NIOExtras"]), + .testTarget(name: "NIOExtrasTests", dependencies: ["NIOExtras", "NIOTestUtils"]), .testTarget(name: "NIOHTTPCompressionTests", dependencies: ["NIOHTTPCompression"]), ] @@ -38,7 +38,7 @@ let package = Package( .library(name: "NIOHTTPCompression", targets: ["NIOHTTPCompression"]), ], dependencies: [ - .package(url: "https://github.com/apple/swift-nio.git", from: "2.0.0"), + .package(url: "https://github.com/apple/swift-nio.git", from: "2.2.0"), ], targets: targets ) diff --git a/Sources/NIOExtras/LengthFieldBasedFrameDecoder.swift b/Sources/NIOExtras/LengthFieldBasedFrameDecoder.swift index b69ba48..feb5b03 100644 --- a/Sources/NIOExtras/LengthFieldBasedFrameDecoder.swift +++ b/Sources/NIOExtras/LengthFieldBasedFrameDecoder.swift @@ -43,8 +43,7 @@ public final class LengthFieldBasedFrameDecoder: ByteToMessageDecoder { case four case eight - fileprivate var length: Int { - + var length: Int { switch self { case .one: return 1 diff --git a/Tests/NIOExtrasTests/FixedLengthFrameDecoderTest+XCTest.swift b/Tests/NIOExtrasTests/FixedLengthFrameDecoderTest+XCTest.swift index 71bf53d..5e6b5d7 100644 --- a/Tests/NIOExtrasTests/FixedLengthFrameDecoderTest+XCTest.swift +++ b/Tests/NIOExtrasTests/FixedLengthFrameDecoderTest+XCTest.swift @@ -30,7 +30,7 @@ extension FixedLengthFrameDecoderTest { ("testDecodeIfMoreBytesAreSent", testDecodeIfMoreBytesAreSent), ("testRemoveHandlerWhenBufferIsNotEmpty", testRemoveHandlerWhenBufferIsNotEmpty), ("testRemoveHandlerWhenBufferIsEmpty", testRemoveHandlerWhenBufferIsEmpty), - ("testCloseInChannelRead", testCloseInChannelRead), + ("testBasicValidation", testBasicValidation), ] } } diff --git a/Tests/NIOExtrasTests/FixedLengthFrameDecoderTest.swift b/Tests/NIOExtrasTests/FixedLengthFrameDecoderTest.swift index 3c5bcd1..d72620f 100644 --- a/Tests/NIOExtrasTests/FixedLengthFrameDecoderTest.swift +++ b/Tests/NIOExtrasTests/FixedLengthFrameDecoderTest.swift @@ -15,6 +15,7 @@ import XCTest import NIO import NIOExtras +import NIOTestUtils class FixedLengthFrameDecoderTest: XCTestCase { public func testDecodeIfFewerBytesAreSent() throws { @@ -115,30 +116,22 @@ class FixedLengthFrameDecoderTest: XCTestCase { XCTAssertTrue(try channel.finish().isClean) } - func testCloseInChannelRead() { - let channel = EmbeddedChannel(handler: ByteToMessageHandler(LengthFieldBasedFrameDecoder(lengthFieldLength: .four))) - class CloseInReadHandler: ChannelInboundHandler { - typealias InboundIn = ByteBuffer - - private var numberOfReads = 0 - - func channelRead(context: ChannelHandlerContext, data: NIOAny) { - self.numberOfReads += 1 - XCTAssertEqual(1, self.numberOfReads) - XCTAssertEqual([UInt8(100)], Array(self.unwrapInboundIn(data).readableBytesView)) - context.close().whenFailure { error in - XCTFail("unexpected error: \(error)") - } - context.fireChannelRead(data) + func testBasicValidation() { + for length in 1 ... 20 { + let inputs = [ + String(decoding: Array(repeating: UInt8(ascii: "a"), count: length), as: Unicode.UTF8.self), + String(decoding: Array(repeating: UInt8(ascii: "b"), count: length), as: Unicode.UTF8.self), + String(decoding: Array(repeating: UInt8(ascii: "c"), count: length), as: Unicode.UTF8.self), + ] + func byteBuffer(_ string: String) -> ByteBuffer { + var buffer = ByteBufferAllocator().buffer(capacity: string.utf8.count) + buffer.writeString(string) + return buffer } + let inputOutputPairs: [(String, [ByteBuffer])] = inputs.map { ($0, [byteBuffer($0)]) } + XCTAssertNoThrow(try ByteToMessageDecoderVerifier.verifyDecoder(stringInputOutputPairs: inputOutputPairs) { + FixedLengthFrameDecoder(frameLength: length) + }) } - XCTAssertNoThrow(try channel.pipeline.addHandler(CloseInReadHandler()).wait()) - - var buf = channel.allocator.buffer(capacity: 1024) - buf.writeBytes([UInt8(0), 0, 0, 1, 100]) - XCTAssertNoThrow(try channel.writeInbound(buf)) - XCTAssertNoThrow(XCTAssertEqual([100], Array((try channel.readInbound() as ByteBuffer?)!.readableBytesView))) - XCTAssertNoThrow(XCTAssertNil(try channel.readInbound())) - } } diff --git a/Tests/NIOExtrasTests/LengthFieldBasedFrameDecoderTest+XCTest.swift b/Tests/NIOExtrasTests/LengthFieldBasedFrameDecoderTest+XCTest.swift index e657782..35d1c34 100644 --- a/Tests/NIOExtrasTests/LengthFieldBasedFrameDecoderTest+XCTest.swift +++ b/Tests/NIOExtrasTests/LengthFieldBasedFrameDecoderTest+XCTest.swift @@ -40,6 +40,8 @@ extension LengthFieldBasedFrameDecoderTest { ("testDecodeWithUInt16HeaderWithPartialBody", testDecodeWithUInt16HeaderWithPartialBody), ("testRemoveHandlerWhenBufferIsEmpty", testRemoveHandlerWhenBufferIsEmpty), ("testRemoveHandlerWhenBufferIsNotEmpty", testRemoveHandlerWhenBufferIsNotEmpty), + ("testCloseInChannelRead", testCloseInChannelRead), + ("testBasicVerification", testBasicVerification), ] } } diff --git a/Tests/NIOExtrasTests/LengthFieldBasedFrameDecoderTest.swift b/Tests/NIOExtrasTests/LengthFieldBasedFrameDecoderTest.swift index eac01b5..a2f2093 100644 --- a/Tests/NIOExtrasTests/LengthFieldBasedFrameDecoderTest.swift +++ b/Tests/NIOExtrasTests/LengthFieldBasedFrameDecoderTest.swift @@ -14,7 +14,8 @@ import XCTest import NIO -import NIOExtras +@testable import NIOExtras +import NIOTestUtils private let standardDataString = "abcde" @@ -375,4 +376,83 @@ class LengthFieldBasedFrameDecoderTest: XCTestCase { })) XCTAssertTrue(try self.channel.finish().isClean) } + + func testCloseInChannelRead() { + let channel = EmbeddedChannel(handler: ByteToMessageHandler(LengthFieldBasedFrameDecoder(lengthFieldLength: .four))) + class CloseInReadHandler: ChannelInboundHandler { + typealias InboundIn = ByteBuffer + + private var numberOfReads = 0 + + func channelRead(context: ChannelHandlerContext, data: NIOAny) { + self.numberOfReads += 1 + XCTAssertEqual(1, self.numberOfReads) + XCTAssertEqual([UInt8(100)], Array(self.unwrapInboundIn(data).readableBytesView)) + context.close().whenFailure { error in + XCTFail("unexpected error: \(error)") + } + context.fireChannelRead(data) + } + } + XCTAssertNoThrow(try channel.pipeline.addHandler(CloseInReadHandler()).wait()) + + var buf = channel.allocator.buffer(capacity: 1024) + buf.writeBytes([UInt8(0), 0, 0, 1, 100]) + XCTAssertNoThrow(try channel.writeInbound(buf)) + XCTAssertNoThrow(XCTAssertEqual([100], Array((try channel.readInbound() as ByteBuffer?)!.readableBytesView))) + XCTAssertNoThrow(XCTAssertNil(try channel.readInbound())) + } + + func testBasicVerification() { + let inputs: [(LengthFieldBasedFrameDecoder.ByteLength, [(Int, String)])] = [ + (.one, [ + (6, "abcdef"), + (0, ""), + (9, "123456789"), + (Int(UInt8.max), + String(decoding: Array(repeating: UInt8(ascii: "X"), count: Int(UInt8.max)), as: Unicode.UTF8.self)), + ]), + (.two, [ + (1, "a"), + (0, ""), + (9, "123456789"), + (307, + String(decoding: Array(repeating: UInt8(ascii: "X"), count: 307), as: Unicode.UTF8.self)), + ]), + (.four, [ + (1, "a"), + (0, ""), + (3, "333"), + (307, + String(decoding: Array(repeating: UInt8(ascii: "X"), count: 307), as: Unicode.UTF8.self)), + ]), + (.eight, [ + (1, "a"), + (0, ""), + (4, "aaaa"), + (307, + String(decoding: Array(repeating: UInt8(ascii: "X"), count: 307), as: Unicode.UTF8.self)), + ]), + ] + + for input in inputs { + let (lenBytes, inputData) = input + + func byteBuffer(length: Int, string: String) -> ByteBuffer { + var buf = self.channel.allocator.buffer(capacity: string.utf8.count + 8) + buf.writeInteger(length) + buf.moveReaderIndex(forwardBy: 8 - lenBytes.length) + buf.writeString(string) + return buf + } + + let inputOutputPairs = inputData.map { (input: (Int, String)) -> (ByteBuffer, [ByteBuffer]) in + let bytes = byteBuffer(length: input.0, string: input.1) + return (bytes, [bytes.getSlice(at: bytes.readerIndex + lenBytes.length, length: input.0)!]) + } + XCTAssertNoThrow(try ByteToMessageDecoderVerifier.verifyDecoder(inputOutputPairs: inputOutputPairs) { + LengthFieldBasedFrameDecoder(lengthFieldLength: lenBytes) + }) + } + } } diff --git a/Tests/NIOExtrasTests/LineBasedFrameDecoderTest+XCTest.swift b/Tests/NIOExtrasTests/LineBasedFrameDecoderTest+XCTest.swift index 74d76e8..565c4cd 100644 --- a/Tests/NIOExtrasTests/LineBasedFrameDecoderTest+XCTest.swift +++ b/Tests/NIOExtrasTests/LineBasedFrameDecoderTest+XCTest.swift @@ -34,6 +34,7 @@ extension LineBasedFrameDecoderTest { ("testChannelInactiveWithLeftOverBytes", testChannelInactiveWithLeftOverBytes), ("testMoreDataAvailableWhenChannelBecomesInactive", testMoreDataAvailableWhenChannelBecomesInactive), ("testDripFedCRLN", testDripFedCRLN), + ("testBasicValidation", testBasicValidation), ] } } diff --git a/Tests/NIOExtrasTests/LineBasedFrameDecoderTest.swift b/Tests/NIOExtrasTests/LineBasedFrameDecoderTest.swift index e865520..c680444 100644 --- a/Tests/NIOExtrasTests/LineBasedFrameDecoderTest.swift +++ b/Tests/NIOExtrasTests/LineBasedFrameDecoderTest.swift @@ -15,6 +15,7 @@ import XCTest @testable import NIO // to inspect the cumulationBuffer import NIOExtras +import NIOTestUtils class LineBasedFrameDecoderTest: XCTestCase { private var channel: EmbeddedChannel! @@ -186,4 +187,30 @@ class LineBasedFrameDecoderTest: XCTestCase { buffer.writeString("a") XCTAssertNoThrow(XCTAssertEqual(buffer, try self.channel.readInbound())) } + + func testBasicValidation() { + func byteBuffer(_ string: String) -> ByteBuffer { + var buffer = self.channel.allocator.buffer(capacity: string.utf8.count) + buffer.writeString(string) + return buffer + } + + do { + try ByteToMessageDecoderVerifier.verifyDecoder(stringInputOutputPairs: [ + ("\n", [byteBuffer("")]), + ("\r\n", [byteBuffer("")]), + ("a\r\n", [byteBuffer("a")]), + ("a\n", [byteBuffer("a")]), + ("a\rb\n", [byteBuffer("a\rb")]), + ("Content-Length: 17\r\nConnection: close\r\n\r\n", [byteBuffer("Content-Length: 17"), + byteBuffer("Connection: close"), + byteBuffer("")]) + ]) { + return LineBasedFrameDecoder() + } + } catch { + print(error) + XCTFail() + } + } }