Skip to content

Commit a5635a4

Browse files
committed
Update test case for ObjectCache to verify fix
1 parent c07c138 commit a5635a4

File tree

1 file changed

+93
-25
lines changed

1 file changed

+93
-25
lines changed

Tests/OpenSwiftUICoreTests/Data/Util/ObjectCacheTests.swift

Lines changed: 93 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,32 +7,100 @@ import Testing
77

88
struct ObjectCacheTests {
99
@Test
10-
func example() {
11-
let table = [
12-
0: "0",
13-
1: "1",
14-
2: "2",
15-
]
16-
17-
var accessCounts = [
18-
0: 0,
19-
1: 0,
20-
2: 0,
21-
]
22-
10+
func accessCount() {
11+
var accessCounts: [Int: Int] = [:]
2312
let cache: ObjectCache<Int, String> = ObjectCache { key in
24-
accessCounts[key]! += 1
25-
return table[key]!
26-
}
27-
28-
for (key, value) in table {
29-
#expect(accessCounts[key] == 0)
30-
31-
#expect(cache[key] == value)
32-
#expect(accessCounts[key] == 1)
33-
34-
#expect(cache[key] == value)
35-
#expect(accessCounts[key] == 1)
13+
accessCounts[key, default: 0] += 1
14+
return "\(key)"
3615
}
16+
17+
#expect(accessCounts[0] == nil)
18+
19+
#expect(cache[0] == "0")
20+
#expect(accessCounts[0] == 1)
21+
22+
#expect(cache[0] == "0")
23+
#expect(accessCounts[0] == 1)
24+
}
25+
26+
private struct Key: Hashable {
27+
var value: Int
28+
29+
// Intended behavior for the test case
30+
var hashValue: Int { value }
31+
32+
func hash(into hasher: inout Hasher) {
33+
// suppress warning
34+
}
35+
}
36+
37+
@Test
38+
func bucketFullEviction() {
39+
enum Count {
40+
static var deinitValue: Int?
41+
}
42+
43+
class Object {
44+
var value: Int
45+
46+
init(value: Int) {
47+
self.value = value
48+
}
49+
50+
deinit { Count.deinitValue = value }
51+
}
52+
53+
var accessCounts: [Int: Int] = [:]
54+
let cache: ObjectCache<Key, Object> = ObjectCache { key in
55+
accessCounts[key.value, default: 0] += 1
56+
return Object(value: key.value)
57+
}
58+
for key in (0 ..< 32).map(Key.init(value:)) {
59+
#expect(accessCounts[key.value] == nil)
60+
#expect(cache[key].value == key.value)
61+
#expect(accessCounts[key.value] == 1)
62+
}
63+
#expect(Count.deinitValue == nil)
64+
#if DEBUG
65+
#expect(cache.count == 32)
66+
#endif
67+
_ = cache[Key(value: 32)] // This will evict one value since the bucket is full
68+
#expect(Count.deinitValue != nil)
69+
}
70+
71+
@Test
72+
func bucketCollisionEviction() {
73+
enum Count {
74+
static var deinitOrder: [Int] = []
75+
}
76+
77+
class Object {
78+
var value: Int
79+
80+
init(value: Int) {
81+
self.value = value
82+
}
83+
84+
deinit {
85+
Count.deinitOrder.append(value)
86+
}
87+
}
88+
89+
var accessCounts: [Int: Int] = [:]
90+
let cache: ObjectCache<Key, Object> = ObjectCache { key in
91+
accessCounts[key.value, default: 0] += 1
92+
return Object(value: key.value)
93+
}
94+
for key in [0, 8, 16, 24].map(Key.init(value:)) {
95+
#expect(accessCounts[key.value] == nil)
96+
#expect(cache[key].value == key.value)
97+
#expect(accessCounts[key.value] == 1)
98+
}
99+
_ = cache[Key(value: 32)] // This will evict object for Key(value: 0)
100+
#expect(Count.deinitOrder == [0])
101+
102+
_ = cache[Key(value: 8)]
103+
_ = cache[Key(value: 40)] // This will evict object for Key(value: 16) since we have visited Key(value: 8) recently
104+
#expect(Count.deinitOrder == [0, 16])
37105
}
38106
}

0 commit comments

Comments
 (0)