@@ -115,7 +115,74 @@ extension TaskDelegate: NSURLSessionDataDelegate {
115
115
}
116
116
}
117
117
118
+ //MARK: - Header Field Helpers
118
119
120
+ private struct HeaderFields {
121
+ let fields : [ ( name: String , value: String ) ]
122
+ }
123
+ extension HeaderFields {
124
+ func value( forField name: String ) -> String ? {
125
+ let lowercasedName = name. lowercased ( )
126
+ return fields. index ( where: { lowercasedName == $0. name. lowercased ( ) } ) . map { fields [ $0] . value }
127
+ }
128
+ var count : Int { return fields. count }
129
+ }
130
+ extension HeaderFields {
131
+ func hasValue( forField name: String ) -> Bool {
132
+ return value ( forField: name) != nil
133
+ }
134
+ }
135
+
136
+
137
+ /// Tests that the header fields with the given names exist.
138
+ private func AssertHeaderFieldsExist( @autoclosure expression1: ( ) throws -> HeaderFields , @autoclosure _ expression2: ( ) throws -> [ String ] , @autoclosure _ message: ( ) -> String = " " , file: StaticString = #file, line: UInt = #line) {
139
+ let m = message ( )
140
+ do {
141
+ let f = try expression1 ( )
142
+ try expression2 ( ) . forEach {
143
+ if !f. hasValue ( forField: $0) {
144
+ XCTFail ( file: file, line: line, " Field ' \( $0) ' is missing. " + m)
145
+ }
146
+ }
147
+ } catch let e { XCTFail ( file: file, line: line, " \( m) -- \( e) " ) }
148
+ }
149
+ /// Tests that header fields only exist for the given names and nothing else.
150
+ private func AssertHeaderFieldNamesEqual( @autoclosure expression1: ( ) throws -> HeaderFields , @autoclosure _ expression2: ( ) throws -> [ String ] , @autoclosure _ message: ( ) -> String = " " , file: StaticString = #file, line: UInt = #line) {
151
+ let m = message ( )
152
+ do {
153
+ let fields = try expression1 ( )
154
+ var extraNames = fields. fields. map { $0. name }
155
+ try expression2 ( ) . forEach { field in
156
+ if let idx = extraNames. index ( where: { field. lowercased ( ) == $0. lowercased ( ) } ) {
157
+ extraNames. remove ( at: idx)
158
+ } else {
159
+ XCTFail ( file: file, line: line, " Header field ' \( field) ' is missing. " + m)
160
+ }
161
+ }
162
+ extraNames. forEach {
163
+ let value = fields. value ( forField: $0) ?? " "
164
+ XCTFail ( file: file, line: line, " Extranous header field ' \( $0) : \( value) '. " + m)
165
+ }
166
+
167
+ } catch let e { XCTFail ( file: file, line: line, " \( m) -- \( e) " ) }
168
+ }
169
+ /// Tests the the header fields match those in the dictionary, *and* that all
170
+ /// fields in the dictionary exist. But the headers might have more values.
171
+ private func AssertHeaderFieldsEqual( @autoclosure expression1: ( ) throws -> HeaderFields , @autoclosure _ expression2: ( ) throws -> [ String : String ] , @autoclosure _ message: ( ) -> String = " " , file: StaticString = #file, line: UInt = #line) {
172
+ let m = message ( )
173
+ do {
174
+ let f = try expression1 ( )
175
+ try expression2 ( ) . forEach { field in
176
+ if let v = f. value ( forField: field. 0 ) {
177
+ XCTAssertEqual ( v, field. 1 , file: file, line: line, " Header field ' \( field. 0 ) '. \( m) " )
178
+ } else {
179
+ XCTFail ( file: file, line: line, " Header field ' \( field. 0 ) ' is missing. \( m) " )
180
+ }
181
+ }
182
+ } catch let e { XCTFail ( file: file, line: line, " \( m) -- \( e) " ) }
183
+ }
184
+
185
+ //MARK: - Tests
119
186
120
187
class TestNSURLSession : XCTestCase {
121
188
static var allTests : [ ( String , TestNSURLSession -> ( ) throws -> Void ) ] {
@@ -130,6 +197,8 @@ class TestNSURLSession : XCTestCase {
130
197
( " test_functional_POST_fromData " , test_functional_POST_fromData) ,
131
198
( " test_functional_POST_fromFile " , test_functional_POST_fromFile) ,
132
199
( " test_functional_POST_fromFileThatDoesNotExist " , test_functional_POST_fromFileThatDoesNotExist) ,
200
+
201
+ ( " test_functional_defaultHeaders " , test_functional_defaultHeaders) ,
133
202
]
134
203
}
135
204
@@ -146,8 +215,8 @@ class TestNSURLSession : XCTestCase {
146
215
let task = sut. dataTaskWithURL ( NSURL ( string: " http://swift.org/ " ) !)
147
216
XCTAssertEqual ( task. state, NSURLSessionTaskState . Suspended)
148
217
}
149
-
150
- func createServer( file: StaticString = #file, line: UInt = #line, handler: ( HTTPRequest ) -> ( HTTPResponse ) ) -> ( SocketServer , NSURL ) ? {
218
+ /// Create an HTTP server and return the server and its base URL.
219
+ private func createServer( file file : StaticString = #file, line: UInt = #line, handler: ( HTTPRequest ) -> HTTPResponse ) -> ( SocketServer , NSURL ) ? {
151
220
let q = dispatch_queue_create ( " test_2 " , DISPATCH_QUEUE_SERIAL)
152
221
let server : SocketServer
153
222
do {
@@ -167,6 +236,30 @@ class TestNSURLSession : XCTestCase {
167
236
c. path = " / "
168
237
return ( server, c. URL!)
169
238
}
239
+ /// Creates an HTTP server with a block that gets all headers.
240
+ private func createExtractHeaderServer( file file: StaticString = #file, line: UInt = #line, headerHandler: ( HeaderFields ) -> ( ) ) -> ( SocketServer , NSURL ) ? {
241
+ return createServer ( file: file, line: line) { request in
242
+ var headerFields : [ ( name: String , value: String ) ] = [ ]
243
+ request. forEachHeaderField {
244
+ headerFields. append ( ( name: $0, value: $1) )
245
+ }
246
+ headerHandler ( HeaderFields ( fields: headerFields) )
247
+ return HTTPResponse ( statusCode: 200 , additionalHeaderFields: [ ] , body: " " )
248
+ }
249
+ }
250
+ /// Helper function that creates a session with a delegate and calls the
251
+ /// given closure with all three wrapped in a `withExtendedLifetime()` call.
252
+ private func with( server server: SocketServer , @noescape testClosure: ( NSURLSession , TaskDelegate ) -> ( ) ) {
253
+ withExtendedLifetime ( server) {
254
+ let delegate = TaskDelegate ( )
255
+ let session = NSURLSession ( configuration: NSURLSessionConfiguration . defaultSessionConfiguration ( ) , delegate: delegate, delegateQueue: queue)
256
+ withExtendedLifetime ( delegate) {
257
+ withExtendedLifetime ( session) {
258
+ testClosure ( session, delegate)
259
+ }
260
+ }
261
+ }
262
+ }
170
263
}
171
264
172
265
/// Create test data with a well-defined pattern.
@@ -545,9 +638,37 @@ extension TestNSURLSession {
545
638
}
546
639
}
547
640
641
+
548
642
//TODO: Uploading data with completion handler
549
643
// public func uploadTaskWithRequest(request: NSURLRequest, fromFile fileURL: NSURL, completionHandler: (NSData?, NSURLResponse?, NSError?) -> Void) -> NSURLSessionUploadTask
550
644
// public func uploadTaskWithRequest(request: NSURLRequest, fromData bodyData: NSData?, completionHandler: (NSData?, NSURLResponse?, NSError?) -> Void) -> NSURLSessionUploadTask
645
+
646
+ func test_functional_defaultHeaders( ) {
647
+ var baseURL : NSURL ?
648
+ guard let ( server, _baseURL) = createExtractHeaderServer ( headerHandler: { headers in
649
+ let port = Int ( baseURL? . port ?? 0 )
650
+ let expected = [ " Host " : " \( baseURL? . host ?? " " ) : \( String ( port) ) " ,
651
+ " Accept " : " */* " ,
652
+ " Accept-Language " : " en-us " ,
653
+ " Accept-Encoding " : " gzip, deflate " ,
654
+ ]
655
+ AssertHeaderFieldsEqual ( headers, expected)
656
+ AssertHeaderFieldNamesEqual ( headers, [ " Host " , " Accept " , " Accept-Language " , " Accept-Encoding " , " Connection " , " User-Agent " ] )
657
+ } ) else { XCTFail ( ) ; return }
658
+ baseURL = _baseURL
659
+ with ( server: server) { ( session, delegate) in
660
+ let URL = NSURL ( string: " /bar " , relativeToURL: baseURL) !
661
+
662
+ let completionExpectation = expectationWithDescription ( " Task did complete " )
663
+ let task = session. dataTaskWithURL ( URL, completionHandler: { ( _, _, _) in
664
+ completionExpectation. fulfill ( )
665
+ } )
666
+
667
+ task. resume ( )
668
+ waitForExpectationsWithTimeout ( 1 , handler: nil )
669
+ XCTAssertEqual ( task. state, NSURLSessionTaskState . Completed)
670
+ }
671
+ }
551
672
}
552
673
553
674
private extension HTTPResponse {
0 commit comments