Move EmbeddedChannel to its own library. (#1933)

Motivation:

EmbeddedChannel is an important testing tool, and we want to use it
without needing to bring along the POSIX layer. They are not tightly
coupled. However, it also doesn't belong naturally in NIOCore, so we
should probably put it in its own place.

Modifications:

- Moved EmbeddedChannel and EmbeddedEventLoop to NIOEmbedded.
- Moved the tests to NIOEmbeddedTests
- Duplicated some test helpers

Result:

Easy to use EmbeddedChannel without the POSIX layer.
This commit is contained in:
Cory Benfield 2021-08-11 10:50:32 +01:00 committed by GitHub
parent d62c733653
commit f2ab9ab422
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 130 additions and 7 deletions

View File

@ -19,13 +19,15 @@ var targets: [PackageDescription.Target] = [
.target(name: "NIOCore",
dependencies: ["NIOConcurrencyHelpers", "CNIOLinux"]),
.target(name: "_NIODataStructures"),
.target(name: "NIOEmbedded", dependencies: ["NIOCore", "_NIODataStructures"]),
.target(name: "NIO",
dependencies: ["CNIOLinux",
"CNIODarwin",
"CNIOWindows",
"NIOConcurrencyHelpers",
"NIOCore",
"_NIODataStructures"]),
"_NIODataStructures",
"NIOEmbedded"]),
.target(name: "_NIOConcurrency",
dependencies: ["NIO"]),
.target(name: "NIOFoundationCompat", dependencies: ["NIO"]),
@ -72,8 +74,10 @@ var targets: [PackageDescription.Target] = [
dependencies: ["NIO", "NIOHTTP1", "NIOWebSocket", "NIOFoundationCompat"]),
.target(name: "NIOAsyncAwaitDemo",
dependencies: ["NIO", "NIOHTTP1", "_NIOConcurrency"]),
.testTarget(name: "NIOEmbeddedTests",
dependencies: ["NIOConcurrencyHelpers", "NIOCore", "NIOEmbedded"]),
.testTarget(name: "NIOTests",
dependencies: ["NIO", "NIOFoundationCompat", "NIOTestUtils", "NIOConcurrencyHelpers"]),
dependencies: ["NIO", "NIOFoundationCompat", "NIOTestUtils", "NIOConcurrencyHelpers", "NIOEmbedded"]),
.testTarget(name: "NIOConcurrencyHelpersTests",
dependencies: ["NIOConcurrencyHelpers", "NIO"]),
.testTarget(name: "NIODataStructuresTests",
@ -95,6 +99,7 @@ let package = Package(
products: [
.library(name: "NIOCore", targets: ["NIOCore"]),
.library(name: "NIO", targets: ["NIO"]),
.library(name: "NIOEmbedded", targets: ["NIOEmbedded"]),
.library(name: "_NIOConcurrency", targets: ["_NIOConcurrency"]),
.library(name: "NIOTLS", targets: ["NIOTLS"]),
.library(name: "NIOHTTP1", targets: ["NIOHTTP1"]),

View File

@ -12,3 +12,4 @@
//
//===----------------------------------------------------------------------===//
@_exported import NIOCore
@_exported import NIOEmbedded

View File

@ -2,7 +2,7 @@
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2017-2020 Apple Inc. and the SwiftNIO project authors
// Copyright (c) 2017-2021 Apple Inc. and the SwiftNIO project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
@ -14,6 +14,7 @@
import Dispatch
import _NIODataStructures
import NIOCore
private final class EmbeddedScheduledTask {
let task: () -> Void

View File

@ -25,6 +25,7 @@ import XCTest
#if os(Linux) || os(FreeBSD) || os(Android)
@testable import NIOConcurrencyHelpersTests
@testable import NIODataStructuresTests
@testable import NIOEmbeddedTests
@testable import NIOFoundationCompatTests
@testable import NIOHTTP1Tests
@testable import NIOTLSTests

View File

@ -2,7 +2,7 @@
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2017-2019 Apple Inc. and the SwiftNIO project authors
// Copyright (c) 2017-2021 Apple Inc. and the SwiftNIO project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
@ -13,7 +13,60 @@
//===----------------------------------------------------------------------===//
import XCTest
@testable import NIO
import NIOCore
@testable import NIOEmbedded
class ChannelLifecycleHandler: ChannelInboundHandler {
public typealias InboundIn = Any
public enum ChannelState {
case unregistered
case registered
case inactive
case active
}
public var currentState: ChannelState
public var stateHistory: [ChannelState]
public init() {
currentState = .unregistered
stateHistory = [.unregistered]
}
private func updateState(_ state: ChannelState) {
currentState = state
stateHistory.append(state)
}
public func channelRegistered(context: ChannelHandlerContext) {
XCTAssertEqual(currentState, .unregistered)
XCTAssertFalse(context.channel.isActive)
updateState(.registered)
context.fireChannelRegistered()
}
public func channelActive(context: ChannelHandlerContext) {
XCTAssertEqual(currentState, .registered)
XCTAssertTrue(context.channel.isActive)
updateState(.active)
context.fireChannelActive()
}
public func channelInactive(context: ChannelHandlerContext) {
XCTAssertEqual(currentState, .active)
XCTAssertFalse(context.channel.isActive)
updateState(.inactive)
context.fireChannelInactive()
}
public func channelUnregistered(context: ChannelHandlerContext) {
XCTAssertEqual(currentState, .inactive)
XCTAssertFalse(context.channel.isActive)
updateState(.unregistered)
context.fireChannelUnregistered()
}
}
class EmbeddedChannelTest: XCTestCase {

View File

@ -2,7 +2,7 @@
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
// Copyright (c) 2017-2021 Apple Inc. and the SwiftNIO project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
@ -12,7 +12,8 @@
//
//===----------------------------------------------------------------------===//
@testable import NIO
import NIOCore
@testable import NIOEmbedded
import XCTest
private class EmbeddedTestError: Error { }

View File

@ -0,0 +1,61 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2021 Apple Inc. and the SwiftNIO project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
import Foundation
import XCTest
import NIOCore
import NIOConcurrencyHelpers
// FIXME: Duplicated with NIO
func assert(_ condition: @autoclosure () -> Bool, within time: TimeAmount, testInterval: TimeAmount? = nil, _ message: String = "condition not satisfied in time", file: StaticString = #file, line: UInt = #line) {
let testInterval = testInterval ?? TimeAmount.nanoseconds(time.nanoseconds / 5)
let endTime = NIODeadline.now() + time
repeat {
if condition() { return }
usleep(UInt32(testInterval.nanoseconds / 1000))
} while (NIODeadline.now() < endTime)
if !condition() {
XCTFail(message, file: (file), line: line)
}
}
extension EventLoopFuture {
var isFulfilled: Bool {
if self.eventLoop.inEventLoop {
// Easy, we're on the EventLoop. Let's just use our knowledge that we run completed future callbacks
// immediately.
var fulfilled = false
self.whenComplete { _ in
fulfilled = true
}
return fulfilled
} else {
let lock = Lock()
let group = DispatchGroup()
var fulfilled = false // protected by lock
group.enter()
self.eventLoop.execute {
let isFulfilled = self.isFulfilled // This will now enter the above branch.
lock.withLock {
fulfilled = isFulfilled
}
group.leave()
}
group.wait() // this is very nasty but this is for tests only, so...
return lock.withLock { fulfilled }
}
}
}