blob: 2f8282233db7c3992ce672308ea1e9020acd5eee [file] [log] [blame]
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
class TestURLSession : LoopbackServerTest {
func test_dataTaskWithURL() {
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/Nepal"
let url = URL(string: urlString)!
let d = DataTask(with: expectation(description: "GET \(urlString): with a delegate"))
d.run(with: url)
waitForExpectations(timeout: 12)
if !d.error {
XCTAssertEqual(d.capital, "Kathmandu", "test_dataTaskWithURLRequest returned an unexpected result")
}
}
func test_dataTaskWithURLCompletionHandler() {
//shared session
dataTaskWithURLCompletionHandler(with: URLSession.shared)
//new session
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 8
let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
dataTaskWithURLCompletionHandler(with: session)
}
func dataTaskWithURLCompletionHandler(with session: URLSession) {
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/USA"
let url = URL(string: urlString)!
let expect = expectation(description: "GET \(urlString): with a completion handler")
var expectedResult = "unknown"
let task = session.dataTask(with: url) { data, response, error in
defer { expect.fulfill() }
XCTAssertNil(error as? URLError, "error = \(error as! URLError)")
XCTAssertNotNil(response)
XCTAssertNotNil(data)
guard let httpResponse = response as? HTTPURLResponse, let data = data else { return }
XCTAssertEqual(200, httpResponse.statusCode, "HTTP response code is not 200")
expectedResult = String(data: data, encoding: .utf8) ?? ""
XCTAssertEqual("Washington, D.C.", expectedResult, "Did not receive expected value")
}
task.resume()
waitForExpectations(timeout: 12)
}
func test_dataTaskWithURLRequest() {
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/Peru"
let urlRequest = URLRequest(url: URL(string: urlString)!)
let d = DataTask(with: expectation(description: "GET \(urlString): with a delegate"))
d.run(with: urlRequest)
waitForExpectations(timeout: 12)
if !d.error {
XCTAssertEqual(d.capital, "Lima", "test_dataTaskWithURLRequest returned an unexpected result")
}
}
func test_dataTaskWithURLRequestCompletionHandler() {
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/Italy"
let urlRequest = URLRequest(url: URL(string: urlString)!)
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 8
let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
let expect = expectation(description: "GET \(urlString): with a completion handler")
var expectedResult = "unknown"
let task = session.dataTask(with: urlRequest) { data, response, error in
defer { expect.fulfill() }
XCTAssertNotNil(data)
XCTAssertNotNil(response)
XCTAssertNil(error as? URLError, "error = \(error as! URLError)")
guard let httpResponse = response as? HTTPURLResponse, let data = data else { return }
XCTAssertEqual(200, httpResponse.statusCode, "HTTP response code is not 200")
expectedResult = String(data: data, encoding: .utf8) ?? ""
XCTAssertEqual("Rome", expectedResult, "Did not receive expected value")
}
task.resume()
waitForExpectations(timeout: 12)
}
func test_dataTaskWithHttpInputStream() {
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/echo"
let dataString = """
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras congue laoreet facilisis. Sed porta tristique orci. Fusce ut nisl dignissim, tempor tortor id, molestie neque. Nam non tincidunt mi. Integer ac diam quis leo aliquam congue et non magna. In porta mauris suscipit erat pulvinar, sed fringilla quam ornare. Nulla vulputate et ligula vitae sollicitudin. Nulla vel vehicula risus. Quisque eu urna ullamcorper, tincidunt ante vitae, aliquet sem. Suspendisse nec turpis placerat, porttitor ex vel, tristique orci. Maecenas pretium, augue non elementum imperdiet, diam ex vestibulum tortor, non ultrices ante enim iaculis ex.
Suspendisse ante eros, scelerisque ut molestie vitae, lacinia nec metus. Sed in feugiat sem. Nullam sed congue nulla, id vehicula mauris. Aliquam ultrices ultricies pellentesque. Etiam blandit ultrices quam in egestas. Donec a vulputate est, ut ultricies dui. In non maximus velit.
Vivamus vehicula faucibus odio vel maximus. Vivamus elementum, quam at accumsan rhoncus, ex ligula maximus sem, sed pretium urna enim ut urna. Donec semper porta augue at faucibus. Quisque vel congue purus. Morbi vitae elit pellentesque, finibus lectus quis, laoreet nulla. Praesent in fermentum felis. Aenean vestibulum dictum lorem quis egestas. Sed dictum elementum est laoreet volutpat.
"""
let url = URL(string: urlString)!
let urlSession = URLSession(configuration: URLSessionConfiguration.default)
var urlRequest = URLRequest(url: url)
urlRequest.httpMethod = "POST"
guard let data = dataString.data(using: .utf8) else {
XCTFail()
return
}
let inputStream = InputStream(data: data)
inputStream.open()
urlRequest.httpBodyStream = inputStream
urlRequest.setValue("en-us", forHTTPHeaderField: "Accept-Language")
urlRequest.setValue("text/xml; charset=utf-8", forHTTPHeaderField: "Content-Type")
urlRequest.setValue("chunked", forHTTPHeaderField: "Transfer-Encoding")
let expect = expectation(description: "POST \(urlString): with HTTP Body as InputStream")
let task = urlSession.dataTask(with: urlRequest) { respData, response, error in
XCTAssertNotNil(respData)
XCTAssertNotNil(response)
XCTAssertNil(error)
defer { expect.fulfill() }
guard let httpResponse = response as? HTTPURLResponse else {
XCTFail("response (\(response.debugDescription)) invalid")
return
}
XCTAssertEqual(data, respData!, "Response Data and Data is not equal")
XCTAssertEqual(200, httpResponse.statusCode, "HTTP response code is not 200")
}
task.resume()
waitForExpectations(timeout: 12)
}
func test_gzippedDataTask() {
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/gzipped-response"
let url = URL(string: urlString)!
let d = DataTask(with: expectation(description: "GET \(urlString): gzipped response"))
d.run(with: url)
waitForExpectations(timeout: 12)
if !d.error {
XCTAssertEqual(d.capital, "Hello World!")
}
}
func test_downloadTaskWithURL() {
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/country.txt"
let url = URL(string: urlString)!
let d = DownloadTask(testCase: self, description: "Download GET \(urlString): with a delegate")
d.run(with: url)
waitForExpectations(timeout: 12)
}
func test_downloadTaskWithURLRequest() {
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/country.txt"
let urlRequest = URLRequest(url: URL(string: urlString)!)
let d = DownloadTask(testCase: self, description: "Download GET \(urlString): with a delegate")
d.run(with: urlRequest)
waitForExpectations(timeout: 12)
}
func test_downloadTaskWithRequestAndHandler() {
//shared session
downloadTaskWithRequestAndHandler(with: URLSession.shared)
//newly created session
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 8
let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
downloadTaskWithRequestAndHandler(with: session)
}
func downloadTaskWithRequestAndHandler(with session: URLSession) {
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/country.txt"
let expect = expectation(description: "Download GET \(urlString): with a completion handler")
let req = URLRequest(url: URL(string: urlString)!)
let task = session.downloadTask(with: req) { (_, _, error) -> Void in
XCTAssertNil(error as? URLError, "error = \(error as! URLError)")
expect.fulfill()
}
task.resume()
waitForExpectations(timeout: 12)
}
func test_downloadTaskWithURLAndHandler() {
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 8
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/country.txt"
let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
let expect = expectation(description: "Download GET \(urlString): with a completion handler")
let req = URLRequest(url: URL(string: urlString)!)
let task = session.downloadTask(with: req) { (_, _, error) -> Void in
if let e = error as? URLError {
XCTAssertEqual(e.code, .timedOut, "Unexpected error code")
}
expect.fulfill()
}
task.resume()
waitForExpectations(timeout: 12)
}
func test_gzippedDownloadTask() {
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/gzipped-response"
let url = URL(string: urlString)!
let d = DownloadTask(testCase: self, description: "GET \(urlString): gzipped response")
d.run(with: url)
waitForExpectations(timeout: 12)
if d.totalBytesWritten != "Hello World!".utf8.count {
XCTFail("Expected the gzipped-response to be the length of Hello World!")
}
}
func test_finishTasksAndInvalidate() {
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/Nepal"
let invalidateExpectation = expectation(description: "Session invalidation")
let delegate = SessionDelegate(invalidateExpectation: invalidateExpectation)
let url = URL(string: urlString)!
let session = URLSession(configuration: URLSessionConfiguration.default,
delegate: delegate, delegateQueue: nil)
let completionExpectation = expectation(description: "GET \(urlString): task completion before session invalidation")
let task = session.dataTask(with: url) { (_, _, _) in
completionExpectation.fulfill()
}
task.resume()
session.finishTasksAndInvalidate()
waitForExpectations(timeout: 12)
}
func test_taskError() {
let urlString = "http://127.0.0.1:-1/Nepal"
let url = URL(string: urlString)!
let session = URLSession(configuration: URLSessionConfiguration.default,
delegate: nil,
delegateQueue: nil)
let completionExpectation = expectation(description: "GET \(urlString): Bad URL error")
let task = session.dataTask(with: url) { (_, _, result) in
let error = result as? URLError
XCTAssertNotNil(error)
XCTAssertEqual(error?.code, .badURL)
completionExpectation.fulfill()
}
//should result in Bad URL error
task.resume()
waitForExpectations(timeout: 5) { error in
XCTAssertNil(error)
XCTAssertNotNil(task.error)
XCTAssertEqual((task.error as? URLError)?.code, .badURL)
}
}
func test_taskCopy() {
let url = URL(string: "http://127.0.0.1:\(TestURLSession.serverPort)/Nepal")!
let session = URLSession(configuration: URLSessionConfiguration.default,
delegate: nil,
delegateQueue: nil)
let task = session.dataTask(with: url)
XCTAssert(task.isEqual(task.copy()))
}
// This test is buggy becuase the server could respond before the task is cancelled.
func test_cancelTask() {
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/Peru"
var urlRequest = URLRequest(url: URL(string: urlString)!)
urlRequest.setValue("2.0", forHTTPHeaderField: "X-Pause")
let d = DataTask(with: expectation(description: "GET \(urlString): task cancelation"))
d.cancelExpectation = expectation(description: "GET \(urlString): task canceled")
d.run(with: urlRequest)
d.cancel()
waitForExpectations(timeout: 12)
}
func test_verifyRequestHeaders() {
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 5
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/requestHeaders"
let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
var expect = expectation(description: "POST \(urlString): get request headers")
var req = URLRequest(url: URL(string: urlString)!)
let headers = ["header1": "value1"]
req.httpMethod = "POST"
req.allHTTPHeaderFields = headers
let task = session.dataTask(with: req) { (data, _, error) -> Void in
defer { expect.fulfill() }
XCTAssertNotNil(data)
XCTAssertNil(error as? URLError, "error = \(error as! URLError)")
guard let data = data else { return }
let headers = String(data: data, encoding: .utf8) ?? ""
XCTAssertNotNil(headers.range(of: "header1: value1"))
}
task.resume()
req.allHTTPHeaderFields = nil
waitForExpectations(timeout: 30)
}
// Verify httpAdditionalHeaders from session configuration are added to the request
// and whether it is overriden by Request.allHTTPHeaderFields.
func test_verifyHttpAdditionalHeaders() {
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 5
config.httpAdditionalHeaders = ["header2": "svalue2", "header3": "svalue3", "header4": "svalue4"]
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/requestHeaders"
let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
var expect = expectation(description: "POST \(urlString) with additional headers")
var req = URLRequest(url: URL(string: urlString)!)
let headers = ["header1": "rvalue1", "header2": "rvalue2", "Header4": "rvalue4"]
req.httpMethod = "POST"
req.allHTTPHeaderFields = headers
let task = session.dataTask(with: req) { (data, _, error) -> Void in
defer { expect.fulfill() }
XCTAssertNotNil(data)
XCTAssertNil(error as? URLError, "error = \(error as! URLError)")
guard let data = data else { return }
let headers = String(data: data, encoding: .utf8) ?? ""
XCTAssertNotNil(headers.range(of: "header1: rvalue1"))
XCTAssertNotNil(headers.range(of: "header2: rvalue2"))
XCTAssertNotNil(headers.range(of: "header3: svalue3"))
XCTAssertNotNil(headers.range(of: "Header4: rvalue4"))
XCTAssertNil(headers.range(of: "header4: svalue"))
}
task.resume()
waitForExpectations(timeout: 30)
}
func test_taskTimeout() {
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 5
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/Peru"
let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
var expect = expectation(description: "GET \(urlString): no timeout")
let req = URLRequest(url: URL(string: urlString)!)
let task = session.dataTask(with: req) { (data, _, error) -> Void in
defer { expect.fulfill() }
XCTAssertNil(error as? URLError, "error = \(error as! URLError)")
}
task.resume()
waitForExpectations(timeout: 30)
}
func test_timeoutInterval() {
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 10
let urlString = "http://127.0.0.1:-1/Peru"
let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
var expect = expectation(description: "GET \(urlString): will timeout")
var req = URLRequest(url: URL(string: "http://127.0.0.1:-1/Peru")!)
req.timeoutInterval = 1
let task = session.dataTask(with: req) { (data, _, error) -> Void in
defer { expect.fulfill() }
XCTAssertNotNil(error)
}
task.resume()
waitForExpectations(timeout: 30)
}
func test_httpRedirectionWithCompleteRelativePath() {
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/UnitedStates"
let url = URL(string: urlString)!
let d = HTTPRedirectionDataTask(with: expectation(description: "GET \(urlString): with HTTP redirection"))
d.run(with: url)
waitForExpectations(timeout: 12)
}
func test_httpRedirectionWithInCompleteRelativePath() {
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/UnitedKingdom"
let url = URL(string: urlString)!
let d = HTTPRedirectionDataTask(with: expectation(description: "GET \(urlString): with HTTP redirection"))
d.run(with: url)
waitForExpectations(timeout: 12)
}
func test_httpRedirectionWithDefaultPort() {
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/redirect-with-default-port"
let url = URL(string: urlString)!
let d = HTTPRedirectionDataTask(with: expectation(description: "GET \(urlString): with HTTP redirection"))
d.run(with: url)
waitForExpectations(timeout: 12)
}
// temporarily disabled (https://bugs.swift.org/browse/SR-5751)
func test_httpRedirectionTimeout() {
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/UnitedStates"
var req = URLRequest(url: URL(string: urlString)!)
req.timeoutInterval = 3
let config = URLSessionConfiguration.default
var expect = expectation(description: "GET \(urlString): timeout with redirection ")
let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
let task = session.dataTask(with: req) { data, response, error in
defer { expect.fulfill() }
if let e = error as? URLError {
XCTAssertEqual(e.code, .cannotConnectToHost, "Unexpected error code")
return
} else {
XCTFail("test unexpectedly succeeded (response=\(response.debugDescription))")
}
}
task.resume()
waitForExpectations(timeout: 12)
}
func test_http0_9SimpleResponses() {
for brokenCity in ["Pompeii", "Sodom"] {
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/LandOfTheLostCities/\(brokenCity)"
let url = URL(string: urlString)!
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 8
let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
let expect = expectation(description: "GET \(urlString): simple HTTP/0.9 response")
let task = session.dataTask(with: url) { data, response, error in
XCTAssertNotNil(data)
XCTAssertNotNil(response)
XCTAssertNil(error)
defer { expect.fulfill() }
guard let httpResponse = response as? HTTPURLResponse else {
XCTFail("response (\(response.debugDescription)) invalid")
return
}
XCTAssertEqual(200, httpResponse.statusCode, "HTTP response code is not 200")
}
task.resume()
waitForExpectations(timeout: 12)
}
}
func test_outOfRangeButCorrectlyFormattedHTTPCode() {
let brokenCity = "Kameiros"
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/LandOfTheLostCities/\(brokenCity)"
let url = URL(string: urlString)!
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 8
let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
let expect = expectation(description: "GET \(urlString): out of range HTTP code")
let task = session.dataTask(with: url) { data, response, error in
XCTAssertNotNil(data)
XCTAssertNotNil(response)
XCTAssertNil(error)
defer { expect.fulfill() }
guard let httpResponse = response as? HTTPURLResponse else {
XCTFail("response (\(response.debugDescription)) invalid")
return
}
XCTAssertEqual(999, httpResponse.statusCode, "HTTP response code is not 999")
}
task.resume()
waitForExpectations(timeout: 12)
}
func test_missingContentLengthButStillABody() {
let brokenCity = "Myndus"
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/LandOfTheLostCities/\(brokenCity)"
let url = URL(string: urlString)!
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 8
let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
let expect = expectation(description: "GET \(urlString): missing content length")
let task = session.dataTask(with: url) { data, response, error in
XCTAssertNotNil(data)
XCTAssertNotNil(response)
XCTAssertNil(error)
defer { expect.fulfill() }
guard let httpResponse = response as? HTTPURLResponse else {
XCTFail("response (\(response.debugDescription)) invalid")
return
}
XCTAssertEqual(200, httpResponse.statusCode, "HTTP response code is not 200")
}
task.resume()
waitForExpectations(timeout: 12)
}
func test_illegalHTTPServerResponses() {
for brokenCity in ["Gomorrah", "Dinavar", "Kuhikugu"] {
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/LandOfTheLostCities/\(brokenCity)"
let url = URL(string: urlString)!
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 8
let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
let expect = expectation(description: "GET \(urlString): illegal response")
let task = session.dataTask(with: url) { data, response, error in
XCTAssertNil(data)
XCTAssertNil(response)
XCTAssertNotNil(error)
expect.fulfill()
}
task.resume()
waitForExpectations(timeout: 12)
}
}
func test_dataTaskWithSharedDelegate() {
let sharedDelegate = SharedDelegate()
let urlString0 = "http://127.0.0.1:\(TestURLSession.serverPort)/Nepal"
let session = URLSession(configuration: .default, delegate: sharedDelegate, delegateQueue: nil)
let dataRequest = URLRequest(url: URL(string: urlString0)!)
let dataTask = session.dataTask(with: dataRequest)
sharedDelegate.dataCompletionExpectation = expectation(description: "GET \(urlString0)")
dataTask.resume()
waitForExpectations(timeout: 20)
}
func test_simpleUploadWithDelegate() {
let delegate = HTTPUploadDelegate()
let session = URLSession(configuration: .default, delegate: delegate, delegateQueue: nil)
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/upload"
var request = URLRequest(url: URL(string: urlString)!)
request.httpMethod = "PUT"
delegate.uploadCompletedExpectation = expectation(description: "PUT \(urlString): Upload data")
let fileData = Data(count: 16*1024)
let task = session.uploadTask(with: request, from: fileData)
task.resume()
waitForExpectations(timeout: 20)
}
func test_concurrentRequests() {
// "10 tasks ought to be enough for anybody"
let tasks = 10
let syncQ = dispatchQueueMake("test_dataTaskWithURL.syncQ")
var dataTasks: [DataTask] = []
let g = dispatchGroupMake()
for f in 0..<tasks {
g.enter()
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/Nepal"
let expectation = self.expectation(description: "GET \(urlString) [\(f)]: with a delegate")
globalDispatchQueue.async {
let url = URL(string: urlString)!
let d = DataTask(with: expectation)
d.run(with: url)
syncQ.async {
dataTasks.append(d)
g.leave()
}
}
}
waitForExpectations(timeout: 12)
g.wait()
for d in syncQ.sync(execute: {dataTasks}) {
if !d.error {
XCTAssertEqual(d.capital, "Kathmandu", "test_dataTaskWithURLRequest returned an unexpected result")
}
}
}
func emptyCookieStorage(storage: HTTPCookieStorage?) {
if let storage = storage, let cookies = storage.cookies {
for cookie in cookies {
storage.deleteCookie(cookie)
}
}
}
func test_disableCookiesStorage() {
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 5
config.httpCookieAcceptPolicy = HTTPCookie.AcceptPolicy.never
emptyCookieStorage(storage: config.httpCookieStorage)
XCTAssertEqual(config.httpCookieStorage?.cookies?.count, 0)
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/requestCookies"
let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
var expect = expectation(description: "POST \(urlString)")
var req = URLRequest(url: URL(string: urlString)!)
req.httpMethod = "POST"
let task = session.dataTask(with: req) { (data, response, error) -> Void in
defer { expect.fulfill() }
XCTAssertNotNil(data)
XCTAssertNil(error as? URLError, "error = \(error as! URLError)")
guard let httpResponse = try? XCTUnwrap(response as? HTTPURLResponse) else {
XCTFail("response should be a non-nil HTTPURLResponse")
return
}
XCTAssertNotNil(httpResponse.allHeaderFields["Set-Cookie"])
}
task.resume()
waitForExpectations(timeout: 30)
let cookies = HTTPCookieStorage.shared.cookies
XCTAssertEqual(cookies?.count, 0)
}
func test_cookiesStorage() {
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 5
emptyCookieStorage(storage: config.httpCookieStorage)
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/requestCookies"
let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
var expect = expectation(description: "POST \(urlString)")
var req = URLRequest(url: URL(string: urlString)!)
req.httpMethod = "POST"
let task = session.dataTask(with: req) { (data, response, error) -> Void in
defer { expect.fulfill() }
XCTAssertNotNil(data)
XCTAssertNil(error as? URLError, "error = \(error as! URLError)")
guard let httpResponse = try? XCTUnwrap(response as? HTTPURLResponse) else {
XCTFail("response should be a non-nil HTTPURLResponse")
return
}
XCTAssertNotNil(httpResponse.allHeaderFields["Set-Cookie"])
}
task.resume()
waitForExpectations(timeout: 30)
let cookies = HTTPCookieStorage.shared.cookies
XCTAssertEqual(cookies?.count, 1)
}
func test_redirectionWithSetCookies() {
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 5
emptyCookieStorage(storage: config.httpCookieStorage)
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/redirectToEchoHeaders"
let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
var expect = expectation(description: "POST \(urlString)")
let req = URLRequest(url: URL(string: urlString)!)
let task = session.dataTask(with: req) { (data, _, error) -> Void in
defer { expect.fulfill() }
// Because /redirectToEchoHeaders is a redirection, this is the
// final result of the redirection, not the redirection itself.
guard let data = try? XCTUnwrap(data) else {
XCTFail("data should not be nil")
return
}
XCTAssertNil(error as? URLError, "error = \(error as! URLError)")
let headers = String(data: data, encoding: String.Encoding.utf8) ?? ""
XCTAssertNotNil(headers.range(of: "Cookie: redirect=true"))
}
task.resume()
waitForExpectations(timeout: 30)
}
func test_previouslySetCookiesAreSentInLaterRequests() {
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 5
emptyCookieStorage(storage: config.httpCookieStorage)
let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
let urlString1 = "http://127.0.0.1:\(TestURLSession.serverPort)/requestCookies"
var expect1 = expectation(description: "POST \(urlString1)")
var req1 = URLRequest(url: URL(string: urlString1)!)
req1.httpMethod = "POST"
let urlString2 = "http://127.0.0.1:\(TestURLSession.serverPort)/echoHeaders"
var expect2 = expectation(description: "POST \(urlString2)")
var req2 = URLRequest(url: URL(string: urlString2)!)
req2.httpMethod = "POST"
let task1 = session.dataTask(with: req1) { (data, response, error) -> Void in
defer { expect1.fulfill() }
XCTAssertNotNil(data)
XCTAssertNil(error as? URLError, "error = \(error as! URLError)")
guard let httpResponse = try? XCTUnwrap(response as? HTTPURLResponse) else {
XCTFail("response should be a non-nil HTTPURLResponse")
return
}
XCTAssertNotNil(httpResponse.allHeaderFields["Set-Cookie"])
let task2 = session.dataTask(with: req2) { (data, _, error) -> Void in
defer { expect2.fulfill() }
guard let data = try? XCTUnwrap(data) else {
XCTFail("data should not be nil")
return
}
XCTAssertNil(error as? URLError, "error = \(error as! URLError)")
let headers = String(data: data, encoding: String.Encoding.utf8) ?? ""
XCTAssertNotNil(headers.range(of: "Cookie: fr=anjd&232"))
}
task2.resume()
}
task1.resume()
waitForExpectations(timeout: 30)
}
func test_cookieStorageForEphemeralConfiguration() {
let config = URLSessionConfiguration.ephemeral
config.timeoutIntervalForRequest = 5
emptyCookieStorage(storage: config.httpCookieStorage)
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/requestCookies"
let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
var expect = expectation(description: "POST \(urlString)")
var req = URLRequest(url: URL(string: urlString)!)
req.httpMethod = "POST"
let task = session.dataTask(with: req) { (data, _, error) -> Void in
defer { expect.fulfill() }
XCTAssertNotNil(data)
XCTAssertNil(error as? URLError, "error = \(error as! URLError)")
}
task.resume()
waitForExpectations(timeout: 30)
let cookies = config.httpCookieStorage?.cookies
XCTAssertEqual(cookies?.count, 1)
let config2 = URLSessionConfiguration.ephemeral
let cookies2 = config2.httpCookieStorage?.cookies
XCTAssertEqual(cookies2?.count, 0)
}
func test_setCookieHeadersCanBeIgnored() {
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 5
config.httpShouldSetCookies = false
emptyCookieStorage(storage: config.httpCookieStorage)
let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
let urlString1 = "http://127.0.0.1:\(TestURLSession.serverPort)/requestCookies"
var expect1 = expectation(description: "POST \(urlString1)")
var req1 = URLRequest(url: URL(string: urlString1)!)
req1.httpMethod = "POST"
let urlString2 = "http://127.0.0.1:\(TestURLSession.serverPort)/echoHeaders"
var expect2 = expectation(description: "POST \(urlString2)")
var req2 = URLRequest(url: URL(string: urlString2)!)
req2.httpMethod = "POST"
let task1 = session.dataTask(with: req1) { (data, response, error) -> Void in
defer { expect1.fulfill() }
XCTAssertNotNil(data)
XCTAssertNil(error as? URLError, "error = \(error as! URLError)")
guard let httpResponse = try? XCTUnwrap(response as? HTTPURLResponse) else {
XCTFail("response should be a non-nil HTTPURLResponse")
return
}
XCTAssertNotNil(httpResponse.allHeaderFields["Set-Cookie"])
let task2 = session.dataTask(with: req2) { (data, _, error) -> Void in
defer { expect2.fulfill() }
guard let data = try? XCTUnwrap(data) else {
XCTFail("data should not be nil")
return
}
XCTAssertNil(error as? URLError, "error = \(error as! URLError)")
let headers = String(data: data, encoding: String.Encoding.utf8) ?? ""
XCTAssertNil(headers.range(of: "Cookie: fr=anjd&232"))
}
task2.resume()
}
task1.resume()
waitForExpectations(timeout: 30)
}
// Validate that the properties are correctly set
func test_initURLSessionConfiguration() {
let config = URLSessionConfiguration.default
config.requestCachePolicy = .useProtocolCachePolicy
config.timeoutIntervalForRequest = 30
config.timeoutIntervalForResource = 604800
config.networkServiceType = .default
config.allowsCellularAccess = false
config.isDiscretionary = true
config.httpShouldUsePipelining = true
config.httpShouldSetCookies = true
config.httpCookieAcceptPolicy = .always
config.httpMaximumConnectionsPerHost = 2
config.httpCookieStorage = HTTPCookieStorage.shared
config.urlCredentialStorage = nil
config.urlCache = nil
config.shouldUseExtendedBackgroundIdleMode = true
XCTAssertEqual(config.requestCachePolicy, NSURLRequest.CachePolicy.useProtocolCachePolicy)
XCTAssertEqual(config.timeoutIntervalForRequest, 30)
XCTAssertEqual(config.timeoutIntervalForResource, 604800)
XCTAssertEqual(config.networkServiceType, NSURLRequest.NetworkServiceType.default)
XCTAssertEqual(config.allowsCellularAccess, false)
XCTAssertEqual(config.isDiscretionary, true)
XCTAssertEqual(config.httpShouldUsePipelining, true)
XCTAssertEqual(config.httpShouldSetCookies, true)
XCTAssertEqual(config.httpCookieAcceptPolicy, HTTPCookie.AcceptPolicy.always)
XCTAssertEqual(config.httpMaximumConnectionsPerHost, 2)
XCTAssertEqual(config.httpCookieStorage, HTTPCookieStorage.shared)
XCTAssertEqual(config.urlCredentialStorage, nil)
XCTAssertEqual(config.urlCache, nil)
XCTAssertEqual(config.shouldUseExtendedBackgroundIdleMode, true)
}
func test_basicAuthRequest() {
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/auth/basic"
let url = URL(string: urlString)!
let d = DataTask(with: expectation(description: "GET \(urlString): with a delegate"))
d.run(with: url)
waitForExpectations(timeout: 60)
}
/* Test for SR-8970 to verify that content-type header is not added to post with empty body */
func test_postWithEmptyBody() {
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 5
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/emptyPost"
let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
var expect = expectation(description: "POST \(urlString): post with empty body")
var req = URLRequest(url: URL(string: urlString)!)
req.httpMethod = "POST"
let task = session.dataTask(with: req) { (_, response, error) -> Void in
defer { expect.fulfill() }
XCTAssertNil(error as? URLError, "error = \(error as! URLError)")
guard let httpresponse = response as? HTTPURLResponse else { fatalError() }
XCTAssertEqual(200, httpresponse.statusCode, "HTTP response code is not 200")
}
task.resume()
waitForExpectations(timeout: 30)
}
func test_basicAuthWithUnauthorizedHeader() {
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/unauthorized"
let url = URL(string: urlString)!
let expect = expectation(description: "GET \(urlString): with a completion handler")
let session = URLSession(configuration: URLSessionConfiguration.default)
let task = session.dataTask(with: url) { _, response, error in
defer { expect.fulfill() }
XCTAssertNotNil(response)
XCTAssertNil(error)
}
task.resume()
waitForExpectations(timeout: 12, handler: nil)
}
func test_checkErrorTypeAfterInvalidateAndCancel() throws {
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/country.txt"
let url = try XCTUnwrap(URL(string: urlString))
var urlRequest = URLRequest(url: url)
urlRequest.addValue("5", forHTTPHeaderField: "X-Pause")
let expect = expectation(description: "Check error code of tasks after invalidateAndCancel")
let delegate = SessionDelegate()
let session = URLSession(configuration: .default, delegate: delegate, delegateQueue: nil)
let task = session.dataTask(with: urlRequest) { (_, _, error) in
XCTAssertNotNil(error as? URLError)
if let urlError = error as? URLError {
XCTAssertEqual(urlError._nsError.code, NSURLErrorCancelled)
}
expect.fulfill()
}
task.resume()
session.invalidateAndCancel()
waitForExpectations(timeout: 5)
}
func test_taskCountAfterInvalidateAndCancel() throws {
let expect = expectation(description: "Check task count after invalidateAndCancel")
let session = URLSession(configuration: .default)
var request = URLRequest(url: try XCTUnwrap(URL(string: "http://127.0.0.1:\(TestURLSession.serverPort)/country.txt")))
request.addValue("5", forHTTPHeaderField: "X-Pause")
let task1 = session.dataTask(with: request)
request.url = try XCTUnwrap(URL(string: "http://127.0.0.1:\(TestURLSession.serverPort)/requestHeaders"))
let task2 = session.dataTask(with: request)
request.url = try XCTUnwrap(URL(string: "http://127.0.0.1:\(TestURLSession.serverPort)/emptyPost"))
let task3 = session.dataTask(with: request)
task1.resume()
task2.resume()
session.invalidateAndCancel()
session.getAllTasks { tasksBeforeResume in
XCTAssertEqual(tasksBeforeResume.count, 0)
// Resume a task after invalidating a session shouldn't change the task's status
task3.resume()
session.getAllTasks { tasksAfterResume in
XCTAssertEqual(tasksAfterResume.count, 0)
expect.fulfill()
}
}
waitForExpectations(timeout: 5)
}
func test_sessionDelegateAfterInvalidateAndCancel() {
let delegate = SessionDelegate()
let session = URLSession(configuration: .default, delegate: delegate, delegateQueue: nil)
session.invalidateAndCancel()
Thread.sleep(forTimeInterval: 2)
XCTAssertNil(session.delegate)
}
func test_getAllTasks() throws {
let expect = expectation(description: "Tasks URLSession.getAllTasks")
let session = URLSession(configuration: .default)
var request = URLRequest(url: try XCTUnwrap(URL(string: "http://127.0.0.1:\(TestURLSession.serverPort)/country.txt")))
request.addValue("5", forHTTPHeaderField: "X-Pause")
let dataTask1 = session.dataTask(with: request)
request.url = try XCTUnwrap(URL(string: "http://127.0.0.1:\(TestURLSession.serverPort)/requestHeaders"))
let dataTask2 = session.dataTask(with: request)
request.url = try XCTUnwrap(URL(string: "http://127.0.0.1:\(TestURLSession.serverPort)/emptyPost"))
let dataTask3 = session.dataTask(with: request)
session.getAllTasks { (tasksBeforeResume) in
XCTAssertEqual(tasksBeforeResume.count, 0)
dataTask1.cancel()
dataTask2.resume()
dataTask2.suspend()
// dataTask3 is suspended even before it was resumed, so the next call to `getAllTasks` should not include this tasks
dataTask3.suspend()
session.getAllTasks { (tasksAfterCancel) in
// tasksAfterCancel should only contain dataTask2
XCTAssertEqual(tasksAfterCancel.count, 1)
// A task will in be in suspended state when it was created.
// Given that, dataTask3 was suspended once again earlier above, so it should receive `resume()` twice in order to be executed
// Calling `getAllTasks` next time should not include dataTask3
dataTask3.resume()
session.getAllTasks { (tasksAfterFirstResume) in
// tasksAfterFirstResume should only contain dataTask2
XCTAssertEqual(tasksAfterFirstResume.count, 1)
// Now dataTask3 received `resume()` twice, this time `getAllTasks` should include
dataTask3.resume()
session.getAllTasks { (tasksAfterSecondResume) in
// tasksAfterSecondResume should contain dataTask2 and dataTask2 this time
XCTAssertEqual(tasksAfterSecondResume.count, 2)
expect.fulfill()
}
}
}
}
waitForExpectations(timeout: 20)
}
func test_getTasksWithCompletion() throws {
let expect = expectation(description: "Test URLSession.getTasksWithCompletion")
let session = URLSession(configuration: .default)
var request = URLRequest(url: try XCTUnwrap(URL(string: "http://127.0.0.1:\(TestURLSession.serverPort)/country.txt")))
request.addValue("5", forHTTPHeaderField: "X-Pause")
let dataTask1 = session.dataTask(with: request)
request.url = try XCTUnwrap(URL(string: "http://127.0.0.1:\(TestURLSession.serverPort)/requestHeaders"))
let dataTask2 = session.dataTask(with: request)
request.url = try XCTUnwrap(URL(string: "http://127.0.0.1:\(TestURLSession.serverPort)/emptyPost"))
let dataTask3 = session.dataTask(with: request)
request.url = try XCTUnwrap(URL(string: "http://127.0.0.1:\(TestURLSession.serverPort)/upload"))
let uploadTask1 = session.uploadTask(with: request, from: Data())
request.url = try XCTUnwrap(URL(string: "http://127.0.0.1:\(TestURLSession.serverPort)/echo"))
let uploadTask2 = session.uploadTask(with: request, from: Data())
request.url = try XCTUnwrap(URL(string: "http://127.0.0.1:\(TestURLSession.serverPort)/DTDs/PropertyList-1.0.dtd"))
let downloadTask1 = session.downloadTask(with: request)
session.getTasksWithCompletionHandler { (dataTasksBeforeCancel, uploadTasksBeforeCancel, downloadTasksBeforeCancel) in
XCTAssertEqual(dataTasksBeforeCancel.count, 0)
XCTAssertEqual(uploadTasksBeforeCancel.count, 0)
XCTAssertEqual(downloadTasksBeforeCancel.count, 0)
dataTask1.cancel()
dataTask2.resume()
// dataTask3 is resumed and suspended, so this task should be a part of `getTasksWithCompletionHandler` response
dataTask3.resume()
dataTask3.suspend()
// uploadTask1 suspended even before it was resumed, so this task shouldn't be a part of `getTasksWithCompletionHandler` response
uploadTask1.suspend()
uploadTask2.resume()
downloadTask1.cancel()
session.getTasksWithCompletionHandler{ (dataTasksAfterCancel, uploadTasksAfterCancel, downloadTasksAfterCancel) in
XCTAssertEqual(dataTasksAfterCancel.count, 2)
XCTAssertEqual(uploadTasksAfterCancel.count, 1)
XCTAssertEqual(downloadTasksAfterCancel.count, 0)
expect.fulfill()
}
}
waitForExpectations(timeout: 20)
}
func test_noDoubleCallbackWhenCancellingAndProtocolFailsFast() throws {
let urlString = "failfast://bogus"
var callbackCount = 0
let callback1 = expectation(description: "Callback call #1")
let callback2 = expectation(description: "Callback call #2")
callback2.isInverted = true
let delegate = SessionDelegate()
let url = try XCTUnwrap(URL(string: urlString))
let configuration = URLSessionConfiguration.default
configuration.protocolClasses = [FailFastProtocol.self]
let session = URLSession(configuration: configuration, delegate: delegate, delegateQueue: nil)
let task = session.dataTask(with: url) { (_, _, error) in
callbackCount += 1
XCTAssertNotNil(error)
if let urlError = error as? URLError {
XCTAssertNotEqual(urlError._nsError.code, NSURLErrorCancelled)
}
if callbackCount == 1 {
callback1.fulfill()
} else {
callback2.fulfill()
}
}
task.resume()
session.invalidateAndCancel()
waitForExpectations(timeout: 1)
}
func test_cancelledTasksCannotBeResumed() throws {
let url = try XCTUnwrap(URL(string: "http://127.0.0.1:\(TestURLSession.serverPort)/Nepal"))
let session = URLSession(configuration: .default, delegate: nil, delegateQueue: nil)
let task = session.dataTask(with: url)
task.cancel() // should set .cancelling and eventually .completed
task.resume() // should not change the task to .running
let e = expectation(description: "getAllTasks callback called")
session.getAllTasks { tasks in
XCTAssertEqual(tasks.count, 0)
e.fulfill()
}
waitForExpectations(timeout: 1)
}
func test_invalidResumeDataForDownloadTask() {
let done = expectation(description: "Invalid resume data for download task (with completion block)")
URLSession.shared.downloadTask(withResumeData: Data()) { (url, response, error) in
XCTAssertNil(url)
XCTAssertNil(response)
XCTAssert(error is URLError)
XCTAssertEqual((error as? URLError)?.errorCode, URLError.unsupportedURL.rawValue)
done.fulfill()
}.resume()
waitForExpectations(timeout: 20)
let d = DownloadTask(testCase: self, description: "Invalid resume data for download task")
d.run { (session) -> DownloadTask.Configuration in
return DownloadTask.Configuration(task: session.downloadTask(withResumeData: Data()),
errorExpectation:
{ (error) in
XCTAssert(error is URLError)
XCTAssertEqual((error as? URLError)?.errorCode, URLError.unsupportedURL.rawValue)
})
}
waitForExpectations(timeout: 20)
}
func test_simpleUploadWithDelegateProvidingInputStream() throws {
let delegate = HTTPUploadDelegate()
let session = URLSession(configuration: .default, delegate: delegate, delegateQueue: nil)
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/upload"
var request = URLRequest(url: URL(string: urlString)!)
request.httpMethod = "PUT"
delegate.uploadCompletedExpectation = expectation(description: "PUT \(urlString): Upload data")
let fileData = Data(count: 16*1024)
let stream = InputStream(data: fileData)
stream.open()
delegate.streamToProvideOnRequest = stream
let task = session.uploadTask(withStreamedRequest: request)
task.resume()
waitForExpectations(timeout: 20)
}
static var allTests: [(String, (TestURLSession) -> () throws -> Void)] {
return [
("test_dataTaskWithURL", test_dataTaskWithURL),
("test_dataTaskWithURLRequest", test_dataTaskWithURLRequest),
("test_dataTaskWithURLCompletionHandler", test_dataTaskWithURLCompletionHandler),
("test_dataTaskWithURLRequestCompletionHandler", test_dataTaskWithURLRequestCompletionHandler),
// ("test_dataTaskWithHttpInputStream", test_dataTaskWithHttpInputStream), - Flaky test
("test_gzippedDataTask", test_gzippedDataTask),
("test_downloadTaskWithURL", test_downloadTaskWithURL),
("test_downloadTaskWithURLRequest", test_downloadTaskWithURLRequest),
("test_downloadTaskWithRequestAndHandler", test_downloadTaskWithRequestAndHandler),
("test_downloadTaskWithURLAndHandler", test_downloadTaskWithURLAndHandler),
("test_gzippedDownloadTask", test_gzippedDownloadTask),
("test_finishTaskAndInvalidate", test_finishTasksAndInvalidate),
("test_taskError", test_taskError),
("test_taskCopy", test_taskCopy),
("test_cancelTask", test_cancelTask),
("test_taskTimeout", test_taskTimeout),
("test_verifyRequestHeaders", test_verifyRequestHeaders),
("test_verifyHttpAdditionalHeaders", test_verifyHttpAdditionalHeaders),
("test_timeoutInterval", test_timeoutInterval),
("test_httpRedirectionWithCompleteRelativePath", test_httpRedirectionWithCompleteRelativePath),
("test_httpRedirectionWithInCompleteRelativePath", test_httpRedirectionWithInCompleteRelativePath),
("test_httpRedirectionWithDefaultPort", test_httpRedirectionWithDefaultPort),
("test_httpRedirectionTimeout", test_httpRedirectionTimeout),
("test_http0_9SimpleResponses", test_http0_9SimpleResponses),
("test_outOfRangeButCorrectlyFormattedHTTPCode", test_outOfRangeButCorrectlyFormattedHTTPCode),
("test_missingContentLengthButStillABody", test_missingContentLengthButStillABody),
("test_illegalHTTPServerResponses", test_illegalHTTPServerResponses),
("test_dataTaskWithSharedDelegate", test_dataTaskWithSharedDelegate),
// ("test_simpleUploadWithDelegate", test_simpleUploadWithDelegate), - Server needs modification
("test_concurrentRequests", test_concurrentRequests),
("test_disableCookiesStorage", test_disableCookiesStorage),
("test_cookiesStorage", test_cookiesStorage),
("test_cookieStorageForEphemeralConfiguration", test_cookieStorageForEphemeralConfiguration),
("test_previouslySetCookiesAreSentInLaterRequests", test_previouslySetCookiesAreSentInLaterRequests),
("test_setCookieHeadersCanBeIgnored", test_setCookieHeadersCanBeIgnored),
("test_initURLSessionConfiguration", test_initURLSessionConfiguration),
("test_basicAuthRequest", test_basicAuthRequest),
("test_redirectionWithSetCookies", test_redirectionWithSetCookies),
("test_postWithEmptyBody", test_postWithEmptyBody),
("test_basicAuthWithUnauthorizedHeader", test_basicAuthWithUnauthorizedHeader),
("test_checkErrorTypeAfterInvalidateAndCancel", test_checkErrorTypeAfterInvalidateAndCancel),
("test_taskCountAfterInvalidateAndCancel", test_taskCountAfterInvalidateAndCancel),
("test_sessionDelegateAfterInvalidateAndCancel", test_sessionDelegateAfterInvalidateAndCancel),
("test_getAllTasks", test_getAllTasks),
("test_getTasksWithCompletion", test_getTasksWithCompletion),
/* ⚠️ */ ("test_invalidResumeDataForDownloadTask",
/* ⚠️ */ testExpectedToFail(test_invalidResumeDataForDownloadTask, "This test crashes nondeterministically: https://bugs.swift.org/browse/SR-11353")),
/* ⚠️ */ ("test_simpleUploadWithDelegateProvidingInputStream",
/* ⚠️ */ testExpectedToFail(test_simpleUploadWithDelegateProvidingInputStream, "This test times out frequently: https://bugs.swift.org/browse/SR-11343")),
/* ⚠️ */ ("test_noDoubleCallbackWhenCancellingAndProtocolFailsFast",
/* ⚠️ */ testExpectedToFail(test_noDoubleCallbackWhenCancellingAndProtocolFailsFast, "This test crashes nondeterministically: https://bugs.swift.org/browse/SR-11310")),
("test_cancelledTasksCannotBeResumed", test_cancelledTasksCannotBeResumed),
]
}
}
class SharedDelegate: NSObject {
var dataCompletionExpectation: XCTestExpectation!
}
extension SharedDelegate: URLSessionDataDelegate {
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
dataCompletionExpectation.fulfill()
}
}
extension SharedDelegate: URLSessionDownloadDelegate {
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
}
}
class SessionDelegate: NSObject, URLSessionDelegate {
let invalidateExpectation: XCTestExpectation?
override init() {
invalidateExpectation = nil
super.init()
}
init(invalidateExpectation: XCTestExpectation) {
self.invalidateExpectation = invalidateExpectation
}
func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {
invalidateExpectation?.fulfill()
}
}
class DataTask : NSObject {
let syncQ = dispatchQueueMake("org.swift.TestFoundation.TestURLSession.DataTask.syncQ")
let dataTaskExpectation: XCTestExpectation!
let protocols: [AnyClass]?
/* all the following var _XYZ need to be synchronized on syncQ.
We can't just assert that we're on main thread here as we're modified in the URLSessionDataDelegate extension
for DataTask
*/
var _capital = "unknown"
var capital: String {
get {
return self.syncQ.sync { self._capital }
}
set {
self.syncQ.sync { self._capital = newValue }
}
}
var _session: URLSession! = nil
var session: URLSession! {
get {
return self.syncQ.sync { self._session }
}
set {
self.syncQ.sync { self._session = newValue }
}
}
var _task: URLSessionDataTask! = nil
var task: URLSessionDataTask! {
get {
return self.syncQ.sync { self._task }
}
set {
self.syncQ.sync { self._task = newValue }
}
}
var _cancelExpectation: XCTestExpectation?
var cancelExpectation: XCTestExpectation? {
get {
return self.syncQ.sync { self._cancelExpectation }
}
set {
self.syncQ.sync { self._cancelExpectation = newValue }
}
}
var _responseReceivedExpectation: XCTestExpectation?
var responseReceivedExpectation: XCTestExpectation? {
get {
return self.syncQ.sync { self._responseReceivedExpectation }
}
set {
self.syncQ.sync { self._responseReceivedExpectation = newValue }
}
}
private var _error = false
public var error: Bool {
get {
return self.syncQ.sync { self._error }
}
set {
self.syncQ.sync { self._error = newValue }
}
}
init(with expectation: XCTestExpectation, protocolClasses: [AnyClass]? = nil) {
dataTaskExpectation = expectation
protocols = protocolClasses
}
func run(with request: URLRequest) {
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 8
if let customProtocols = protocols {
config.protocolClasses = customProtocols
}
session = URLSession(configuration: config, delegate: self, delegateQueue: nil)
task = session.dataTask(with: request)
task.resume()
}
func run(with url: URL) {
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 8
if let customProtocols = protocols {
config.protocolClasses = customProtocols
}
session = URLSession(configuration: config, delegate: self, delegateQueue: nil)
task = session.dataTask(with: url)
task.resume()
}
func cancel() {
task.cancel()
}
}
extension DataTask : URLSessionDataDelegate {
public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
capital = String(data: data, encoding: .utf8)!
}
public func urlSession(_ session: URLSession,
dataTask: URLSessionDataTask,
didReceive response: URLResponse,
completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
guard responseReceivedExpectation != nil else { return }
responseReceivedExpectation!.fulfill()
}
}
extension DataTask : URLSessionTaskDelegate {
public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
dataTaskExpectation.fulfill()
guard (error as? URLError) != nil else { return }
if let cancellation = cancelExpectation {
cancellation.fulfill()
}
self.error = true
}
public func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge:
URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition,
URLCredential?) -> Void) {
completionHandler(.useCredential, URLCredential(user: "user", password: "passwd", persistence: .none))
}
}
class DownloadTask : NSObject {
var totalBytesWritten: Int64 = 0
var didDownloadExpectation: XCTestExpectation?
let didCompleteExpectation: XCTestExpectation
var session: URLSession! = nil
var task: URLSessionDownloadTask! = nil
var errorExpectation: ((Error) -> Void)?
weak var testCase: XCTestCase?
var expectationsDescription: String
init(testCase: XCTestCase, description: String) {
self.expectationsDescription = description
self.testCase = testCase
self.didCompleteExpectation = testCase.expectation(description: "Did complete \(description)")
}
private func makeDownloadExpectation() {
guard didDownloadExpectation == nil else { return }
self.didDownloadExpectation = testCase!.expectation(description: "Did finish download: \(description)")
self.testCase = nil // No need for it any more here.
}
func run(with url: URL) {
makeDownloadExpectation()
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 8
session = URLSession(configuration: config, delegate: self, delegateQueue: nil)
task = session.downloadTask(with: url)
task.resume()
}
func run(with urlRequest: URLRequest) {
makeDownloadExpectation()
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 8
session = URLSession(configuration: config, delegate: self, delegateQueue: nil)
task = session.downloadTask(with: urlRequest)
task.resume()
}
struct Configuration {
var task: URLSessionDownloadTask
var errorExpectation: ((Error) -> Void)?
}
func run(configuration: (URLSession) -> Configuration) {
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 8
session = URLSession(configuration: config, delegate: self, delegateQueue: nil)
let taskConfiguration = configuration(session)
task = taskConfiguration.task
errorExpectation = taskConfiguration.errorExpectation
if errorExpectation == nil {
makeDownloadExpectation()
}
task.resume()
}
}
extension DownloadTask : URLSessionDownloadDelegate {
public func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64,
totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) -> Void {
self.totalBytesWritten = totalBytesWritten
}
public func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
defer { didDownloadExpectation?.fulfill() }
guard self.errorExpectation == nil else {
XCTFail("Expected an error, but got …didFinishDownloadingTo… from download task \(downloadTask) (at \(location))")
return
}
do {
let attr = try FileManager.default.attributesOfItem(atPath: location.path)
XCTAssertEqual((attr[.size]! as? NSNumber)!.int64Value, totalBytesWritten, "Size of downloaded file not equal to total bytes downloaded")
} catch {
XCTFail("Unable to calculate size of the downloaded file")
}
}
}
extension DownloadTask : URLSessionTaskDelegate {
public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
defer { didCompleteExpectation.fulfill() }
if let errorExpectation = self.errorExpectation {
if let error = error {
errorExpectation(error)
} else {
XCTFail("Expected an error, but got a completion without error from download task \(task)")
}
} else {
guard let e = error as? URLError else { return }
XCTAssertEqual(e.code, .timedOut, "Unexpected error code")
}
}
}
class FailFastProtocol: URLProtocol {
enum Error: Swift.Error {
case fastError
}
override class func canInit(with request: URLRequest) -> Bool {
return request.url?.scheme == "failfast"
}
override class func canInit(with task: URLSessionTask) -> Bool {
guard let request = task.currentRequest else { return false }
return canInit(with: request)
}
override func startLoading() {
client?.urlProtocol(self, didFailWithError: Error.fastError)
}
override func stopLoading() {
// Intentionally blank
}
}
class HTTPRedirectionDataTask : NSObject {
let dataTaskExpectation: XCTestExpectation!
var session: URLSession! = nil
var task: URLSessionDataTask! = nil
var cancelExpectation: XCTestExpectation?
public var error = false
init(with expectation: XCTestExpectation) {
dataTaskExpectation = expectation
}
func run(with request: URLRequest) {
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 8
session = URLSession(configuration: config, delegate: self, delegateQueue: nil)
task = session.dataTask(with: request)
task.resume()
}
func run(with url: URL) {
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 8
session = URLSession(configuration: config, delegate: self, delegateQueue: nil)
task = session.dataTask(with: url)
task.resume()
}
func cancel() {
task.cancel()
}
}
extension HTTPRedirectionDataTask : URLSessionDataDelegate {
public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
guard let httpresponse = response as? HTTPURLResponse else { fatalError() }
XCTAssertNotNil(response)
XCTAssertEqual(200, httpresponse.statusCode, "HTTP response code is not 200")
}
}
extension HTTPRedirectionDataTask : URLSessionTaskDelegate {
public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
dataTaskExpectation.fulfill()
guard (error as? URLError) != nil else { return }
if let cancellation = cancelExpectation {
cancellation.fulfill()
}
self.error = true
}
public func urlSession(_ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: @escaping (URLRequest?) -> Void) {
XCTAssertNotNil(response)
XCTAssertEqual(302, response.statusCode, "HTTP response code is not 302")
if let url = response.url, url.path.hasSuffix("/redirect-with-default-port") {
XCTAssertEqual(request.url?.absoluteString, "http://127.0.0.1/redirected-with-default-port")
// Don't follow the redirect as the test server is not running on port 80
return
}
completionHandler(request)
}
}
class HTTPUploadDelegate: NSObject {
var uploadCompletedExpectation: XCTestExpectation!
var streamToProvideOnRequest: InputStream?
var totalBytesSent: Int64 = 0
}
extension HTTPUploadDelegate: URLSessionTaskDelegate {
func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
self.totalBytesSent = totalBytesSent
}
func urlSession(_ session: URLSession, task: URLSessionTask, needNewBodyStream completionHandler: @escaping (InputStream?) -> Void) {
if streamToProvideOnRequest == nil {
XCTFail("This shouldn't have been invoked -- no stream was set.")
}
completionHandler(self.streamToProvideOnRequest)
}
}
extension HTTPUploadDelegate: URLSessionDataDelegate {
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
XCTAssertEqual(self.totalBytesSent, 16*1024)
uploadCompletedExpectation.fulfill()
}
}