MemoryStorageTests.swift 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. //
  2. // MemoryStorageTests.swift
  3. // Kingfisher
  4. //
  5. // Created by Wei Wang on 2018/11/12.
  6. //
  7. // Copyright (c) 2019 Wei Wang <onevcat@gmail.com>
  8. //
  9. // Permission is hereby granted, free of charge, to any person obtaining a copy
  10. // of this software and associated documentation files (the "Software"), to deal
  11. // in the Software without restriction, including without limitation the rights
  12. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. // copies of the Software, and to permit persons to whom the Software is
  14. // furnished to do so, subject to the following conditions:
  15. //
  16. // The above copyright notice and this permission notice shall be included in
  17. // all copies or substantial portions of the Software.
  18. //
  19. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25. // THE SOFTWARE.
  26. import XCTest
  27. @testable import Kingfisher
  28. extension Int: CacheCostCalculable {
  29. public var cacheCost: Int {
  30. return 1
  31. }
  32. }
  33. class MemoryStorageTests: XCTestCase {
  34. var storage: MemoryStorage.Backend<Int>!
  35. override func setUp() {
  36. super.setUp()
  37. let config = MemoryStorage.Config(totalCostLimit: 3)
  38. storage = MemoryStorage.Backend(config: config)
  39. }
  40. override func tearDown() {
  41. storage = nil
  42. super.tearDown()
  43. }
  44. func testConfigSettingStorage() {
  45. XCTAssertEqual(storage.config.totalCostLimit, 3)
  46. XCTAssertEqual(storage.storage.totalCostLimit, 3)
  47. storage.config = MemoryStorage.Config(totalCostLimit: 10)
  48. XCTAssertEqual(storage.config.totalCostLimit, 10)
  49. XCTAssertEqual(storage.storage.totalCostLimit, 10)
  50. storage.config.countLimit = 100
  51. XCTAssertEqual(storage.config.countLimit, 100)
  52. XCTAssertEqual(storage.storage.countLimit, 100)
  53. }
  54. func testStoreAndGetValue() {
  55. XCTAssertFalse(storage.isCached(forKey: "1"))
  56. try! storage.store(value: 1, forKey: "1")
  57. XCTAssertTrue(storage.isCached(forKey: "1"))
  58. XCTAssertEqual(try! storage.value(forKey: "1"), 1)
  59. }
  60. func testStoreValueOverwritting() {
  61. try! storage.store(value: 1, forKey: "1")
  62. XCTAssertEqual(try! storage.value(forKey: "1"), 1)
  63. try! storage.store(value: 100, forKey: "1")
  64. XCTAssertEqual(try! storage.value(forKey: "1"), 100)
  65. }
  66. func testRemoveValue() {
  67. XCTAssertFalse(storage.isCached(forKey: "1"))
  68. try! storage.store(value: 1, forKey: "1")
  69. XCTAssertTrue(storage.isCached(forKey: "1"))
  70. try! storage.remove(forKey: "1")
  71. XCTAssertFalse(storage.isCached(forKey: "1"))
  72. }
  73. func testRemoveAllValues() {
  74. try! storage.store(value: 1, forKey: "1")
  75. try! storage.store(value: 2, forKey: "2")
  76. XCTAssertTrue(storage.isCached(forKey: "1"))
  77. XCTAssertTrue(storage.isCached(forKey: "2"))
  78. try! storage.removeAll()
  79. XCTAssertFalse(storage.isCached(forKey: "1"))
  80. XCTAssertFalse(storage.isCached(forKey: "2"))
  81. }
  82. func testStoreWithExpiration() {
  83. let exp = expectation(description: #function)
  84. XCTAssertFalse(storage.isCached(forKey: "1"))
  85. try! storage.store(value: 1, forKey: "1", expiration: .seconds(0.1))
  86. XCTAssertTrue(storage.isCached(forKey: "1"))
  87. XCTAssertFalse(storage.isCached(forKey: "2"))
  88. try! storage.store(value: 2, forKey: "2")
  89. XCTAssertTrue(storage.isCached(forKey: "2"))
  90. delay(0.2) {
  91. XCTAssertFalse(self.storage.isCached(forKey: "1"))
  92. XCTAssertTrue(self.storage.isCached(forKey: "2"))
  93. // But the object is still in underlying cache.
  94. let obj = self.storage.storage.object(forKey: "1")
  95. XCTAssertNotNil(obj)
  96. exp.fulfill()
  97. }
  98. waitForExpectations(timeout: 3, handler: nil)
  99. }
  100. func testStoreWithConfigExpiration() {
  101. let exp = expectation(description: #function)
  102. storage.config.expiration = .seconds(0.1)
  103. XCTAssertFalse(storage.isCached(forKey: "1"))
  104. try! storage.store(value: 1, forKey: "1")
  105. XCTAssertTrue(storage.isCached(forKey: "1"))
  106. delay(0.2) {
  107. XCTAssertFalse(self.storage.isCached(forKey: "1"))
  108. // But the object is still in underlying cache.
  109. let obj = self.storage.storage.object(forKey: "1")
  110. XCTAssertNotNil(obj)
  111. exp.fulfill()
  112. }
  113. waitForExpectations(timeout: 3, handler: nil)
  114. }
  115. func testStoreWithExpirationExtending() {
  116. let exp = expectation(description: #function)
  117. XCTAssertFalse(storage.isCached(forKey: "1"))
  118. try! storage.store(value: 1, forKey: "1", expiration: .seconds(0.15))
  119. XCTAssertTrue(storage.isCached(forKey: "1"))
  120. XCTAssertFalse(storage.isCached(forKey: "2"))
  121. try! storage.store(value: 2, forKey: "2")
  122. XCTAssertTrue(storage.isCached(forKey: "2"))
  123. delay(0.1) {
  124. // Request for the object to extend it's expiration date
  125. let obj = self.storage.value(forKey: "1", extendingExpiration: true)
  126. XCTAssertNotNil(obj)
  127. }
  128. delay(0.2) {
  129. XCTAssertTrue(self.storage.isCached(forKey: "1"))
  130. XCTAssertTrue(self.storage.isCached(forKey: "2"))
  131. exp.fulfill()
  132. }
  133. waitForExpectations(timeout: 3, handler: nil)
  134. }
  135. func testStoreWithExpirationNotExtending() {
  136. let exp = expectation(description: #function)
  137. XCTAssertFalse(storage.isCached(forKey: "1"))
  138. try! storage.store(value: 1, forKey: "1", expiration: .seconds(0.15))
  139. XCTAssertTrue(storage.isCached(forKey: "1"))
  140. XCTAssertFalse(storage.isCached(forKey: "2"))
  141. try! storage.store(value: 2, forKey: "2")
  142. XCTAssertTrue(storage.isCached(forKey: "2"))
  143. delay(0.1) {
  144. // Request for the object to not extend it's expiration date
  145. let obj = self.storage.value(forKey: "1", extendingExpiration: false)
  146. XCTAssertNotNil(obj)
  147. }
  148. delay(0.2) {
  149. XCTAssertFalse(self.storage.isCached(forKey: "1"))
  150. XCTAssertTrue(self.storage.isCached(forKey: "2"))
  151. exp.fulfill()
  152. }
  153. waitForExpectations(timeout: 3, handler: nil)
  154. }
  155. func testRemoveExpired() {
  156. let exp = expectation(description: #function)
  157. XCTAssertFalse(storage.isCached(forKey: "1"))
  158. try! storage.store(value: 1, forKey: "1", expiration: .seconds(0.1))
  159. XCTAssertTrue(storage.isCached(forKey: "1"))
  160. delay(0.2) {
  161. XCTAssertFalse(self.storage.isCached(forKey: "1"))
  162. // But the object is still in underlying cache.
  163. XCTAssertNotNil(self.storage.storage.object(forKey: "1"))
  164. self.storage.removeExpired()
  165. // It should be removed now.
  166. XCTAssertNil(self.storage.storage.object(forKey: "1"))
  167. exp.fulfill()
  168. }
  169. waitForExpectations(timeout: 3, handler: nil)
  170. }
  171. func testExtendExpirationByAccessing() {
  172. let exp = expectation(description: #function)
  173. let expiration = StorageExpiration.seconds(0.5)
  174. try! storage.store(value: 1, forKey: "1", expiration: expiration)
  175. delay(0.3) {
  176. // This should extend the expiration to (0.3 + 0.5) from initially created.
  177. let v = try! self.storage.value(forKey: "1")
  178. XCTAssertEqual(v, 1)
  179. }
  180. delay(0.6) {
  181. // Accessing `isCached` does not extend expiration
  182. XCTAssertTrue(self.storage.isCached(forKey: "1"))
  183. }
  184. delay(1) {
  185. XCTAssertFalse(self.storage.isCached(forKey: "1"))
  186. exp.fulfill()
  187. }
  188. waitForExpectations(timeout: 3, handler: nil)
  189. }
  190. func testAutoCleanExpiredMemory() {
  191. let exp = expectation(description: #function)
  192. let config = MemoryStorage.Config(totalCostLimit: 3, cleanInterval: 0.1)
  193. storage = MemoryStorage.Backend(config: config)
  194. try! storage.store(value: 1, forKey: "1", expiration: .seconds(0.1))
  195. XCTAssertTrue(storage.isCached(forKey: "1"))
  196. XCTAssertEqual(self.storage.keys.count, 1)
  197. delay(0.2) {
  198. XCTAssertFalse(self.storage.isCached(forKey: "1"))
  199. XCTAssertNil(self.storage.storage.object(forKey: "1"))
  200. XCTAssertEqual(self.storage.keys.count, 0)
  201. exp.fulfill()
  202. }
  203. waitForExpectations(timeout: 3, handler: nil)
  204. }
  205. func testStorageObject() {
  206. let now = Date()
  207. let obj = MemoryStorage.StorageObject(1, key: "1", expiration: .seconds(1))
  208. XCTAssertEqual(obj.value, 1)
  209. XCTAssertEqual(
  210. obj.estimatedExpiration.timeIntervalSince1970,
  211. now.addingTimeInterval(1).timeIntervalSince1970,
  212. accuracy: 0.2)
  213. let exp = expectation(description: #function)
  214. delay(0.5) {
  215. obj.extendExpiration()
  216. XCTAssertEqual(
  217. obj.estimatedExpiration.timeIntervalSince1970,
  218. now.addingTimeInterval(1.5).timeIntervalSince1970,
  219. accuracy: 0.2)
  220. exp.fulfill()
  221. }
  222. waitForExpectations(timeout: 3, handler: nil)
  223. }
  224. }