1
+ /*
2
+ * Copyright 2016-2025 JetBrains s.r.o.
3
+ * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file.
4
+ */
5
+
6
+ package tests.contract.map
7
+
8
+ import kotlinx.collections.immutable.implementations.immutableMap.PersistentHashMap
9
+ import kotlinx.collections.immutable.persistentHashMapOf
10
+ import tests.stress.IntWrapper
11
+ import kotlin.collections.iterator
12
+ import kotlin.test.Test
13
+ import kotlin.test.assertEquals
14
+ import kotlin.test.assertFailsWith
15
+ import kotlin.test.assertFalse
16
+ import kotlin.test.assertTrue
17
+
18
+ class PersistentHashMapBuilderTest {
19
+
20
+ @Test
21
+ fun `should correctly iterate after removing integer key and promotion colliding key during iteration` () {
22
+ val removedKey = 0
23
+ val map: PersistentHashMap <Int , String > =
24
+ persistentHashMapOf(1 to " a" , 2 to " b" , 3 to " c" , removedKey to " y" , 32 to " z" )
25
+ as PersistentHashMap <Int , String >
26
+
27
+ validatePromotion(map, removedKey)
28
+ }
29
+
30
+ @Test
31
+ fun `should correctly iterate after removing IntWrapper key and promotion colliding key during iteration` () {
32
+ val removedKey = IntWrapper (0 , 0 )
33
+ val map: PersistentHashMap <IntWrapper , String > = persistentHashMapOf(
34
+ removedKey to " a" ,
35
+ IntWrapper (1 , 0 ) to " b" ,
36
+ IntWrapper (2 , 32 ) to " c" ,
37
+ IntWrapper (3 , 32 ) to " d"
38
+ ) as PersistentHashMap <IntWrapper , String >
39
+
40
+ validatePromotion(map, removedKey)
41
+ }
42
+
43
+ private fun <K > validatePromotion (map : PersistentHashMap <K , * >, removedKey : K ) {
44
+ val builder = map.builder()
45
+ val iterator = builder.entries.iterator()
46
+
47
+ val expectedCount = map.size
48
+ var actualCount = 0
49
+
50
+ while (iterator.hasNext()) {
51
+ val (key, _) = iterator.next()
52
+ if (key == removedKey) {
53
+ iterator.remove()
54
+ }
55
+ actualCount++
56
+ }
57
+
58
+ val resultMap = builder.build()
59
+ for ((key, value) in map) {
60
+ if (key != removedKey) {
61
+ assertTrue(key in resultMap)
62
+ assertEquals(resultMap[key], value)
63
+ } else {
64
+ assertFalse(key in resultMap)
65
+ }
66
+ }
67
+
68
+ assertEquals(expectedCount, actualCount)
69
+ }
70
+
71
+ @Test
72
+ fun `removing twice on iterators throws IllegalStateException` () {
73
+ val map: PersistentHashMap <Int , String > =
74
+ persistentHashMapOf(1 to " a" , 2 to " b" , 3 to " c" , 0 to " y" , 32 to " z" ) as PersistentHashMap <Int , String >
75
+ val builder = map.builder()
76
+ val iterator = builder.entries.iterator()
77
+
78
+ assertFailsWith<IllegalStateException > {
79
+ while (iterator.hasNext()) {
80
+ val (key, _) = iterator.next()
81
+ if (key == 0 ) iterator.remove()
82
+ if (key == 0 ) {
83
+ iterator.remove()
84
+ iterator.remove()
85
+ }
86
+ }
87
+ }
88
+ }
89
+
90
+ @Test
91
+ fun `removing elements from different iterators throws ConcurrentModificationException` () {
92
+ val map: PersistentHashMap <Int , String > =
93
+ persistentHashMapOf(1 to " a" , 2 to " b" , 3 to " c" , 0 to " y" , 32 to " z" ) as PersistentHashMap <Int , String >
94
+ val builder = map.builder()
95
+ val iterator1 = builder.entries.iterator()
96
+ val iterator2 = builder.entries.iterator()
97
+
98
+ assertFailsWith<ConcurrentModificationException > {
99
+ while (iterator1.hasNext()) {
100
+ val (key, _) = iterator1.next()
101
+ iterator2.next()
102
+ if (key == 0 ) iterator1.remove()
103
+ if (key == 2 ) iterator2.remove()
104
+ }
105
+ }
106
+ }
107
+
108
+ @Test
109
+ fun `removing element from one iterator and accessing another throws ConcurrentModificationException` () {
110
+ val map = persistentHashMapOf(1 to " a" , 2 to " b" , 3 to " c" )
111
+ val builder = map.builder()
112
+ val iterator1 = builder.entries.iterator()
113
+ val iterator2 = builder.entries.iterator()
114
+
115
+ assertFailsWith<ConcurrentModificationException > {
116
+ iterator1.next()
117
+ iterator1.remove()
118
+ iterator2.next()
119
+ }
120
+ }
121
+ }
0 commit comments