Skip to content

Commit f2ab9ab

Browse files
authored
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.
1 parent d62c733 commit f2ab9ab

File tree

9 files changed

+130
-7
lines changed

9 files changed

+130
-7
lines changed

Package.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@ var targets: [PackageDescription.Target] = [
1919
.target(name: "NIOCore",
2020
dependencies: ["NIOConcurrencyHelpers", "CNIOLinux"]),
2121
.target(name: "_NIODataStructures"),
22+
.target(name: "NIOEmbedded", dependencies: ["NIOCore", "_NIODataStructures"]),
2223
.target(name: "NIO",
2324
dependencies: ["CNIOLinux",
2425
"CNIODarwin",
2526
"CNIOWindows",
2627
"NIOConcurrencyHelpers",
2728
"NIOCore",
28-
"_NIODataStructures"]),
29+
"_NIODataStructures",
30+
"NIOEmbedded"]),
2931
.target(name: "_NIOConcurrency",
3032
dependencies: ["NIO"]),
3133
.target(name: "NIOFoundationCompat", dependencies: ["NIO"]),
@@ -72,8 +74,10 @@ var targets: [PackageDescription.Target] = [
7274
dependencies: ["NIO", "NIOHTTP1", "NIOWebSocket", "NIOFoundationCompat"]),
7375
.target(name: "NIOAsyncAwaitDemo",
7476
dependencies: ["NIO", "NIOHTTP1", "_NIOConcurrency"]),
77+
.testTarget(name: "NIOEmbeddedTests",
78+
dependencies: ["NIOConcurrencyHelpers", "NIOCore", "NIOEmbedded"]),
7579
.testTarget(name: "NIOTests",
76-
dependencies: ["NIO", "NIOFoundationCompat", "NIOTestUtils", "NIOConcurrencyHelpers"]),
80+
dependencies: ["NIO", "NIOFoundationCompat", "NIOTestUtils", "NIOConcurrencyHelpers", "NIOEmbedded"]),
7781
.testTarget(name: "NIOConcurrencyHelpersTests",
7882
dependencies: ["NIOConcurrencyHelpers", "NIO"]),
7983
.testTarget(name: "NIODataStructuresTests",
@@ -95,6 +99,7 @@ let package = Package(
9599
products: [
96100
.library(name: "NIOCore", targets: ["NIOCore"]),
97101
.library(name: "NIO", targets: ["NIO"]),
102+
.library(name: "NIOEmbedded", targets: ["NIOEmbedded"]),
98103
.library(name: "_NIOConcurrency", targets: ["_NIOConcurrency"]),
99104
.library(name: "NIOTLS", targets: ["NIOTLS"]),
100105
.library(name: "NIOHTTP1", targets: ["NIOHTTP1"]),

Sources/NIO/ExportCore.swift renamed to Sources/NIO/Exports.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@
1212
//
1313
//===----------------------------------------------------------------------===//
1414
@_exported import NIOCore
15+
@_exported import NIOEmbedded

Sources/NIO/Embedded.swift renamed to Sources/NIOEmbedded/Embedded.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the SwiftNIO open source project
44
//
5-
// Copyright (c) 2017-2020 Apple Inc. and the SwiftNIO project authors
5+
// Copyright (c) 2017-2021 Apple Inc. and the SwiftNIO project authors
66
// Licensed under Apache License v2.0
77
//
88
// See LICENSE.txt for license information
@@ -14,6 +14,7 @@
1414

1515
import Dispatch
1616
import _NIODataStructures
17+
import NIOCore
1718

1819
private final class EmbeddedScheduledTask {
1920
let task: () -> Void

Tests/LinuxMain.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import XCTest
2525
#if os(Linux) || os(FreeBSD) || os(Android)
2626
@testable import NIOConcurrencyHelpersTests
2727
@testable import NIODataStructuresTests
28+
@testable import NIOEmbeddedTests
2829
@testable import NIOFoundationCompatTests
2930
@testable import NIOHTTP1Tests
3031
@testable import NIOTLSTests
File renamed without changes.

Tests/NIOTests/EmbeddedChannelTest.swift renamed to Tests/NIOEmbeddedTests/EmbeddedChannelTest.swift

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the SwiftNIO open source project
44
//
5-
// Copyright (c) 2017-2019 Apple Inc. and the SwiftNIO project authors
5+
// Copyright (c) 2017-2021 Apple Inc. and the SwiftNIO project authors
66
// Licensed under Apache License v2.0
77
//
88
// See LICENSE.txt for license information
@@ -13,7 +13,60 @@
1313
//===----------------------------------------------------------------------===//
1414

1515
import XCTest
16-
@testable import NIO
16+
import NIOCore
17+
@testable import NIOEmbedded
18+
19+
class ChannelLifecycleHandler: ChannelInboundHandler {
20+
public typealias InboundIn = Any
21+
22+
public enum ChannelState {
23+
case unregistered
24+
case registered
25+
case inactive
26+
case active
27+
}
28+
29+
public var currentState: ChannelState
30+
public var stateHistory: [ChannelState]
31+
32+
public init() {
33+
currentState = .unregistered
34+
stateHistory = [.unregistered]
35+
}
36+
37+
private func updateState(_ state: ChannelState) {
38+
currentState = state
39+
stateHistory.append(state)
40+
}
41+
42+
public func channelRegistered(context: ChannelHandlerContext) {
43+
XCTAssertEqual(currentState, .unregistered)
44+
XCTAssertFalse(context.channel.isActive)
45+
updateState(.registered)
46+
context.fireChannelRegistered()
47+
}
48+
49+
public func channelActive(context: ChannelHandlerContext) {
50+
XCTAssertEqual(currentState, .registered)
51+
XCTAssertTrue(context.channel.isActive)
52+
updateState(.active)
53+
context.fireChannelActive()
54+
}
55+
56+
public func channelInactive(context: ChannelHandlerContext) {
57+
XCTAssertEqual(currentState, .active)
58+
XCTAssertFalse(context.channel.isActive)
59+
updateState(.inactive)
60+
context.fireChannelInactive()
61+
}
62+
63+
public func channelUnregistered(context: ChannelHandlerContext) {
64+
XCTAssertEqual(currentState, .inactive)
65+
XCTAssertFalse(context.channel.isActive)
66+
updateState(.unregistered)
67+
context.fireChannelUnregistered()
68+
}
69+
}
1770

1871
class EmbeddedChannelTest: XCTestCase {
1972

File renamed without changes.

Tests/NIOTests/EmbeddedEventLoopTest.swift renamed to Tests/NIOEmbeddedTests/EmbeddedEventLoopTest.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the SwiftNIO open source project
44
//
5-
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
5+
// Copyright (c) 2017-2021 Apple Inc. and the SwiftNIO project authors
66
// Licensed under Apache License v2.0
77
//
88
// See LICENSE.txt for license information
@@ -12,7 +12,8 @@
1212
//
1313
//===----------------------------------------------------------------------===//
1414

15-
@testable import NIO
15+
import NIOCore
16+
@testable import NIOEmbedded
1617
import XCTest
1718

1819
private class EmbeddedTestError: Error { }
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the SwiftNIO open source project
4+
//
5+
// Copyright (c) 2021 Apple Inc. and the SwiftNIO project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
import Foundation
15+
import XCTest
16+
import NIOCore
17+
import NIOConcurrencyHelpers
18+
19+
// FIXME: Duplicated with NIO
20+
func assert(_ condition: @autoclosure () -> Bool, within time: TimeAmount, testInterval: TimeAmount? = nil, _ message: String = "condition not satisfied in time", file: StaticString = #file, line: UInt = #line) {
21+
let testInterval = testInterval ?? TimeAmount.nanoseconds(time.nanoseconds / 5)
22+
let endTime = NIODeadline.now() + time
23+
24+
repeat {
25+
if condition() { return }
26+
usleep(UInt32(testInterval.nanoseconds / 1000))
27+
} while (NIODeadline.now() < endTime)
28+
29+
if !condition() {
30+
XCTFail(message, file: (file), line: line)
31+
}
32+
}
33+
34+
extension EventLoopFuture {
35+
var isFulfilled: Bool {
36+
if self.eventLoop.inEventLoop {
37+
// Easy, we're on the EventLoop. Let's just use our knowledge that we run completed future callbacks
38+
// immediately.
39+
var fulfilled = false
40+
self.whenComplete { _ in
41+
fulfilled = true
42+
}
43+
return fulfilled
44+
} else {
45+
let lock = Lock()
46+
let group = DispatchGroup()
47+
var fulfilled = false // protected by lock
48+
49+
group.enter()
50+
self.eventLoop.execute {
51+
let isFulfilled = self.isFulfilled // This will now enter the above branch.
52+
lock.withLock {
53+
fulfilled = isFulfilled
54+
}
55+
group.leave()
56+
}
57+
group.wait() // this is very nasty but this is for tests only, so...
58+
return lock.withLock { fulfilled }
59+
}
60+
}
61+
}

0 commit comments

Comments
 (0)