DiskStorageTests.swift 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. //
  2. // DiskStorageTests.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. #if compiler(>=6)
  29. extension String: @retroactive DataTransformable { }
  30. #else
  31. extension String: DataTransformable { }
  32. #endif
  33. extension String {
  34. public func toData() throws -> Data {
  35. return data(using: .utf8)!
  36. }
  37. public static func fromData(_ data: Data) throws -> String {
  38. return String(data: data, encoding: .utf8)!
  39. }
  40. public static var empty: String { return "" }
  41. }
  42. class DiskStorageTests: XCTestCase {
  43. var storage: DiskStorage.Backend<String>!
  44. override func setUp() {
  45. super.setUp()
  46. let uuid = UUID().uuidString
  47. let config = DiskStorage.Config(name: "test-\(uuid)", sizeLimit: 5)
  48. storage = try! DiskStorage.Backend<String>(config: config)
  49. }
  50. override func tearDown() {
  51. try! storage.removeAll(skipCreatingDirectory: true)
  52. super.tearDown()
  53. }
  54. func testStoreAndGet() {
  55. XCTAssertFalse(storage.isCached(forKey: "1"))
  56. try! storage.store(value: "1", forKey: "1")
  57. XCTAssertTrue(storage.isCached(forKey: "1"))
  58. let value = try! storage.value(forKey: "1")
  59. XCTAssertEqual(value, "1")
  60. }
  61. func testRemove() {
  62. XCTAssertFalse(storage.isCached(forKey: "1"))
  63. try! storage.store(value: "1", forKey: "1")
  64. try! storage.remove(forKey: "1")
  65. XCTAssertFalse(storage.isCached(forKey: "1"))
  66. }
  67. func testRemoveAll() {
  68. try! storage.store(value: "1", forKey: "1")
  69. try! storage.store(value: "2", forKey: "2")
  70. try! storage.store(value: "3", forKey: "3")
  71. try! storage.removeAll()
  72. XCTAssertFalse(storage.isCached(forKey: "1"))
  73. XCTAssertFalse(storage.isCached(forKey: "2"))
  74. XCTAssertFalse(storage.isCached(forKey: "3"))
  75. }
  76. func testTotalSize() {
  77. var size = try! storage.totalSize()
  78. XCTAssertEqual(size, 0)
  79. try! storage.store(value: "1", forKey: "1")
  80. size = try! storage.totalSize()
  81. XCTAssertEqual(size, 1)
  82. }
  83. func testSetExpiration() {
  84. let now = Date()
  85. try! storage.store(value: "1", forKey: "1", expiration: .seconds(1))
  86. XCTAssertTrue(storage.isCached(forKey: "1", referenceDate: now))
  87. XCTAssertFalse(storage.isCached(forKey: "1", referenceDate: now.addingTimeInterval(5)))
  88. }
  89. func testConfigExpiration() {
  90. let now = Date()
  91. storage.config.expiration = .seconds(1)
  92. try! storage.store(value: "1", forKey: "1")
  93. XCTAssertTrue(storage.isCached(forKey: "1", referenceDate: now))
  94. XCTAssertFalse(storage.isCached(forKey: "1", referenceDate: now.addingTimeInterval(5)))
  95. }
  96. func testExtendExpirationByAccessing() {
  97. let exp = expectation(description: #function)
  98. let now = Date()
  99. try! storage.store(value: "1", forKey: "1", expiration: .seconds(2))
  100. XCTAssertTrue(storage.isCached(forKey: "1"))
  101. XCTAssertFalse(storage.isCached(forKey: "1", referenceDate: now.addingTimeInterval(5)))
  102. delay(1) {
  103. let v = try! self.storage.value(forKey: "1")
  104. XCTAssertNotNil(v)
  105. // The meta extending happens on its own queue.
  106. self.storage.metaChangingQueue.async {
  107. XCTAssertTrue(self.storage.isCached(forKey: "1", referenceDate: now.addingTimeInterval(3)))
  108. XCTAssertFalse(self.storage.isCached(forKey: "1", referenceDate: now.addingTimeInterval(10)))
  109. exp.fulfill()
  110. }
  111. }
  112. waitForExpectations(timeout: 2, handler: nil)
  113. }
  114. func testNotExtendExpirationByAccessing() {
  115. let exp = expectation(description: #function)
  116. let now = Date()
  117. try! storage.store(value: "1", forKey: "1", expiration: .seconds(2))
  118. XCTAssertTrue(storage.isCached(forKey: "1"))
  119. XCTAssertFalse(storage.isCached(forKey: "1", referenceDate: now.addingTimeInterval(3)))
  120. delay(1) {
  121. let v = try! self.storage.value(forKey: "1", extendingExpiration: .none)
  122. XCTAssertNotNil(v)
  123. // The meta extending happens on its own queue.
  124. self.storage.metaChangingQueue.async {
  125. XCTAssertFalse(self.storage.isCached(forKey: "1", referenceDate: now.addingTimeInterval(3)))
  126. XCTAssertFalse(self.storage.isCached(forKey: "1", referenceDate: now.addingTimeInterval(10)))
  127. exp.fulfill()
  128. }
  129. }
  130. waitForExpectations(timeout: 2, handler: nil)
  131. }
  132. func testRemoveExpired() {
  133. let expiration = StorageExpiration.seconds(1)
  134. try! storage.store(value: "1", forKey: "1", expiration: expiration)
  135. try! storage.store(value: "2", forKey: "2", expiration: expiration)
  136. try! storage.store(value: "3", forKey: "3")
  137. let urls = try! self.storage.removeExpiredValues(referenceDate: Date().addingTimeInterval(2))
  138. XCTAssertEqual(urls.count, 2)
  139. XCTAssertTrue(self.storage.isCached(forKey: "3"))
  140. }
  141. func testRemoveSizeExceeded() {
  142. let count = 10
  143. for i in 0..<count {
  144. let s = String(i)
  145. try! storage.store(value: s, forKey: s)
  146. }
  147. let urls = try! storage.removeSizeExceededValues()
  148. XCTAssertTrue(urls.count < count)
  149. XCTAssertTrue(urls.count > 0)
  150. }
  151. func testConfigUsesHashedFileName() {
  152. let key = "test"
  153. // hashed fileName
  154. storage.config.usesHashedFileName = true
  155. let hashedFileName = storage.cacheFileName(forKey: key)
  156. XCTAssertNotEqual(hashedFileName, key)
  157. // validation sha256 hash of the key
  158. XCTAssertEqual(hashedFileName, key.kf.sha256)
  159. // fileName without hash
  160. storage.config.usesHashedFileName = false
  161. let originalFileName = storage.cacheFileName(forKey: key)
  162. XCTAssertEqual(originalFileName, key)
  163. }
  164. func testConfigUsesHashedFileNameWithAutoExt() {
  165. let key = "test.gif"
  166. // hashed fileName
  167. storage.config.usesHashedFileName = true
  168. storage.config.autoExtAfterHashedFileName = true
  169. let hashedFileName = storage.cacheFileName(forKey: key)
  170. XCTAssertNotEqual(hashedFileName, key)
  171. // validation sha256 hash of the key
  172. XCTAssertEqual(hashedFileName, key.kf.sha256 + ".gif")
  173. // fileName without hash
  174. storage.config.usesHashedFileName = false
  175. let originalFileName = storage.cacheFileName(forKey: key)
  176. XCTAssertEqual(originalFileName, key)
  177. }
  178. func testConfigUsesHashedFileNameWithAutoExtAndProcessor() {
  179. // The key of an image with processor will be as this format.
  180. let key = "test.jpeg@com.onevcat.Kingfisher.DownsamplingImageProcessor"
  181. // hashed fileName
  182. storage.config.usesHashedFileName = true
  183. storage.config.autoExtAfterHashedFileName = true
  184. let hashedFileName = storage.cacheFileName(forKey: key)
  185. XCTAssertNotEqual(hashedFileName, key)
  186. // validation sha256 hash of the key
  187. XCTAssertEqual(hashedFileName, key.kf.sha256 + ".jpeg")
  188. // fileName without hash
  189. storage.config.usesHashedFileName = false
  190. let originalFileName = storage.cacheFileName(forKey: key)
  191. XCTAssertEqual(originalFileName, key)
  192. }
  193. func testFileMetaOrder() {
  194. let urls = [URL(string: "test1")!, URL(string: "test2")!, URL(string: "test3")!]
  195. let now = Date()
  196. let file1 = DiskStorage.FileMeta(
  197. fileURL: urls[0],
  198. lastAccessDate: now,
  199. estimatedExpirationDate: now.addingTimeInterval(1),
  200. isDirectory: false,
  201. fileSize: 1)
  202. let file2 = DiskStorage.FileMeta(
  203. fileURL: urls[1],
  204. lastAccessDate: now.addingTimeInterval(1),
  205. estimatedExpirationDate: now.addingTimeInterval(2),
  206. isDirectory: false,
  207. fileSize: 1)
  208. let file3 = DiskStorage.FileMeta(
  209. fileURL: urls[2],
  210. lastAccessDate: now.addingTimeInterval(2),
  211. estimatedExpirationDate: now.addingTimeInterval(3),
  212. isDirectory: false,
  213. fileSize: 1)
  214. let ordered = [file2, file1, file3].sorted(by: DiskStorage.FileMeta.lastAccessDate)
  215. XCTAssertTrue(ordered[0].lastAccessDate! > ordered[1].lastAccessDate!)
  216. XCTAssertTrue(ordered[1].lastAccessDate! > ordered[2].lastAccessDate!)
  217. }
  218. }