@@ -18,6 +18,12 @@ import FoundationNetworking
18
18
#endif
19
19
20
20
class DownloaderTests : XCTestCase {
21
+
22
+ override func tearDown( ) {
23
+ if #available( OSX 10 . 13 , * ) {
24
+ Netrc . _mock = nil
25
+ }
26
+ }
21
27
22
28
func testSuccess( ) {
23
29
// FIXME: Remove once https://github.com/apple/swift-corelibs-foundation/pull/2593 gets inside a toolchain.
@@ -73,6 +79,140 @@ class DownloaderTests: XCTestCase {
73
79
}
74
80
#endif
75
81
}
82
+
83
+ @available ( OSX 10 . 13 , * )
84
+ func testAuthenticatedSuccess( ) {
85
+
86
+ let netrcContent = " machine protected.downloader-tests.com login anonymous password qwerty "
87
+ guard case . success( let netrc) = Netrc . from ( netrcContent) else {
88
+ return XCTFail ( " Cannot load netrc content " )
89
+ }
90
+ let authData = " anonymous:qwerty " . data ( using: . utf8) !
91
+ let testAuthHeader = " Basic \( authData. base64EncodedString ( ) ) "
92
+ Netrc . _mock = netrc
93
+
94
+ #if os(macOS)
95
+ let configuration = URLSessionConfiguration . default
96
+ configuration. protocolClasses = [ MockAuthenticatingURLProtocol . self]
97
+ let downloader = FoundationDownloader ( configuration: configuration)
98
+
99
+ mktmpdir { tmpdir in
100
+ let url = URL ( string: " https://protected.downloader-tests.com/testBasics.zip " ) !
101
+ let destination = tmpdir. appending ( component: " download " )
102
+
103
+ let didStartLoadingExpectation = XCTestExpectation ( description: " didStartLoading " )
104
+ let progress50Expectation = XCTestExpectation ( description: " progress50 " )
105
+ let progress100Expectation = XCTestExpectation ( description: " progress100 " )
106
+ let successExpectation = XCTestExpectation ( description: " success " )
107
+ MockAuthenticatingURLProtocol . notifyDidStartLoading ( for: url, completion: { didStartLoadingExpectation. fulfill ( ) } )
108
+
109
+ downloader. downloadFile ( at: url, to: destination, progress: { bytesDownloaded, totalBytesToDownload in
110
+
111
+ XCTAssertEqual ( MockAuthenticatingURLProtocol . authenticationHeader ( for: url) , testAuthHeader)
112
+
113
+ switch ( bytesDownloaded, totalBytesToDownload) {
114
+ case ( 512 , 1024 ) :
115
+ progress50Expectation. fulfill ( )
116
+ case ( 1024 , 1024 ) :
117
+ progress100Expectation. fulfill ( )
118
+ default :
119
+ XCTFail ( " unexpected progress " )
120
+ }
121
+ } , completion: { result in
122
+ switch result {
123
+ case . success:
124
+ XCTAssert ( localFileSystem. exists ( destination) )
125
+ let bytes = ByteString ( Array ( repeating: 0xbe , count: 512 ) + Array( repeating: 0xef , count: 512 ) )
126
+ XCTAssertEqual ( try ! localFileSystem. readFileContents ( destination) , bytes)
127
+ successExpectation. fulfill ( )
128
+ case . failure( let error) :
129
+ XCTFail ( " \( error) " )
130
+ }
131
+ } )
132
+
133
+ wait ( for: [ didStartLoadingExpectation] , timeout: 1.0 )
134
+
135
+ let response = HTTPURLResponse ( url: url, statusCode: 200 , httpVersion: " 1.1 " , headerFields: [
136
+ " Content-Length " : " 1024 "
137
+ ] ) !
138
+
139
+ MockAuthenticatingURLProtocol . sendResponse ( response, for: url)
140
+ MockAuthenticatingURLProtocol . sendData ( Data ( repeating: 0xbe , count: 512 ) , for: url)
141
+ wait ( for: [ progress50Expectation] , timeout: 1.0 )
142
+ MockAuthenticatingURLProtocol . sendData ( Data ( repeating: 0xef , count: 512 ) , for: url)
143
+ wait ( for: [ progress100Expectation] , timeout: 1.0 )
144
+ MockAuthenticatingURLProtocol . sendCompletion ( for: url)
145
+ wait ( for: [ successExpectation] , timeout: 1.0 )
146
+ }
147
+ #endif
148
+ }
149
+
150
+ @available ( OSX 10 . 13 , * )
151
+ func testDefaultAuthenticationSuccess( ) {
152
+
153
+ let netrcContent = " default login default password default "
154
+ guard case . success( let netrc) = Netrc . from ( netrcContent) else {
155
+ return XCTFail ( " Cannot load netrc content " )
156
+ }
157
+ let authData = " default:default " . data ( using: . utf8) !
158
+ let testAuthHeader = " Basic \( authData. base64EncodedString ( ) ) "
159
+ Netrc . _mock = netrc
160
+
161
+ #if os(macOS)
162
+ let configuration = URLSessionConfiguration . default
163
+ configuration. protocolClasses = [ MockAuthenticatingURLProtocol . self]
164
+ let downloader = FoundationDownloader ( configuration: configuration)
165
+
166
+ mktmpdir { tmpdir in
167
+ let url = URL ( string: " https://restricted.downloader-tests.com/testBasics.zip " ) !
168
+ let destination = tmpdir. appending ( component: " download " )
169
+
170
+ let didStartLoadingExpectation = XCTestExpectation ( description: " didStartLoading " )
171
+ let progress50Expectation = XCTestExpectation ( description: " progress50 " )
172
+ let progress100Expectation = XCTestExpectation ( description: " progress100 " )
173
+ let successExpectation = XCTestExpectation ( description: " success " )
174
+ MockAuthenticatingURLProtocol . notifyDidStartLoading ( for: url, completion: { didStartLoadingExpectation. fulfill ( ) } )
175
+
176
+ downloader. downloadFile ( at: url, to: destination, progress: { bytesDownloaded, totalBytesToDownload in
177
+
178
+ XCTAssertEqual ( MockAuthenticatingURLProtocol . authenticationHeader ( for: url) , testAuthHeader)
179
+
180
+ switch ( bytesDownloaded, totalBytesToDownload) {
181
+ case ( 512 , 1024 ) :
182
+ progress50Expectation. fulfill ( )
183
+ case ( 1024 , 1024 ) :
184
+ progress100Expectation. fulfill ( )
185
+ default :
186
+ XCTFail ( " unexpected progress " )
187
+ }
188
+ } , completion: { result in
189
+ switch result {
190
+ case . success:
191
+ XCTAssert ( localFileSystem. exists ( destination) )
192
+ let bytes = ByteString ( Array ( repeating: 0xbe , count: 512 ) + Array( repeating: 0xef , count: 512 ) )
193
+ XCTAssertEqual ( try ! localFileSystem. readFileContents ( destination) , bytes)
194
+ successExpectation. fulfill ( )
195
+ case . failure( let error) :
196
+ XCTFail ( " \( error) " )
197
+ }
198
+ } )
199
+
200
+ wait ( for: [ didStartLoadingExpectation] , timeout: 1.0 )
201
+
202
+ let response = HTTPURLResponse ( url: url, statusCode: 200 , httpVersion: " 1.1 " , headerFields: [
203
+ " Content-Length " : " 1024 "
204
+ ] ) !
205
+
206
+ MockAuthenticatingURLProtocol . sendResponse ( response, for: url)
207
+ MockAuthenticatingURLProtocol . sendData ( Data ( repeating: 0xbe , count: 512 ) , for: url)
208
+ wait ( for: [ progress50Expectation] , timeout: 1.0 )
209
+ MockAuthenticatingURLProtocol . sendData ( Data ( repeating: 0xef , count: 512 ) , for: url)
210
+ wait ( for: [ progress100Expectation] , timeout: 1.0 )
211
+ MockAuthenticatingURLProtocol . sendCompletion ( for: url)
212
+ wait ( for: [ successExpectation] , timeout: 1.0 )
213
+ }
214
+ #endif
215
+ }
76
216
77
217
func testClientError( ) {
78
218
// FIXME: Remove once https://github.com/apple/swift-corelibs-foundation/pull/2593 gets inside a toolchain.
@@ -209,6 +349,16 @@ private struct DummyError: Error {
209
349
210
350
private typealias Action = ( ) -> Void
211
351
352
+ private class MockAuthenticatingURLProtocol : MockURLProtocol {
353
+
354
+ fileprivate static func authenticationHeader( for url: Foundation . URL ) -> String ? {
355
+ guard let instance = instance ( for: url) else {
356
+ fatalError ( " url did not start loading " )
357
+ }
358
+ return instance. request. allHTTPHeaderFields ? [ " Authorization " ]
359
+ }
360
+ }
361
+
212
362
private class MockURLProtocol : URLProtocol {
213
363
private static var queue = DispatchQueue ( label: " org.swift.swiftpm.basic-tests.mock-url-protocol " )
214
364
private static var observers : [ Foundation . URL : Action ] = [ : ]
@@ -310,6 +460,10 @@ private class MockURLProtocol: URLProtocol {
310
460
Self . instances [ url] = nil
311
461
}
312
462
}
463
+
464
+ fileprivate static func instance( for url: Foundation . URL ) -> URLProtocol ? {
465
+ return Self . instances [ url]
466
+ }
313
467
}
314
468
315
469
class FailingFileSystem : FileSystem {
0 commit comments