@@ -2146,6 +2146,107 @@ final class TestURLSession: LoopbackServerTest, @unchecked Sendable {
2146
2146
XCTAssertEqual ( delegate. callbacks, callbacks, " Callbacks for \( #function) " )
2147
2147
}
2148
2148
2149
+ func test_webSocketCompletions( ) async throws {
2150
+ guard #available( macOS 10 . 15 , iOS 13 . 0 , watchOS 6 . 0 , tvOS 13 . 0 , * ) else { return }
2151
+ guard URLSessionWebSocketTask . supportsWebSockets else {
2152
+ print ( " libcurl lacks WebSockets support, skipping \( #function) " )
2153
+ return
2154
+ }
2155
+
2156
+ let urlString = " ws://127.0.0.1: \( TestURLSession . serverPort) /web-socket "
2157
+ let url = try XCTUnwrap ( URL ( string: urlString) )
2158
+ let request = URLRequest ( url: url)
2159
+
2160
+ let delegate = SessionDelegate ( with: expectation ( description: " \( urlString) : Connect " ) )
2161
+ let task = delegate. runWebSocketTask ( with: request, timeoutInterval: 4 )
2162
+
2163
+ // We interleave sending and receiving, as the test HTTPServer implementation is barebones, and can't handle receiving more than one frame at a time. So, this back-and-forth acts as a gating mechanism
2164
+
2165
+ let didCompleteSendingString = expectation ( description: " Did complete sending a string " )
2166
+ task. send ( . string( " Hello " ) ) { error in
2167
+ XCTAssertNil ( error)
2168
+ didCompleteSendingString. fulfill ( )
2169
+ }
2170
+ await fulfillment ( of: [ didCompleteSendingString] , timeout: 5.0 )
2171
+
2172
+ let didCompleteReceivingString = expectation ( description: " Did complete receiving a string " )
2173
+ task. receive { result in
2174
+ switch result {
2175
+ case . failure( let error) :
2176
+ XCTFail ( )
2177
+ case . success( let stringMessage) :
2178
+ switch stringMessage {
2179
+ case . string( let str) :
2180
+ XCTAssert ( str == " Hello " )
2181
+ default :
2182
+ XCTFail ( " Unexpected String Message " )
2183
+ }
2184
+ }
2185
+ didCompleteReceivingString. fulfill ( )
2186
+ }
2187
+ await fulfillment ( of: [ didCompleteReceivingString] , timeout: 5.0 )
2188
+
2189
+ let didCompleteSendingData = expectation ( description: " Did complete sending data " )
2190
+ task. send ( . data( Data ( [ 0x20 , 0x22 , 0x10 , 0x03 ] ) ) ) { error in
2191
+ XCTAssertNil ( error)
2192
+ didCompleteSendingData. fulfill ( )
2193
+ }
2194
+ await fulfillment ( of: [ didCompleteSendingData] , timeout: 5.0 )
2195
+
2196
+ let didCompleteReceivingData = expectation ( description: " Did complete receiving data " )
2197
+ task. receive { result in
2198
+ switch result {
2199
+ case . failure( let error) :
2200
+ XCTFail ( )
2201
+ case . success( let dataMessage) :
2202
+ switch dataMessage {
2203
+ case . data( let data) :
2204
+ XCTAssert ( data == Data ( [ 0x20 , 0x22 , 0x10 , 0x03 ] ) )
2205
+ default :
2206
+ XCTFail ( " Unexpected Data Message " )
2207
+ }
2208
+ }
2209
+ didCompleteReceivingData. fulfill ( )
2210
+ }
2211
+ await fulfillment ( of: [ didCompleteReceivingData] , timeout: 5.0 )
2212
+
2213
+ let didCompleteSendingPing = expectation ( description: " Did complete sending ping " )
2214
+ task. sendPing { error in
2215
+ if let error {
2216
+ // Server closed the connection before we could process the pong
2217
+ if let urlError = error as? URLError {
2218
+ XCTAssertEqual ( urlError. _nsError. code, NSURLErrorNetworkConnectionLost)
2219
+ } else {
2220
+ XCTFail ( " Unexpecter error type " )
2221
+ }
2222
+ }
2223
+ didCompleteSendingPing. fulfill ( )
2224
+ }
2225
+ await fulfillment ( of: [ delegate. expectation, didCompleteSendingPing] , timeout: 50.0 )
2226
+
2227
+ let didCompleteReceiving = expectation ( description: " Did complete receiving " )
2228
+ task. receive { result in
2229
+ switch result {
2230
+ case . failure( let error) :
2231
+ if let urlError = error as? URLError {
2232
+ XCTAssertEqual ( urlError. _nsError. code, NSURLErrorNetworkConnectionLost)
2233
+ } else {
2234
+ XCTFail ( " Unexpecter error type " )
2235
+ }
2236
+ case . success:
2237
+ XCTFail ( " Expected to throw when receiving on closed task " )
2238
+ }
2239
+ didCompleteReceiving. fulfill ( )
2240
+ }
2241
+ await fulfillment ( of: [ didCompleteReceiving] , timeout: 5.0 )
2242
+
2243
+ let callbacks = [ " urlSession(_:webSocketTask:didOpenWithProtocol:) " ,
2244
+ " urlSession(_:webSocketTask:didCloseWith:reason:) " ,
2245
+ " urlSession(_:task:didCompleteWithError:) " ]
2246
+ XCTAssertEqual ( delegate. callbacks. count, callbacks. count)
2247
+ XCTAssertEqual ( delegate. callbacks, callbacks, " Callbacks for \( #function) " )
2248
+ }
2249
+
2149
2250
func test_webSocketSpecificProtocol( ) async throws {
2150
2251
guard #available( macOS 12 , iOS 13 . 0 , watchOS 6 . 0 , tvOS 13 . 0 , * ) else { return }
2151
2252
guard URLSessionWebSocketTask . supportsWebSockets else {
0 commit comments