我有一個適用于 UI 組件的 XCTest。我嘗試使用 SwiftNIO 在 xctext 函式中打開服務器套接字。
我從這里拿回聲服務器的例子。為了進行臟測驗,我簡化了,洗掉了帶有硬編碼值的引數。
import XCTest
import NIOCore
import NIOPosix
private final class EchoHandler: ChannelInboundHandler {
public typealias InboundIn = ByteBuffer
public typealias OutboundOut = ByteBuffer
public func channelRead(context: ChannelHandlerContext, data: NIOAny) {
// As we are not really interested getting notified on success or failure we just pass nil as promise to
// reduce allocations.
context.write(data, promise: nil)
}
// Flush it out. This can make use of gathering writes if multiple buffers are pending
public func channelReadComplete(context: ChannelHandlerContext) {
context.flush()
}
public func errorCaught(context: ChannelHandlerContext, error: Error) {
print("error: ", error)
// As we are not really interested getting notified on success or failure we just pass nil as promise to
// reduce allocations.
context.close(promise: nil)
}
}
class MyXCTests: XCTestCase {
var app: XCUIApplication!
override func setUpWithError() throws {
continueAfterFailure = false
app = XCUIApplication()
// Catch system alerts such as "allow connecting to Wi-fi network"
addUIInterruptionMonitor(withDescription: "System Dialog") { (alert) -> Bool in
alert.buttons["Join"].tap()
return true
}
}
override func tearDownWithError() throws {
}
func testXYZ() throws {
app.launch()
let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)
let bootstrap = ServerBootstrap(group: group)
// Specify backlog and enable SO_REUSEADDR for the server itself
.serverChannelOption(ChannelOptions.backlog, value: 256)
.serverChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
// Set the handlers that are appled to the accepted Channels
.childChannelInitializer { channel in
// Ensure we don't read faster than we can write by adding the BackPressureHandler into the pipeline.
channel.pipeline.addHandler(BackPressureHandler()).flatMap { v in
channel.pipeline.addHandler(EchoHandler())
}
}
// Enable SO_REUSEADDR for the accepted Channels
.childChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
.childChannelOption(ChannelOptions.maxMessagesPerRead, value: 16)
.childChannelOption(ChannelOptions.recvAllocator, value: AdaptiveRecvByteBufferAllocator())
defer {
try! group.syncShutdownGracefully()
}
let channel = try { () -> Channel in
return try bootstrap.bind(host: "0.0.0.0", port: 1234).wait()
}()
print("============= Server started and listening on \(channel.localAddress!)")
// then some XCTest code which works here was cut from this snippet
}
測驗運行正確,它還列印
============= Server started and listening on [IPv4]0.0.0.0/0.0.0.0:1234
但實際上這里的EchoClient不起作用
swift run NIOEchoClient localhost 1234 ? 1785
[0/0] Build complete!
Please enter line to send to the server
dfsdfd
Swift/ErrorType.swift:200: Fatal error: Error raised at top level: NIOPosix.NIOConnectionError(host: "localhost", port: 1234, dnsAError: nil, dnsAAAAError: nil, connectionErrors: [NIOPosix.SingleConnectionFailure(target: [IPv6]localhost/::1:1234, error: connection reset (error set): Connection refused (errno: 61)), NIOPosix.SingleConnectionFailure(target: [IPv4]localhost/127.0.0.1:1234, error: connection reset (error set): Connection refused (errno: 61))])
[1] 28213 trace trap swift run NIOEchoClient localhost 1234
偵聽套接字也無法使用
sudo lsof -PiTCP -sTCP:LISTEN
我還嘗試將 UITestEntitlements 設定com.apple.security.app-sandbox為 false。
有沒有辦法允許來自 XCTest 的服務器套接字?
最初我試圖嵌入一個 Swift-GRPC 端點,以允許從回圈控制器中的硬體進行更細粒度的控制。目的是使用命令列 xcodebuild 啟動 XCTest,這反過來又會啟動一個長時間運行的測驗,但不是用 Swift 撰寫的測驗代碼,我會公開事件何時點擊一些按鈕,就在測驗程序之外grpc 端點。
由于 grpc 端點不起作用,我將問題簡化為上述問題。
任何人都有提示,如何解決這個問題,或者有任何提示為什么永遠不可能在 XCTest 應用程式中打開服務器套接字,請不要猶豫在這里回復。
uj5u.com熱心網友回復:
是的,這是可能的,您可以在AsyncHTTPClient和 SwiftNIO 測驗套件中找到許多此類示例。
你的不起作用的原因是因為你MultiThreadedEventLoopGroup在系結套接字后關閉了右邊。所以從本質上講,您正在啟動一切,然后再次將其關閉。
另外,對于單元測驗,我建議127.0.0.1只系結到因為您可能不想要來自其他地方的連接。另一個好主意是使用臨時埠,即。讓系統自動選擇一個免費的隨機埠。您可以通過指定 port 來實作這一點0。在您bind使用服務器之后,Channel您可以通過使用serverChannel.localAddress?.port!它選擇的埠來查詢服務器通道。
這是一個完整的示例,在測驗用例中包含一個客戶端和一個服務器。
import XCTest
import NIO
final class ExampleTests: XCTestCase {
// We keep the `EventLoopGroup` where all the I/O runs alive during the test
private var group: EventLoopGroup!
// Same for the server channel.
private var serverChannel: Channel!
private var serverAddress: SocketAddress {
return self.serverChannel.localAddress!
}
// We set up the server in `setUp`...
override func setUp() {
self.group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
XCTAssertNoThrow(self.serverChannel = try ServerBootstrap(group: self.group)
.childChannelInitializer { channel in
channel.pipeline.addHandler(UppercasingHandler())
}
.bind(host: "127.0.0.1", port: 0) // port 0 means: pick a free port.
.wait())
}
// ... and tear it down in `tearDown`.
override func tearDown() {
XCTAssertNoThrow(try self.serverChannel?.close().wait())
XCTAssertNoThrow(try self.group?.syncShutdownGracefully())
}
func testExample() throws {
// Here we just bootstrap a little client that sends "Hello world!\n"
let clientChannel = try ClientBootstrap(group: self.group)
.channelInitializer { channel in
channel.pipeline.addHandler(PrintEverythingHandler())
}
.connect(to: self.serverAddress)
.wait()
XCTAssertNoThrow(try clientChannel
.writeAndFlush(ByteBuffer(string: "Hello world!\n"))
.wait())
XCTAssertNoThrow(try clientChannel.closeFuture.wait())
}
}
// Just a handler that uses the C `toupper` function which uppercases characters.
final class UppercasingHandler: ChannelInboundHandler {
typealias InboundIn = ByteBuffer
typealias OutboundOut = ByteBuffer
func channelRead(context: ChannelHandlerContext, data: NIOAny) {
let inBuffer = self.unwrapInboundIn(data)
var outBuffer = context.channel.allocator.buffer(capacity: inBuffer.readableBytes)
// Here we just upper case each byte using the C stdlib's `toupper` function.
outBuffer.writeBytes(inBuffer.readableBytesView.map { UInt8(toupper(CInt($0))) })
context.writeAndFlush(self.wrapOutboundOut(outBuffer),
promise: nil)
}
// We want to close the connection on any error really.
func errorCaught(context: ChannelHandlerContext, error: Error) {
print("server: unexpected error \(error), closing")
context.close(promise: nil)
}
}
// This handler just prints everything using the `write` system call. And closes the connection on a newline.
final class PrintEverythingHandler: ChannelInboundHandler {
typealias InboundIn = ByteBuffer
func channelRead(context: ChannelHandlerContext, data: NIOAny) {
let inBuffer = self.unwrapInboundIn(data)
guard inBuffer.readableBytes > 0 else {
return
}
// We're using Unsafe* stuff here because we're using the `write` system call, which is a C function.
_ = inBuffer.withUnsafeReadableBytes { ptr in
write(STDOUT_FILENO, ptr.baseAddress!, ptr.count)
}
// If we see a newline, then let's actually close the connection...
if inBuffer.readableBytesView.contains(UInt8(ascii: "\n")) {
print("found newline, closing...")
context.close(promise: nil)
}
}
func errorCaught(context: ChannelHandlerContext, error: Error) {
print("client: unexpected error \(error), closing")
context.close(promise: nil)
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/390822.html
