Skip to content

Commit f0c90a9

Browse files
LRUCache: add remove method with test cases coverages (#597)
1 parent 075cacd commit f0c90a9

File tree

2 files changed

+70
-0
lines changed

2 files changed

+70
-0
lines changed

Sources/ODP/LruCache.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,16 @@ class LruCache<K: Hashable, V> {
8989
}
9090
}
9191

92+
func remove(key: K) {
93+
if maxSize <= 0 { return }
94+
queue.async(flags: .barrier) {
95+
if var item = self.map[key] {
96+
self.removeFromLink(item)
97+
self.map[key] = nil
98+
}
99+
}
100+
}
101+
92102
// read cache contents without order update
93103
func peek(key: K) -> V? {
94104
if maxSize <= 0 { return nil }

Tests/OptimizelyTests-Common/LruCacheTests.swift

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,37 @@ class LruCacheTests: XCTestCase {
108108
XCTAssertEqual(700, cache.peek(key: 2))
109109
}
110110

111+
func testRemoveKey() {
112+
let cache = LruCache<Int, Int>(size: 3, timeoutInSecs: 1000)
113+
114+
// Save a few items
115+
cache.save(key: 1, value: 100) // [1]
116+
cache.save(key: 2, value: 200) // [1, 2]
117+
cache.save(key: 3, value: 300) // [1, 2, 3]
118+
119+
// Ensure they are saved
120+
XCTAssertEqual(cache.peek(key: 1), 100)
121+
XCTAssertEqual(cache.peek(key: 2), 200)
122+
XCTAssertEqual(cache.peek(key: 3), 300)
123+
124+
// Remove one key
125+
cache.remove(key: 2)
126+
127+
// Check it's gone
128+
XCTAssertNil(cache.peek(key: 2))
129+
XCTAssertNil(cache.lookup(key: 2))
130+
131+
// Ensure others still exist
132+
XCTAssertEqual(cache.peek(key: 1), 100)
133+
XCTAssertEqual(cache.peek(key: 3), 300)
134+
135+
// Remove non-existent key (should not crash or affect others)
136+
cache.remove(key: 999)
137+
XCTAssertEqual(cache.peek(key: 1), 100)
138+
XCTAssertEqual(cache.peek(key: 3), 300)
139+
}
140+
141+
111142
func testSize_zero() {
112143
let cache = LruCache<Int, Int>(size: 0, timeoutInSecs: 1000)
113144

@@ -132,6 +163,35 @@ class LruCacheTests: XCTestCase {
132163
}
133164
XCTAssertTrue(result, "Concurrent tasks timed out")
134165
}
166+
167+
func testRemoveIsThreadSafe() {
168+
let numThreads = 50
169+
let numIterations = 100
170+
171+
let cache = LruCache<Int, Int>(size: 100, timeoutInSecs: 1000)
172+
173+
// Pre-fill the cache with keys
174+
for i in 0..<(numThreads * numIterations) {
175+
cache.save(key: i, value: i * 10)
176+
}
177+
178+
let result = OTUtils.runConcurrent(count: numThreads, timeoutInSecs: 10) { threadIndex in
179+
let base = threadIndex * numIterations
180+
for i in 0..<numIterations {
181+
let key = base + i
182+
cache.remove(key: key)
183+
XCTAssertNil(cache.lookup(key: key))
184+
}
185+
}
186+
187+
XCTAssertTrue(result, "Concurrent removals timed out or failed")
188+
189+
// Ensure all targeted keys are removed
190+
for i in 0..<(numThreads * numIterations) {
191+
XCTAssertNil(cache.peek(key: i))
192+
}
193+
}
194+
135195

136196
}
137197

0 commit comments

Comments
 (0)