Skip to content

Commit d6d6325

Browse files
authoredJul 7, 2023
Leif/2.0.0 (#12)
* Update with changes for 2.0.0 * Remove unused workflow * Update PropertyWrappersTests * Remove ComposableCache from windows * Remove AnyCacheable from windows * Fix test to handle windows * Update test case * Add Logger typealias and Logging wrapper (#11) * Update README
1 parent d9be5df commit d6d6325

13 files changed

+1006
-64
lines changed
 

‎README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ Add the following line to your `Package.swift` file in the dependencies array:
2222

2323
```swift
2424
dependencies: [
25-
.package(url: "https://github.com/0xLeif/Cache.git", from: "1.0.0")
25+
.package(url: "https://github.com/0xLeif/Cache.git", from: "2.0.0")
2626
]
2727
```
2828

+153
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
#if !os(Windows)
2+
public class AnyCacheable: Cacheable {
3+
public typealias Key = AnyHashable
4+
public typealias Value = Any
5+
6+
private var cache: any Cacheable
7+
8+
private var cacheGet: ((AnyHashable) -> Any?)!
9+
private var cacheResolve: ((AnyHashable) throws -> Any)!
10+
private var cacheSet: ((Any, AnyHashable) -> Void)!
11+
private var cacheRemove: ((AnyHashable) -> Void)!
12+
private var cacheContains: ((AnyHashable) -> Bool)!
13+
private var cacheRequireKeys: ((Set<AnyHashable>) throws -> Void)!
14+
private var cacheRequireKey: ((AnyHashable) throws -> Void)!
15+
private var cacheValues: (() -> [AnyHashable: Any])!
16+
17+
public init<InnerCache: Cacheable>(_ cache: InnerCache) {
18+
self.cache = cache
19+
20+
self.cacheGet = { key in
21+
guard let key = key as? InnerCache.Key else { return nil }
22+
23+
return cache.get(key, as: Any.self)
24+
}
25+
26+
self.cacheResolve = { key in
27+
guard
28+
let key = key as? InnerCache.Key
29+
else { throw MissingRequiredKeysError(keys: [key]) }
30+
31+
return try cache.resolve(key, as: Any.self)
32+
}
33+
34+
self.cacheSet = { value, key in
35+
guard
36+
let value = value as? InnerCache.Value,
37+
let key = key as? InnerCache.Key
38+
else { return }
39+
40+
var mutableCache = cache
41+
42+
mutableCache.set(value: value, forKey: key)
43+
44+
self.cache = mutableCache
45+
}
46+
47+
self.cacheRemove = { key in
48+
guard let key = key as? InnerCache.Key else { return }
49+
50+
var mutableCache = cache
51+
52+
mutableCache.remove(key)
53+
54+
self.cache = mutableCache
55+
}
56+
57+
self.cacheContains = { key in
58+
guard
59+
let key = key as? InnerCache.Key
60+
else { return false }
61+
62+
return cache.contains(key)
63+
}
64+
65+
self.cacheRequireKeys = { keys in
66+
let validKeys: Set<InnerCache.Key> = Set(keys.compactMap { $0 as? InnerCache.Key })
67+
68+
guard
69+
validKeys.count == keys.count
70+
else { throw MissingRequiredKeysError(keys: keys.subtracting(validKeys)) }
71+
72+
_ = try cache.require(keys: validKeys)
73+
}
74+
75+
self.cacheRequireKey = { key in
76+
guard
77+
let key = key as? InnerCache.Key
78+
else { throw MissingRequiredKeysError(keys: [key]) }
79+
80+
_ = try cache.require(key)
81+
}
82+
83+
self.cacheValues = {
84+
cache.values(ofType: Any.self)
85+
}
86+
}
87+
88+
required public convenience init(initialValues: [AnyHashable: Any]) {
89+
self.init(Cache(initialValues: initialValues))
90+
}
91+
92+
public func get<Output>(
93+
_ key: AnyHashable,
94+
as: Output.Type = Output.self
95+
) -> Output? {
96+
guard let value = cacheGet(key) else {
97+
return nil
98+
}
99+
100+
guard let output = value as? Output else {
101+
return nil
102+
}
103+
104+
return output
105+
}
106+
107+
public func resolve<Output>(
108+
_ key: AnyHashable,
109+
as: Output.Type = Output.self
110+
) throws -> Output {
111+
let resolvedValue = try cacheResolve(key)
112+
113+
guard let output = resolvedValue as? Output else {
114+
throw InvalidTypeError(
115+
expectedType: Output.self,
116+
actualType: type(of: get(key, as: Any.self))
117+
)
118+
}
119+
120+
return output
121+
}
122+
123+
public func set(value: Value, forKey key: AnyHashable) {
124+
cacheSet(value, key)
125+
}
126+
127+
public func remove(_ key: AnyHashable) {
128+
cacheRemove(key)
129+
}
130+
131+
public func contains(_ key: AnyHashable) -> Bool {
132+
cacheContains(key)
133+
}
134+
135+
public func require(keys: Set<AnyHashable>) throws -> Self {
136+
try cacheRequireKeys(keys)
137+
138+
return self
139+
}
140+
141+
public func require(_ key: AnyHashable) throws -> Self {
142+
try cacheRequireKey(key)
143+
144+
return self
145+
}
146+
147+
public func values<Output>(ofType: Output.Type) -> [AnyHashable: Output] {
148+
cacheValues().compactMapValues { value in
149+
value as? Output
150+
}
151+
}
152+
}
153+
#endif

‎Sources/Cache/Cache/Cache.swift

+25
Original file line numberDiff line numberDiff line change
@@ -162,3 +162,28 @@ open class Cache<Key: Hashable, Value>: Cacheable {
162162
#if !os(Linux) && !os(Windows)
163163
extension Cache: ObservableObject { }
164164
#endif
165+
166+
extension Cache {
167+
/**
168+
Gets a value from the cache for a given key.
169+
170+
- Parameters:
171+
- key: The key to retrieve the value for.
172+
- Returns: The value stored in cache for the given key, or `nil` if it doesn't exist.
173+
*/
174+
public func get(_ key: Key) -> Value? {
175+
get(key, as: Value.self)
176+
}
177+
178+
/**
179+
Resolves a value from the cache for a given key.
180+
181+
- Parameters:
182+
- key: The key to retrieve the value for.
183+
- Returns: The value stored in cache for the given key.
184+
- Throws: `MissingRequiredKeysError` if the key is missing, or `InvalidTypeError` if the value type is not compatible with the expected type.
185+
*/
186+
public func resolve(_ key: Key) throws -> Value {
187+
try resolve(key, as: Value.self)
188+
}
189+
}
+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
#if !os(Windows)
2+
public struct ComposableCache<Key: Hashable>: Cacheable {
3+
private let caches: [AnyCacheable]
4+
5+
public init(caches: [any Cacheable]) {
6+
self.caches = caches.map { AnyCacheable($0) }
7+
}
8+
9+
public init(initialValues: [Key: Any]) {
10+
self.init(caches: [Cache(initialValues: initialValues)])
11+
}
12+
13+
public func get<Output>(
14+
_ key: Key,
15+
as: Output.Type = Output.self
16+
) -> Output? {
17+
for cache in caches {
18+
guard
19+
let output = cache.get(key, as: Output.self)
20+
else {
21+
continue
22+
}
23+
24+
return output
25+
}
26+
27+
return nil
28+
}
29+
30+
public func resolve<Output>(
31+
_ key: Key,
32+
as: Output.Type = Output.self
33+
) throws -> Output {
34+
for cache in caches {
35+
guard
36+
let output = try? cache.resolve(key, as: Output.self)
37+
else {
38+
continue
39+
}
40+
41+
return output
42+
}
43+
44+
throw MissingRequiredKeysError(keys: [key])
45+
}
46+
47+
public func set(value: Any, forKey key: Key) {
48+
for cache in caches {
49+
cache.set(value: value, forKey: key)
50+
}
51+
}
52+
53+
public func remove(_ key: Key) {
54+
for cache in caches {
55+
cache.remove(key)
56+
}
57+
}
58+
59+
public func contains(_ key: Key) -> Bool {
60+
for cache in caches {
61+
if cache.contains(key) {
62+
return true
63+
}
64+
}
65+
66+
return false
67+
}
68+
69+
public func require(keys: Set<Key>) throws -> ComposableCache<Key> {
70+
for cache in caches {
71+
_ = try cache.require(keys: keys)
72+
}
73+
74+
return self
75+
}
76+
77+
public func require(_ key: Key) throws -> ComposableCache<Key> {
78+
for cache in caches {
79+
_ = try cache.require(key)
80+
}
81+
82+
return self
83+
}
84+
85+
public func values<Output>(ofType: Output.Type) -> [Key: Output] {
86+
for cache in caches {
87+
let values = cache.values(ofType: Output.self).compactMapKeys { $0 as? Key }
88+
89+
guard values.keys.count != 0 else {
90+
continue
91+
}
92+
93+
return values
94+
}
95+
96+
return [:]
97+
}
98+
}
99+
#endif

‎Sources/Cache/Cache/RequiredKeysCache.swift

+3-8
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,9 @@ public class RequiredKeysCache<Key: Hashable, Value>: Cache<Key, Value> {
6565
- Throws: A runtime error if the required key is not present in the cache or if the expected value type is incorrect.
6666
*/
6767
public func resolve<Output>(requiredKey: Key, as: Output.Type = Output.self) -> Output {
68-
guard
69-
requiredKeys.contains(requiredKey)
70-
else { fatalError("The key '\(requiredKey)' is not a Required Key.") }
71-
72-
guard
73-
contains(requiredKey)
74-
else { fatalError("Required Key Missing: '\(requiredKey)'") }
75-
68+
precondition(requiredKeys.contains(requiredKey), "The key '\(requiredKey)' is not a Required Key.")
69+
precondition(contains(requiredKey), "Required Key Missing: '\(requiredKey)'")
70+
7671
do {
7772
return try resolve(requiredKey, as: Output.self)
7873
}
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
public protocol CacheInitializable {
2+
associatedtype OriginCache: Cacheable
3+
4+
init(cache: OriginCache)
5+
}

‎Sources/Cache/Cacheable.swift

-23
Original file line numberDiff line numberDiff line change
@@ -70,27 +70,4 @@ public extension Cacheable {
7070
var allValues: [Key: Value] {
7171
values(ofType: Value.self)
7272
}
73-
74-
/**
75-
Gets a value from the cache for a given key.
76-
77-
- Parameters:
78-
- key: The key to retrieve the value for.
79-
- Returns: The value stored in cache for the given key, or `nil` if it doesn't exist.
80-
*/
81-
func get(_ key: Key) -> Value? {
82-
get(key, as: Value.self)
83-
}
84-
85-
/**
86-
Resolves a value from the cache for a given key.
87-
88-
- Parameters:
89-
- key: The key to retrieve the value for.
90-
- Returns: The value stored in cache for the given key.
91-
- Throws: `MissingRequiredKeysError` if the key is missing, or `InvalidTypeError` if the value type is not compatible with the expected type.
92-
*/
93-
func resolve(_ key: Key) throws -> Value {
94-
try resolve(key, as: Value.self)
95-
}
9673
}

‎Sources/Cache/PropertyWrappers/Cached.swift

+8-13
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@
2222

2323
- Note: The `Cached` property wrapper relies on a cache instance that conforms to the `Cache` protocol, in order to retrieve and store the values efficiently.
2424
*/
25-
@propertyWrapper public struct Cached<Key: Hashable, Value> {
25+
@propertyWrapper public struct Cached<Key: Hashable, Value, CacheSource: Cacheable> where CacheSource.Key == Key, CacheSource.Value == Any {
2626
/// The key associated with the value in the cache.
2727
public let key: Key
2828

2929
/// The cache instance to retrieve the value from.
30-
public let cache: Cache<Key, Any>
30+
public var cache: CacheSource
3131

3232
/// The default value to be used if the value is not present in the cache.
3333
public let defaultValue: Value
@@ -42,42 +42,37 @@
4242
}
4343
}
4444

45-
46-
#if !os(Windows)
4745
/**
4846
Initializes a new instance of the `Cached` property wrapper.
4947

5048
- Parameters:
5149
- key: The key associated with the value in the cache.
52-
- cache: The cache instance to retrieve the value from. The default is `Global.cache`.
50+
- cache: The cache instance to retrieve the value from.
5351
- defaultValue: The default value to be used if the value is not present in the cache.
5452
*/
5553
public init(
5654
key: Key,
57-
using cache: Cache<Key, Any> = Global.cache,
55+
using cache: CacheSource,
5856
defaultValue: Value
5957
) {
6058
self.key = key
6159
self.cache = cache
6260
self.defaultValue = defaultValue
6361
}
64-
#else
62+
6563
/**
66-
Initializes a new instance of the `Cached` property wrapper.
64+
Initializes a new instance of the `Cached` property wrapper using the`Global.cache`.
6765

6866
- Parameters:
6967
- key: The key associated with the value in the cache.
70-
- cache: The cache instance to retrieve the value from.
7168
- defaultValue: The default value to be used if the value is not present in the cache.
7269
*/
7370
public init(
7471
key: Key,
75-
using cache: Cache<Key, Any>,
7672
defaultValue: Value
77-
) {
73+
) where CacheSource == Cache<AnyHashable, Any> {
7874
self.key = key
79-
self.cache = cache
75+
self.cache = Global.cache
8076
self.defaultValue = defaultValue
8177
}
82-
#endif
8378
}

‎Sources/Cache/PropertyWrappers/OptionallyCached.swift

+11-15
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,13 @@
2525
- Note: The `OptionallyCached` property wrapper relies on a cache instance that conforms to the `Cache` protocol, in order to retrieve and store the values efficiently.
2626

2727
*/
28-
@propertyWrapper public struct OptionallyCached<Key: Hashable, Value> {
28+
@propertyWrapper public struct OptionallyCached<Key: Hashable, Value, CacheSource: Cacheable> where CacheSource.Key == Key, CacheSource.Value == Any {
2929
/// The key associated with the value in the cache.
3030
public let key: Key
3131

3232
/// The cache instance to retrieve the value from.
33-
public let cache: Cache<Key, Any>
34-
33+
public var cache: CacheSource
34+
3535
/// The wrapped value that can be accessed and mutated by the property wrapper.
3636
public var wrappedValue: Value? {
3737
get {
@@ -45,36 +45,32 @@
4545
cache.set(value: newValue, forKey: key)
4646
}
4747
}
48-
49-
#if !os(Windows)
48+
5049
/**
5150
Initializes a new instance of the `OptionallyCached` property wrapper.
5251

5352
- Parameters:
5453
- key: The key associated with the value in the cache.
55-
- cache: The cache instance to retrieve the value from. The default is `Global.cache`.
54+
- cache: The cache instance to retrieve the value from.
5655
*/
5756
public init(
5857
key: Key,
59-
using cache: Cache<Key, Any> = Global.cache
58+
using cache: CacheSource
6059
) {
6160
self.key = key
6261
self.cache = cache
6362
}
64-
#else
63+
6564
/**
66-
Initializes a new instance of the `OptionallyCached` property wrapper.
65+
Initializes a new instance of the `OptionallyCached` property wrapper using the`Global.cache`
6766

6867
- Parameters:
6968
- key: The key associated with the value in the cache.
70-
- cache: The cache instance to retrieve the value from.
7169
*/
7270
public init(
73-
key: Key,
74-
using cache: Cache<Key, Any>
75-
) {
71+
key: Key
72+
) where CacheSource == Cache<AnyHashable, Any> {
7673
self.key = key
77-
self.cache = cache
74+
self.cache = Global.cache
7875
}
79-
#endif
8076
}
+236
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
#if !os(Windows)
2+
import XCTest
3+
@testable import Cache
4+
5+
final class AnyCacheableTests: XCTestCase {
6+
func testAllValues() {
7+
enum Key {
8+
case text
9+
}
10+
11+
let cacheable: AnyCacheable = AnyCacheable(
12+
initialValues: [
13+
Key.text: "Hello, World!"
14+
]
15+
)
16+
17+
XCTAssertEqual(cacheable.allValues.count, 1)
18+
}
19+
20+
func testGet_Success() {
21+
enum Key {
22+
case text
23+
}
24+
25+
let cache: Cache<Key, String> = Cache(
26+
initialValues: [
27+
.text: "Hello, World!"
28+
]
29+
)
30+
31+
let cacheable: AnyCacheable = AnyCacheable(cache)
32+
33+
XCTAssertEqual(cacheable.get(Key.text, as: String.self), "Hello, World!")
34+
}
35+
36+
func testGet_MissingKey() {
37+
enum Key {
38+
case text
39+
case missingKey
40+
}
41+
42+
let cache: Cache<Key, String> = Cache(
43+
initialValues: [
44+
.text: "Hello, World!"
45+
]
46+
)
47+
48+
let cacheable: AnyCacheable = AnyCacheable(cache)
49+
50+
let missingKeyValue: Any? = cacheable.get(Key.missingKey)
51+
52+
XCTAssertNil(missingKeyValue)
53+
}
54+
55+
func testGet_InvalidType() {
56+
enum Key {
57+
case text
58+
}
59+
60+
let cache: Cache<Key, String> = Cache(
61+
initialValues: [
62+
.text: "Hello, World!"
63+
]
64+
)
65+
66+
let cacheable: AnyCacheable = AnyCacheable(cache)
67+
68+
XCTAssertNil(cacheable.get(Key.text, as: Int.self))
69+
}
70+
71+
func testResolve_Success() throws {
72+
enum Key: String {
73+
case text
74+
}
75+
76+
let cache: Cache<Key, String> = Cache(
77+
initialValues: [
78+
.text: "Hello, World!"
79+
]
80+
)
81+
82+
let cacheable: AnyCacheable = AnyCacheable(cache)
83+
84+
XCTAssertEqual(try cacheable.resolve(Key.text), "Hello, World!")
85+
}
86+
87+
func testResolve_MissingKey() throws {
88+
enum Key {
89+
case text
90+
case missingKey
91+
}
92+
93+
let cache: Cache<Key, String> = Cache(
94+
initialValues: [
95+
.text: "Hello, World!"
96+
]
97+
)
98+
99+
let cacheable: AnyCacheable = AnyCacheable(cache)
100+
101+
XCTAssertThrowsError(try cacheable.resolve(Key.missingKey, as: Any.self))
102+
}
103+
104+
func testResolve_InvalidType() throws {
105+
enum Key {
106+
case text
107+
}
108+
109+
let cache: Cache<Key, String> = Cache(
110+
initialValues: [
111+
.text: "Hello, World!"
112+
]
113+
)
114+
115+
let cacheable: AnyCacheable = AnyCacheable(cache)
116+
117+
XCTAssertThrowsError(try cacheable.resolve(Key.text, as: Int.self))
118+
}
119+
120+
func testSet() {
121+
enum Key {
122+
case text
123+
}
124+
125+
let cache: Cache<Key, String> = Cache()
126+
127+
let cacheable: AnyCacheable = AnyCacheable(cache)
128+
129+
cacheable.set(value: "Hello, World!", forKey: Key.text)
130+
131+
XCTAssertEqual(cacheable.get(Key.text), "Hello, World!")
132+
}
133+
134+
func testRemove() {
135+
enum Key {
136+
case text
137+
}
138+
139+
let cache: Cache<Key, String> = Cache(
140+
initialValues: [
141+
.text: "Hello, World!"
142+
]
143+
)
144+
145+
let cacheable: AnyCacheable = AnyCacheable(cache)
146+
147+
XCTAssertEqual(cacheable.get(Key.text), "Hello, World!")
148+
149+
cacheable.remove(Key.text)
150+
151+
XCTAssertNil(cacheable.get(Key.text))
152+
}
153+
154+
func testContains() {
155+
enum Key {
156+
case text
157+
}
158+
159+
let cache: Cache<Key, String> = Cache(
160+
initialValues: [
161+
.text: "Hello, World!"
162+
]
163+
)
164+
165+
let cacheable: AnyCacheable = AnyCacheable(cache)
166+
167+
XCTAssert(cacheable.contains(Key.text))
168+
}
169+
170+
func testRequire_Success() throws {
171+
enum Key {
172+
case text
173+
}
174+
175+
let cache: Cache<Key, String> = Cache(
176+
initialValues: [
177+
.text: "Hello, World!"
178+
]
179+
)
180+
181+
let cacheable: AnyCacheable = AnyCacheable(cache)
182+
183+
XCTAssertNoThrow(try cacheable.require(Key.text))
184+
}
185+
186+
func testRequire_Missing() throws {
187+
enum Key {
188+
case text
189+
case missingKey
190+
}
191+
192+
let cache: Cache<Key, String> = Cache(
193+
initialValues: [
194+
.text: "Hello, World!"
195+
]
196+
)
197+
198+
let cacheable: AnyCacheable = AnyCacheable(cache)
199+
200+
XCTAssertThrowsError(try cacheable.require(Key.missingKey))
201+
}
202+
203+
func testRequireSet_Success() throws {
204+
enum Key {
205+
case text
206+
}
207+
208+
let cache: Cache<Key, String> = Cache(
209+
initialValues: [
210+
.text: "Hello, World!"
211+
]
212+
)
213+
214+
let cacheable: AnyCacheable = AnyCacheable(cache)
215+
216+
XCTAssertNoThrow(try cacheable.require(keys: [Key.text]))
217+
}
218+
219+
func testRequireSet_Missing() throws {
220+
enum Key {
221+
case text
222+
case missingKey
223+
}
224+
225+
let cache: Cache<Key, String> = Cache(
226+
initialValues: [
227+
.text: "Hello, World!"
228+
]
229+
)
230+
231+
let cacheable: AnyCacheable = AnyCacheable(cache)
232+
233+
XCTAssertThrowsError(try cacheable.require(keys: [Key.text, Key.missingKey]))
234+
}
235+
}
236+
#endif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import XCTest
2+
@testable import Cache
3+
4+
final class CacheInitializableTests: XCTestCase {
5+
class TestCache: Cache<TestCache.Key, Any> {
6+
enum Key {
7+
case text
8+
}
9+
}
10+
11+
struct CacheInitializableObject: CacheInitializable {
12+
let text: String?
13+
14+
init(cache: TestCache) {
15+
text = cache.get(.text)
16+
}
17+
}
18+
19+
func testCacheInitializableEmpty() {
20+
let cache = TestCache()
21+
22+
let object = CacheInitializableObject(cache: cache)
23+
24+
XCTAssertNil(object.text)
25+
}
26+
27+
func testCacheInitializable() {
28+
let cache = TestCache(
29+
initialValues: [
30+
.text: "Hello, World!"
31+
]
32+
)
33+
34+
let object = CacheInitializableObject(cache: cache)
35+
36+
XCTAssertNotNil(object.text)
37+
}
38+
}
+349
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,349 @@
1+
#if !os(Windows)
2+
import XCTest
3+
@testable import Cache
4+
5+
final class ComposableCacheTests: XCTestCase {
6+
func testComplexComposableCache() {
7+
enum Key {
8+
case a
9+
case b
10+
case c
11+
case d
12+
}
13+
14+
let lruCache: LRUCache<Key, Any> = LRUCache(capacity: 3)
15+
let expiringCache: ExpiringCache<Key, Any> = ExpiringCache(duration: .seconds(1))
16+
17+
let cache: ComposableCache = ComposableCache<Key>(
18+
caches: [
19+
lruCache, // First Cache
20+
// ... // Other Caches
21+
expiringCache // Last Cache
22+
]
23+
)
24+
25+
XCTAssertNil(cache.get(.a))
26+
27+
cache.set(value: "Hello, A!", forKey: .a)
28+
29+
XCTAssertNotNil(cache.get(.a))
30+
XCTAssertNotNil(lruCache.get(.a))
31+
XCTAssertNotNil(expiringCache.get(.a))
32+
33+
cache.set(value: "Hello, B!", forKey: .b)
34+
cache.set(value: "Hello, C!", forKey: .c)
35+
cache.set(value: "Hello, D!", forKey: .d)
36+
37+
XCTAssertNil(lruCache.get(.a))
38+
XCTAssertNotNil(cache.get(.a))
39+
XCTAssertNotNil(expiringCache.get(.a))
40+
41+
XCTAssertNotNil(cache.get(.b))
42+
XCTAssertNotNil(cache.get(.c))
43+
XCTAssertNotNil(cache.get(.d))
44+
45+
sleep(1)
46+
47+
// Check ComposableCache
48+
49+
XCTAssertNil(cache.get(.a))
50+
XCTAssertNotNil(cache.get(.b))
51+
XCTAssertNotNil(cache.get(.c))
52+
XCTAssertNotNil(cache.get(.d))
53+
54+
// Check LRUCache
55+
56+
XCTAssertNil(lruCache.get(.a))
57+
XCTAssertNotNil(lruCache.get(.b))
58+
XCTAssertNotNil(lruCache.get(.c))
59+
XCTAssertNotNil(lruCache.get(.d))
60+
61+
// Check ExpiringCache
62+
63+
XCTAssertNil(expiringCache.get(.a))
64+
XCTAssertNil(expiringCache.get(.b))
65+
XCTAssertNil(expiringCache.get(.c))
66+
XCTAssertNil(expiringCache.get(.d))
67+
}
68+
69+
func testAllValues() {
70+
enum Key {
71+
case text
72+
}
73+
74+
let cache: ComposableCache = ComposableCache<Key>(
75+
initialValues: [
76+
.text: "Hello, World!"
77+
]
78+
)
79+
80+
XCTAssertEqual(cache.allValues.count, 1)
81+
}
82+
83+
func testAllValues_None() {
84+
enum Key {
85+
case text
86+
}
87+
88+
let cache: ComposableCache = ComposableCache<Key>(
89+
initialValues: [:]
90+
)
91+
92+
XCTAssertEqual(cache.allValues.count, 0)
93+
}
94+
95+
func testGet_Success() {
96+
enum Key {
97+
case text
98+
}
99+
100+
let cache: ComposableCache = ComposableCache<Key>(
101+
caches: [
102+
Cache<Key, String>(
103+
initialValues: [
104+
.text: "Hello, World!"
105+
]
106+
)
107+
]
108+
)
109+
110+
XCTAssertEqual(cache.get(.text), "Hello, World!")
111+
}
112+
113+
func testGet_MissingKey() {
114+
enum Key {
115+
case text
116+
case missingKey
117+
}
118+
119+
let cache: ComposableCache = ComposableCache<Key>(
120+
caches: [
121+
Cache<Key, String>(
122+
initialValues: [
123+
.text: "Hello, World!"
124+
]
125+
)
126+
]
127+
)
128+
129+
XCTAssertNil(cache.get(.missingKey))
130+
}
131+
132+
func testGet_InvalidType() {
133+
enum Key {
134+
case text
135+
}
136+
137+
let cache: ComposableCache = ComposableCache<Key>(
138+
caches: [
139+
Cache<Key, String>(
140+
initialValues: [
141+
.text: "Hello, World!"
142+
]
143+
)
144+
]
145+
)
146+
147+
XCTAssertNil(cache.get(.text, as: Int.self))
148+
}
149+
150+
func testResolve_Success() throws {
151+
enum Key {
152+
case text
153+
}
154+
155+
let cache: ComposableCache = ComposableCache<Key>(
156+
caches: [
157+
Cache<Key, String>(
158+
initialValues: [
159+
.text: "Hello, World!"
160+
]
161+
)
162+
]
163+
)
164+
165+
XCTAssertEqual(try cache.resolve(.text), "Hello, World!")
166+
}
167+
168+
func testResolve_MissingKey() throws {
169+
enum Key {
170+
case text
171+
case missingKey
172+
}
173+
174+
let cache: ComposableCache = ComposableCache<Key>(
175+
caches: [
176+
Cache<Key, String>(
177+
initialValues: [
178+
.text: "Hello, World!"
179+
]
180+
)
181+
]
182+
)
183+
184+
XCTAssertThrowsError(try cache.resolve(.missingKey, as: Any.self))
185+
}
186+
187+
func testResolve_InvalidType() throws {
188+
enum Key {
189+
case text
190+
}
191+
192+
let cache: ComposableCache = ComposableCache<Key>(
193+
caches: [
194+
Cache<Key, String>(
195+
initialValues: [
196+
.text: "Hello, World!"
197+
]
198+
)
199+
]
200+
)
201+
202+
XCTAssertThrowsError(try cache.resolve(.text, as: Int.self))
203+
}
204+
205+
func testSet() {
206+
enum Key {
207+
case text
208+
}
209+
210+
let cache: ComposableCache = ComposableCache<Key>(
211+
caches: [
212+
Cache<Key, String>()
213+
]
214+
)
215+
216+
cache.set(value: "Hello, World!", forKey: .text)
217+
218+
XCTAssertEqual(cache.get(.text), "Hello, World!")
219+
}
220+
221+
func testRemove() {
222+
enum Key {
223+
case text
224+
}
225+
226+
let cache: ComposableCache = ComposableCache<Key>(
227+
caches: [
228+
Cache<Key, String>(
229+
initialValues: [
230+
.text: "Hello, World!"
231+
]
232+
)
233+
]
234+
)
235+
236+
XCTAssertEqual(cache.get(.text), "Hello, World!")
237+
238+
cache.remove(.text)
239+
240+
XCTAssertNil(cache.get(.text))
241+
}
242+
243+
func testContains_Success() {
244+
enum Key {
245+
case text
246+
}
247+
248+
let cache: ComposableCache = ComposableCache<Key>(
249+
caches: [
250+
Cache<Key, String>(
251+
initialValues: [
252+
.text: "Hello, World!"
253+
]
254+
)
255+
]
256+
)
257+
258+
XCTAssert(cache.contains(.text))
259+
}
260+
261+
func testContains_Failure() {
262+
enum Key {
263+
case text
264+
}
265+
266+
let cache: ComposableCache = ComposableCache<Key>(
267+
caches: [
268+
Cache<Key, String>()
269+
]
270+
)
271+
272+
XCTAssertFalse(cache.contains(.text))
273+
}
274+
275+
func testRequire_Success() throws {
276+
enum Key {
277+
case text
278+
}
279+
280+
let cache: ComposableCache = ComposableCache<Key>(
281+
caches: [
282+
Cache<Key, String>(
283+
initialValues: [
284+
.text: "Hello, World!"
285+
]
286+
)
287+
]
288+
)
289+
290+
XCTAssertNoThrow(try cache.require(.text))
291+
}
292+
293+
func testRequire_Missing() throws {
294+
enum Key {
295+
case text
296+
case missingKey
297+
}
298+
299+
let cache: ComposableCache = ComposableCache<Key>(
300+
caches: [
301+
Cache<Key, String>(
302+
initialValues: [
303+
.text: "Hello, World!"
304+
]
305+
)
306+
]
307+
)
308+
309+
XCTAssertThrowsError(try cache.require(.missingKey))
310+
}
311+
312+
func testRequireSet_Success() throws {
313+
enum Key {
314+
case text
315+
}
316+
317+
let cache: ComposableCache = ComposableCache<Key>(
318+
caches: [
319+
Cache<Key, String>(
320+
initialValues: [
321+
.text: "Hello, World!"
322+
]
323+
)
324+
]
325+
)
326+
327+
XCTAssertNoThrow(try cache.require(keys: [.text]))
328+
}
329+
330+
func testRequireSet_Missing() throws {
331+
enum Key {
332+
case text
333+
case missingKey
334+
}
335+
336+
let cache: ComposableCache = ComposableCache<Key>(
337+
caches: [
338+
Cache<Key, String>(
339+
initialValues: [
340+
.text: "Hello, World!"
341+
]
342+
)
343+
]
344+
)
345+
346+
XCTAssertThrowsError(try cache.require(keys: [.text, .missingKey]))
347+
}
348+
}
349+
#endif

‎Tests/CacheTests/PropertyWrappersTests.swift

+78-4
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ final class PropertyWrappersTests: XCTestCase {
88
case value
99
}
1010

11-
static let cache = Cache<Key, Any>()
11+
static let cache = ExpiringCache<Key, Any>(duration: .minutes(2))
1212

13-
@Cached(key: Key.value, using: cache, defaultValue: "no value")
13+
@Cached(key: .value, using: cache, defaultValue: "no value")
1414
var someValue: String
1515
}
1616

@@ -34,7 +34,7 @@ final class PropertyWrappersTests: XCTestCase {
3434

3535
static let cache = Cache<Key, Any>()
3636

37-
@OptionallyCached(key: Key.value, using: cache)
37+
@OptionallyCached(key: .value, using: cache)
3838
var someValue: String?
3939
}
4040

@@ -64,7 +64,7 @@ final class PropertyWrappersTests: XCTestCase {
6464

6565
static let cache = RequiredKeysCache<Key, Any>()
6666

67-
@Resolved(key: Key.value, using: cache)
67+
@Resolved(key: .value, using: cache)
6868
var someValue: String
6969
}
7070

@@ -81,4 +81,78 @@ final class PropertyWrappersTests: XCTestCase {
8181
XCTAssertEqual(object.someValue, "Hello, World")
8282
XCTAssertEqual(cachedObject.someValue, "Hello, World")
8383
}
84+
85+
func testGlobalCached() {
86+
struct CachedValueObject {
87+
enum Key {
88+
case value
89+
}
90+
91+
@Cached(key: Key.value, defaultValue: "no value")
92+
var someValue: String
93+
}
94+
95+
var object = CachedValueObject()
96+
97+
XCTAssertEqual(object.someValue, "no value")
98+
99+
object.someValue = "Hello, World"
100+
101+
let cachedObject = CachedValueObject()
102+
103+
XCTAssertEqual(object.someValue, "Hello, World")
104+
XCTAssertEqual(cachedObject.someValue, "Hello, World")
105+
}
106+
107+
func testGloballyOptionallyCached() {
108+
struct CachedValueObject {
109+
enum Key {
110+
case value
111+
}
112+
113+
@OptionallyCached(key: Key.value)
114+
var someValue: String?
115+
}
116+
117+
var object = CachedValueObject()
118+
119+
XCTAssertNil(object.someValue)
120+
121+
object.someValue = "Hello, World"
122+
123+
let cachedObject = CachedValueObject()
124+
125+
XCTAssertEqual(object.someValue, "Hello, World")
126+
XCTAssertEqual(cachedObject.someValue, "Hello, World")
127+
128+
object.someValue = nil
129+
130+
XCTAssertNil(object.someValue)
131+
XCTAssertNil(cachedObject.someValue)
132+
}
133+
134+
func testGloballyResolved() {
135+
struct CachedValueObject {
136+
enum Key {
137+
case pi
138+
case value
139+
}
140+
141+
@Resolved(key: Key.value, using: Global.dependencies)
142+
var someValue: String
143+
}
144+
145+
Global.dependencies.set(value: "init", forKey: CachedValueObject.Key.value)
146+
147+
var object = CachedValueObject()
148+
149+
XCTAssertEqual(object.someValue, "init")
150+
151+
object.someValue = "Hello, World"
152+
153+
let cachedObject = CachedValueObject()
154+
155+
XCTAssertEqual(object.someValue, "Hello, World")
156+
XCTAssertEqual(cachedObject.someValue, "Hello, World")
157+
}
84158
}

0 commit comments

Comments
 (0)
Please sign in to comment.