Skip to content

Commit 52b38df

Browse files
committed
Add server ping/pong test coverage
1 parent 3a934c8 commit 52b38df

2 files changed

Lines changed: 93 additions & 1 deletion

File tree

Tests/WebSocketTests/Server/WebSocketServer.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import WebSocketKit
77

88
enum WebSocketServerOutput {
99
case message(WebSocketMessage)
10+
case ping(Data)
1011
case remoteClose
1112
case remoteCloseWithReason(WebSocketErrorCode, Data)
1213
}
@@ -23,6 +24,7 @@ final class WebSocketServer {
2324

2425
// Publisher that repeats everything sent to it by clients.
2526
private let inputSubject = PassthroughSubject<WebSocketMessage, Never>()
27+
private let pongSubject = PassthroughSubject<Data, Never>()
2628

2729
private let eventLoopGroup: EventLoopGroup
2830
private var channel: Channel?
@@ -52,6 +54,14 @@ final class WebSocketServer {
5254
) else { return }
5355
self?.inputSubject.send(.data(data))
5456
}
57+
ws.onPong { [weak self] _, pong in
58+
var pong = pong
59+
guard let data = pong.readData(
60+
length: pong.readableBytes,
61+
byteTransferStrategy: .copy
62+
) else { return }
63+
self?.pongSubject.send(data)
64+
}
5565
}.bind(host: "127.0.0.1", port: 0).wait()
5666
}
5767

@@ -69,6 +79,9 @@ final class WebSocketServer {
6979
},
7080
receiveValue: { output in
7181
switch output {
82+
case let .ping(data):
83+
ws.sendPing(data)
84+
7285
case .remoteClose:
7386
do { try ws.close(code: .goingAway).wait() }
7487
catch {}
@@ -103,4 +116,8 @@ final class WebSocketServer {
103116
var inputPublisher: AnyPublisher<WebSocketMessage, Never> {
104117
inputSubject.eraseToAnyPublisher()
105118
}
119+
120+
var pongPublisher: AnyPublisher<Data, Never> {
121+
pongSubject.eraseToAnyPublisher()
122+
}
106123
}

Tests/WebSocketTests/SystemWebSocketTests.swift

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ class SystemWebSocketTests: XCTestCase {
160160
else { return XCTFail("Received wrong error: \(error)") }
161161
}
162162

163-
await fulfillment(of: [secondCloseEx], timeout: 0.1)
163+
await fulfillment(of: [secondCloseEx], timeout: 0.05)
164164
}
165165

166166
func testDelegateDoesNotReorderOpenAndCloseCallbacks() async throws {
@@ -292,6 +292,81 @@ class SystemWebSocketTests: XCTestCase {
292292
await fulfillment(of: [receivedEx], timeout: 2)
293293
}
294294

295+
func testServerPingReceivesPongAndDoesNotCloseClient() async throws {
296+
let closeEx = expectation(description: "Should not close after ping")
297+
closeEx.isInverted = true
298+
let shouldFailOnClose = Locked(true)
299+
300+
let (server, client) = try await makeServerAndClient(
301+
onClose: { _ in
302+
guard shouldFailOnClose.access({ $0 }) else { return }
303+
closeEx.fulfill()
304+
}
305+
)
306+
defer { server.shutDown() }
307+
308+
let pingPayload = Data("server ping".utf8)
309+
let pongEx = expectation(description: "Server should receive pong")
310+
let pongSub = server.pongPublisher
311+
.sink { pong in
312+
XCTAssertEqual(pingPayload, pong)
313+
pongEx.fulfill()
314+
}
315+
defer { pongSub.cancel() }
316+
317+
let readyEx = expectation(description: "Should receive initial app message")
318+
let receivedEx = expectation(description: "Should still receive app messages")
319+
let receivedSub = client.sink { message in
320+
guard case let .text(text) = message else {
321+
return XCTFail("Should have received text")
322+
}
323+
324+
switch text {
325+
case "ready":
326+
readyEx.fulfill()
327+
328+
case "still open":
329+
receivedEx.fulfill()
330+
331+
default:
332+
XCTFail("Received unexpected text: \(text)")
333+
}
334+
}
335+
defer { receivedSub.cancel() }
336+
337+
let sentEx = expectation(description: "Server should receive client message")
338+
let sentSub = server.inputPublisher
339+
.sink { message in
340+
guard case let .text(text) = message else {
341+
return XCTFail("Should have received text")
342+
}
343+
XCTAssertEqual("client ready", text)
344+
sentEx.fulfill()
345+
}
346+
defer { sentSub.cancel() }
347+
348+
try await client.open()
349+
350+
try await client.send(.text("client ready"))
351+
await fulfillment(of: [sentEx], timeout: 2)
352+
353+
subject.send(.message(.text("ready")))
354+
await fulfillment(of: [readyEx], timeout: 2)
355+
356+
subject.send(.ping(pingPayload))
357+
await fulfillment(of: [pongEx], timeout: 2)
358+
359+
let isOpen = await client.isOpen
360+
XCTAssertTrue(isOpen)
361+
362+
subject.send(.message(.text("still open")))
363+
await fulfillment(of: [receivedEx], timeout: 2)
364+
await fulfillment(of: [closeEx], timeout: 0.05)
365+
366+
shouldFailOnClose.access { $0 = false }
367+
try await client.close()
368+
}
369+
295370
@available(iOS 15.0, macOS 12.0, *)
296371
func testPushAndReceiveDataWithAsyncPublisher() async throws {
297372
let (server, client) = try await makeServerAndClient()

0 commit comments

Comments
 (0)