CacheTests.swift 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. // CacheTests.swift
  2. //
  3. // Copyright (c) 2014–2015 Alamofire Software Foundation (http://alamofire.org/)
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to deal
  7. // in the Software without restriction, including without limitation the rights
  8. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. // THE SOFTWARE.
  22. import Alamofire
  23. import Foundation
  24. import XCTest
  25. /**
  26. The cache test cases test various NSURLRequestCachePolicy types against different combinations of
  27. Cache-Control headers. These tests use the response timestamp to verify whether the cached response
  28. data was returned. This requires each test to have a 1 second delay built into the second request
  29. which is not ideal.
  30. */
  31. class CacheTestCase: BaseTestCase {
  32. // MARK: Properties
  33. let URLString = "http://httpbin.org/response-headers"
  34. var manager: Manager!
  35. var URLCache: NSURLCache { return self.manager.session.configuration.URLCache! }
  36. var requestCachePolicy: NSURLRequestCachePolicy { return self.manager.session.configuration.requestCachePolicy }
  37. // MARK: Setup and Teardown
  38. override func setUp() {
  39. super.setUp()
  40. // No-op
  41. }
  42. override func tearDown() {
  43. super.tearDown()
  44. self.URLCache.removeAllCachedResponses()
  45. }
  46. // MARK: Test Setup Methods
  47. func setUpManagerWithRequestCachePolicy(requestCachePolicy: NSURLRequestCachePolicy) {
  48. self.manager = {
  49. let configuration: NSURLSessionConfiguration = {
  50. let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
  51. configuration.HTTPAdditionalHeaders = Alamofire.Manager.defaultHTTPHeaders
  52. configuration.requestCachePolicy = requestCachePolicy
  53. let capacity = 50 * 1024 * 1024 // MBs
  54. configuration.URLCache = NSURLCache(memoryCapacity: capacity, diskCapacity: capacity, diskPath: nil)
  55. return configuration
  56. }()
  57. let manager = Manager(configuration: configuration)
  58. return manager
  59. }()
  60. }
  61. // MARK: Test Execution Methods
  62. func executeCacheControlHeaderTestWithValue(
  63. value: String,
  64. cachedResponsesExist: Bool,
  65. responseTimestampsAreEqual: Bool)
  66. {
  67. // Given
  68. let parameters = ["Cache-Control": value]
  69. var request1: NSURLRequest?
  70. var request2: NSURLRequest?
  71. var response1: NSHTTPURLResponse?
  72. var response2: NSHTTPURLResponse?
  73. // When
  74. let expectation1 = expectationWithDescription("GET request1 to httpbin")
  75. startRequestWithParameters(parameters) { request, response in
  76. request1 = request
  77. response1 = response
  78. expectation1.fulfill()
  79. }
  80. waitForExpectationsWithTimeout(self.defaultTimeout, handler: nil)
  81. let expectation2 = expectationWithDescription("GET request2 to httpbin")
  82. startRequestWithParameters(parameters, delay: 1.0) { request, response in
  83. request2 = request
  84. response2 = response
  85. expectation2.fulfill()
  86. }
  87. waitForExpectationsWithTimeout(self.defaultTimeout, handler: nil)
  88. // Then
  89. verifyCachedResponses(forRequest1: request1, andRequest2: request2, exist: cachedResponsesExist)
  90. verifyResponseTimestamps(forResponse1: response1, andResponse2: response2, areEqual: responseTimestampsAreEqual)
  91. }
  92. // MARK: Private - Start Request Methods
  93. private func startRequestWithParameters(
  94. parameters: [String: AnyObject],
  95. delay: Float = 0.0,
  96. completion: (NSURLRequest, NSHTTPURLResponse?) -> Void)
  97. {
  98. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Float(NSEC_PER_SEC))), dispatch_get_main_queue()) {
  99. let request = self.manager.request(.GET, self.URLString, parameters: parameters)
  100. request.response { _, response, _, _ in
  101. completion(request.request, response)
  102. }
  103. }
  104. }
  105. // MARK: Private - Test Verification Methods
  106. private func verifyCachedResponses(
  107. forRequest1 request1: NSURLRequest?,
  108. andRequest2 request2: NSURLRequest?,
  109. exist: Bool)
  110. {
  111. if let
  112. request1 = request1,
  113. request2 = request2
  114. {
  115. let cachedResponse1 = self.URLCache.cachedResponseForRequest(request1)
  116. let cachedResponse2 = self.URLCache.cachedResponseForRequest(request2)
  117. if exist {
  118. XCTAssertNotNil(cachedResponse1, "cached response 1 should not be nil")
  119. XCTAssertNotNil(cachedResponse2, "cached response 2 should not be nil")
  120. } else {
  121. XCTAssertNil(cachedResponse1, "cached response 1 should be nil")
  122. XCTAssertNil(cachedResponse2, "cached response 2 should be nil")
  123. }
  124. } else {
  125. XCTFail("requests should not be nil")
  126. }
  127. }
  128. private func verifyResponseTimestamps(
  129. forResponse1 response1: NSHTTPURLResponse?,
  130. andResponse2 response2: NSHTTPURLResponse?,
  131. areEqual equal: Bool)
  132. {
  133. if let
  134. response1 = response1,
  135. response2 = response2
  136. {
  137. if let
  138. timestamp1 = response1.allHeaderFields["Date"] as? String,
  139. timestamp2 = response2.allHeaderFields["Date"] as? String
  140. {
  141. if equal {
  142. XCTAssertEqual(timestamp1, timestamp2, "timestamps should be equal")
  143. } else {
  144. XCTAssertNotEqual(timestamp1, timestamp2, "timestamps should not be equal")
  145. }
  146. } else {
  147. XCTFail("response timestamps should not be nil")
  148. }
  149. } else {
  150. XCTFail("responses should not be nil")
  151. }
  152. }
  153. }
  154. // MARK: -
  155. class DefaultCacheBehaviorTestCase: CacheTestCase {
  156. // MARK: Setup and Teardown
  157. override func setUp() {
  158. super.setUp()
  159. setUpManagerWithRequestCachePolicy(.UseProtocolCachePolicy)
  160. }
  161. override func tearDown() {
  162. super.tearDown()
  163. // No-op
  164. }
  165. // MARK: Tests
  166. func testCacheControlHeaderWithNoCacheValue() {
  167. executeCacheControlHeaderTestWithValue("no-cache", cachedResponsesExist: true, responseTimestampsAreEqual: false)
  168. }
  169. func testCacheControlHeaderWithNoStoreValue() {
  170. executeCacheControlHeaderTestWithValue("no-store", cachedResponsesExist: false, responseTimestampsAreEqual: false)
  171. }
  172. func testCacheControlHeaderWithPublicValue() {
  173. executeCacheControlHeaderTestWithValue("public", cachedResponsesExist: true, responseTimestampsAreEqual: false)
  174. }
  175. func testCacheControlHeaderWithPrivateValue() {
  176. executeCacheControlHeaderTestWithValue("private", cachedResponsesExist: true, responseTimestampsAreEqual: false)
  177. }
  178. func testCacheControlHeaderWithNonExpiredMaxAgeValue() {
  179. executeCacheControlHeaderTestWithValue("max-age=3600", cachedResponsesExist: true, responseTimestampsAreEqual: true)
  180. }
  181. func testCacheControlHeaderWithExpiredMaxAgeValue() {
  182. executeCacheControlHeaderTestWithValue("max-age=0", cachedResponsesExist: true, responseTimestampsAreEqual: false)
  183. }
  184. }
  185. // MARK: -
  186. class IgnoreLocalCacheDataTestCase: CacheTestCase {
  187. // MARK: Setup and Teardown
  188. override func setUp() {
  189. super.setUp()
  190. setUpManagerWithRequestCachePolicy(.ReloadIgnoringLocalCacheData)
  191. }
  192. override func tearDown() {
  193. super.tearDown()
  194. // No-op
  195. }
  196. // MARK: Tests
  197. func testCacheControlHeaderWithNoCacheValue() {
  198. executeCacheControlHeaderTestWithValue("no-cache", cachedResponsesExist: true, responseTimestampsAreEqual: false)
  199. }
  200. func testCacheControlHeaderWithNoStoreValue() {
  201. executeCacheControlHeaderTestWithValue("no-store", cachedResponsesExist: false, responseTimestampsAreEqual: false)
  202. }
  203. func testCacheControlHeaderWithPublicValue() {
  204. executeCacheControlHeaderTestWithValue("public", cachedResponsesExist: true, responseTimestampsAreEqual: false)
  205. }
  206. func testCacheControlHeaderWithPrivateValue() {
  207. executeCacheControlHeaderTestWithValue("private", cachedResponsesExist: true, responseTimestampsAreEqual: false)
  208. }
  209. func testCacheControlHeaderWithNonExpiredMaxAgeValue() {
  210. executeCacheControlHeaderTestWithValue("max-age=3600", cachedResponsesExist: true, responseTimestampsAreEqual: false)
  211. }
  212. func testCacheControlHeaderWithExpiredMaxAgeValue() {
  213. executeCacheControlHeaderTestWithValue("max-age=0", cachedResponsesExist: true, responseTimestampsAreEqual: false)
  214. }
  215. }
  216. // MARK: -
  217. class UseLocalCacheDataIfExistsOtherwiseLoadFromNetworkTestCase: CacheTestCase {
  218. // MARK: Setup and Teardown
  219. override func setUp() {
  220. super.setUp()
  221. setUpManagerWithRequestCachePolicy(.ReturnCacheDataElseLoad)
  222. }
  223. override func tearDown() {
  224. super.tearDown()
  225. // No-op
  226. }
  227. // MARK: Tests
  228. func testCacheControlHeaderWithNoCacheValue() {
  229. executeCacheControlHeaderTestWithValue("no-cache", cachedResponsesExist: true, responseTimestampsAreEqual: true)
  230. }
  231. func testCacheControlHeaderWithNoStoreValue() {
  232. executeCacheControlHeaderTestWithValue("no-store", cachedResponsesExist: false, responseTimestampsAreEqual: false)
  233. }
  234. func testCacheControlHeaderWithPublicValue() {
  235. executeCacheControlHeaderTestWithValue("public", cachedResponsesExist: true, responseTimestampsAreEqual: true)
  236. }
  237. func testCacheControlHeaderWithPrivateValue() {
  238. executeCacheControlHeaderTestWithValue("private", cachedResponsesExist: true, responseTimestampsAreEqual: true)
  239. }
  240. func testCacheControlHeaderWithNonExpiredMaxAgeValue() {
  241. executeCacheControlHeaderTestWithValue("max-age=3600", cachedResponsesExist: true, responseTimestampsAreEqual: true)
  242. }
  243. func testCacheControlHeaderWithExpiredMaxAgeValue() {
  244. executeCacheControlHeaderTestWithValue("max-age=0", cachedResponsesExist: true, responseTimestampsAreEqual: true)
  245. }
  246. }
  247. // MARK: -
  248. class UseLocalCacheDataAndDontLoadFromNetworkTestCase: CacheTestCase {
  249. // MARK: Properties
  250. var defaultManager: Manager!
  251. // MARK: Setup and Teardown
  252. override func setUp() {
  253. super.setUp()
  254. setUpManagerWithRequestCachePolicy(.ReturnCacheDataDontLoad)
  255. self.defaultManager = {
  256. let configuration: NSURLSessionConfiguration = {
  257. let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
  258. configuration.HTTPAdditionalHeaders = Alamofire.Manager.defaultHTTPHeaders
  259. configuration.requestCachePolicy = .UseProtocolCachePolicy
  260. configuration.URLCache = self.URLCache
  261. return configuration
  262. }()
  263. let manager = Manager(configuration: configuration)
  264. return manager
  265. }()
  266. }
  267. override func tearDown() {
  268. super.tearDown()
  269. // No-op
  270. }
  271. // MARK: Tests
  272. func testRequestWithoutCachedResponseFailsWithResourceUnavailable() {
  273. // Given
  274. let expectation = expectationWithDescription("GET request to httpbin")
  275. var request: NSURLRequest?
  276. var response: NSHTTPURLResponse?
  277. var data: AnyObject?
  278. var error: NSError?
  279. // When
  280. self.manager.request(.GET, "http://httpbin.org/get")
  281. .response { responseRequest, responseResponse, responseData, responseError in
  282. request = responseRequest
  283. response = responseResponse
  284. data = responseData
  285. error = responseError
  286. expectation.fulfill()
  287. }
  288. waitForExpectationsWithTimeout(self.defaultTimeout, handler: nil)
  289. // Then
  290. XCTAssertNotNil(request, "request should not be nil")
  291. XCTAssertNil(response, "response should be nil")
  292. XCTAssertNotNil(data, "data should not be nil")
  293. XCTAssertNotNil(error, "error should not be nil")
  294. if let
  295. data = data as? NSData,
  296. actualData = NSString(data: data, encoding: NSUTF8StringEncoding) as? String
  297. {
  298. XCTAssertEqual(actualData, "", "data values should be equal")
  299. }
  300. if let error = error {
  301. XCTAssertEqual(error.code, NSURLErrorResourceUnavailable, "error code should be equal")
  302. }
  303. }
  304. func testCacheControlHeaderWithNoCacheValue() {
  305. executeCacheControlHeaderTestWithValue("no-cache", cachedResponsesExist: true, responseTimestampsAreEqual: true)
  306. }
  307. func testCacheControlHeaderWithNoStoreValue() {
  308. // Given
  309. let parameters = ["Cache-Control": "no-store"]
  310. var request1: NSURLRequest?
  311. var request2: NSURLRequest?
  312. var response1: NSHTTPURLResponse?
  313. var response2: NSHTTPURLResponse?
  314. var data1: AnyObject?
  315. var data2: AnyObject?
  316. var error1: NSError?
  317. var error2: NSError?
  318. // When
  319. let expectation1 = expectationWithDescription("GET request1 to httpbin")
  320. self.defaultManager.request(.GET, self.URLString, parameters: parameters)
  321. .response { responseRequest, responseResponse, responseData, responseError in
  322. request1 = responseRequest
  323. response1 = responseResponse
  324. data1 = responseData
  325. error1 = responseError
  326. expectation1.fulfill()
  327. }
  328. waitForExpectationsWithTimeout(self.defaultTimeout, handler: nil)
  329. let expectation2 = expectationWithDescription("GET request2 to httpbin")
  330. self.manager.request(.GET, self.URLString, parameters: parameters)
  331. .response { responseRequest, responseResponse, responseData, responseError in
  332. request2 = responseRequest
  333. response2 = responseResponse
  334. data2 = responseData
  335. error2 = responseError
  336. expectation2.fulfill()
  337. }
  338. waitForExpectationsWithTimeout(self.defaultTimeout, handler: nil)
  339. // Then
  340. XCTAssertNotNil(request1, "request1 should not be nil")
  341. XCTAssertNotNil(response1, "response1 should not be nil")
  342. XCTAssertNotNil(data1, "data1 should not be nil")
  343. XCTAssertNil(error1, "error1 should be nil")
  344. XCTAssertNotNil(request2, "request2 should not be nil")
  345. XCTAssertNil(response2, "response2 should be nil")
  346. XCTAssertNotNil(data2, "data2 should not be nil")
  347. XCTAssertNotNil(error2, "error2 should not be nil")
  348. if let
  349. data = data2 as? NSData,
  350. actualData = NSString(data: data, encoding: NSUTF8StringEncoding) as? String
  351. {
  352. XCTAssertEqual(actualData, "", "data values should be equal")
  353. }
  354. if let error = error2 {
  355. XCTAssertEqual(error.code, NSURLErrorResourceUnavailable, "error code should be equal")
  356. }
  357. }
  358. func testCacheControlHeaderWithPublicValue() {
  359. executeCacheControlHeaderTestWithValue("public", cachedResponsesExist: true, responseTimestampsAreEqual: true)
  360. }
  361. func testCacheControlHeaderWithPrivateValue() {
  362. executeCacheControlHeaderTestWithValue("private", cachedResponsesExist: true, responseTimestampsAreEqual: true)
  363. }
  364. func testCacheControlHeaderWithNonExpiredMaxAgeValue() {
  365. executeCacheControlHeaderTestWithValue("max-age=3600", cachedResponsesExist: true, responseTimestampsAreEqual: true)
  366. }
  367. func testCacheControlHeaderWithExpiredMaxAgeValue() {
  368. executeCacheControlHeaderTestWithValue("max-age=0", cachedResponsesExist: true, responseTimestampsAreEqual: true)
  369. }
  370. // MARK: Overridden Test Execution Methods
  371. override func executeCacheControlHeaderTestWithValue(
  372. value: String,
  373. cachedResponsesExist: Bool,
  374. responseTimestampsAreEqual: Bool)
  375. {
  376. // Given
  377. let parameters = ["Cache-Control": value]
  378. var request1: NSURLRequest?
  379. var request2: NSURLRequest?
  380. var response1: NSHTTPURLResponse?
  381. var response2: NSHTTPURLResponse?
  382. // When
  383. let expectation1 = expectationWithDescription("GET request1 to httpbin")
  384. self.defaultManager.request(.GET, self.URLString, parameters: parameters)
  385. .response { request, response, _, _ in
  386. request1 = request
  387. response1 = response
  388. expectation1.fulfill()
  389. }
  390. waitForExpectationsWithTimeout(self.defaultTimeout, handler: nil)
  391. let expectation2 = expectationWithDescription("GET request2 to httpbin")
  392. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(1.0 * Float(NSEC_PER_SEC))), dispatch_get_main_queue()) {
  393. self.manager.request(.GET, self.URLString, parameters: parameters)
  394. .response { request, response, _, _ in
  395. request2 = request
  396. response2 = response
  397. expectation2.fulfill()
  398. }
  399. }
  400. waitForExpectationsWithTimeout(self.defaultTimeout, handler: nil)
  401. // Then
  402. verifyCachedResponses(forRequest1: request1, andRequest2: request2, exist: cachedResponsesExist)
  403. verifyResponseTimestamps(forResponse1: response1, andResponse2: response2, areEqual: responseTimestampsAreEqual)
  404. }
  405. }