Skip to content

Commit 73c95b8

Browse files
authored
CASL-1411 bug JsonMap, add tests (#526)
Signed-off-by: phmai <phuc.mai@here.com>
1 parent 4d064c1 commit 73c95b8

File tree

3 files changed

+211
-11
lines changed

3 files changed

+211
-11
lines changed

here-naksha-lib-base/src/jvmMain/java/naksha/base/JsonMap.java

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,19 @@
55
import org.jetbrains.annotations.Nullable;
66

77
import java.util.*;
8-
9-
/// Used as internal implementation of {@link JvmMap}. Assumption is that each JSON object has only around 4 or fewer key-value pairs.
10-
/// This means each object of this class can fit into CPU L1 which makes it very fast to access.
11-
///
8+
/**
9+
* Used as internal implementation of {@link JvmMap}. Assumption is that each JSON object has only around 4 or fewer key-value pairs.
10+
* This means each object of this class can fit into CPU L1 which makes it very fast to access.
11+
* Not to be confused with the class of the same name in {@link com.here.naksha.lib.core.util.json}.
12+
*/
1213
class JsonMap implements Map<String, Object> {
1314
JsonMap(){
1415
map = EMPTY;
1516
}
1617

17-
/// The internal map representation, \[key1, value1, key2, value2,...].
18+
/**
19+
* The internal map representation, [key1, value1, key2, value2,...].
20+
*/
1821
private @NotNull Object[] map;
1922
private static final Object[] EMPTY = new Object[0];
2023

@@ -60,8 +63,9 @@ public Object put(@NotNull String key, @Nullable Object value) {
6063
var localMapCopy = this.map;
6164
for (int i = 0; i < localMapCopy.length; i+=2 ) {
6265
if( key.equals(localMapCopy[i]) ) {
63-
map[i+1] = value;
64-
return localMapCopy[i+1];
66+
var oldValue = localMapCopy[i+1];
67+
localMapCopy[i+1] = value;
68+
return oldValue;
6569
}
6670
}
6771
localMapCopy = Arrays.copyOf(localMapCopy, localMapCopy.length+2);
@@ -71,7 +75,9 @@ public Object put(@NotNull String key, @Nullable Object value) {
7175
return null;
7276
}
7377

74-
/// @return the previous value that was removed.
78+
/**
79+
* @return the previous value that was removed.
80+
*/
7581
private Object removeAt(int index) {
7682
if( index < 0 ) {
7783
return null;
@@ -106,7 +112,7 @@ public void putAll(@NotNull Map<? extends String, ?> m) {
106112
}
107113
}
108114
var newMap = Arrays.copyOf(localMapCopy, localMapCopy.length + toAdd); //Resize only once
109-
toAdd = localMapCopy.length; //Now toAdd is the index where we can add new elements
115+
toAdd = localMapCopy.length; // Reuse, now toAdd is the index where we can add new elements
110116
for ( var entry : m.entrySet() ) {
111117
final var key = entry.getKey();
112118
int index = indexOf(localMapCopy, key, 0);
@@ -137,7 +143,7 @@ public Iterator<String> iterator() {
137143
boolean canRemove = false;
138144
@Override
139145
public boolean hasNext() {
140-
return index < map.length-2;
146+
return index < map.length;
141147
}
142148

143149
@Override
@@ -177,7 +183,7 @@ public Iterator<Object> iterator() {
177183

178184
@Override
179185
public boolean hasNext() {
180-
return index < map.length-1;
186+
return index < map.length;
181187
}
182188

183189
@Override
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
package naksha.base;
2+
3+
import org.junit.jupiter.api.Test;
4+
import static org.junit.jupiter.api.Assertions.*;
5+
6+
import java.util.HashMap;
7+
import java.util.Map;
8+
import java.util.NoSuchElementException;
9+
10+
public class JsonClassesTest {
11+
12+
@Test
13+
void testEmptyMap() {
14+
JsonMap map = new JsonMap();
15+
assertTrue(map.isEmpty());
16+
assertEquals(0, map.size());
17+
}
18+
19+
@Test
20+
void testPutAndGet() {
21+
JsonMap map = new JsonMap();
22+
assertNull(map.put("key1", "value1"));
23+
assertEquals("value1", map.get("key1"));
24+
assertEquals(1, map.size());
25+
26+
assertEquals("value1", map.put("key1", "value2"));
27+
assertEquals("value2", map.get("key1"));
28+
}
29+
30+
@Test
31+
void testContainsKeyAndValue() {
32+
JsonMap map = new JsonMap();
33+
map.put("key1", "value1");
34+
map.put("key2", "value2");
35+
36+
assertTrue(map.containsKey("key1"));
37+
assertTrue(map.containsKey("key2"));
38+
assertFalse(map.containsKey("key3"));
39+
40+
assertTrue(map.containsValue("value1"));
41+
assertTrue(map.containsValue("value2"));
42+
assertFalse(map.containsValue("value3"));
43+
}
44+
45+
@Test
46+
void testRemove() {
47+
JsonMap map = new JsonMap();
48+
map.put("key1", "value1");
49+
map.put("key2", "value2");
50+
51+
assertEquals("value1", map.remove("key1"));
52+
assertNull(map.get("key1"));
53+
assertEquals(1, map.size());
54+
55+
assertNull(map.remove("nonexistent"));
56+
}
57+
58+
@Test
59+
void testPutAll() {
60+
JsonMap map = new JsonMap();
61+
Map<String, Object> source = new HashMap<>();
62+
source.put("key1", "value1");
63+
source.put("key2", "value2");
64+
65+
map.putAll(source);
66+
assertEquals(2, map.size());
67+
assertEquals("value1", map.get("key1"));
68+
assertEquals("value2", map.get("key2"));
69+
}
70+
71+
@Test
72+
void testClear() {
73+
JsonMap map = new JsonMap();
74+
map.put("key1", "value1");
75+
map.put("key2", "value2");
76+
77+
assertFalse(map.isEmpty());
78+
map.clear();
79+
assertTrue(map.isEmpty());
80+
assertEquals(0, map.size());
81+
}
82+
83+
@Test
84+
void testKeySet() {
85+
JsonMap map = new JsonMap();
86+
map.put("key1", "value1");
87+
map.put("key2", "value2");
88+
89+
var keys = map.keySet();
90+
assertEquals(2, keys.size());
91+
assertTrue(keys.contains("key1"));
92+
assertTrue(keys.contains("key2"));
93+
94+
// Test iterator
95+
var iterator = keys.iterator();
96+
assertTrue(iterator.hasNext());
97+
assertNotNull(iterator.next());
98+
assertTrue(iterator.hasNext());
99+
assertNotNull(iterator.next());
100+
assertFalse(iterator.hasNext());
101+
}
102+
103+
@Test
104+
void testValues() {
105+
JsonMap map = new JsonMap();
106+
map.put("key1", "value1");
107+
map.put("key2", "value2");
108+
109+
var values = map.values();
110+
assertEquals(2, values.size());
111+
assertTrue(values.contains("value1"));
112+
assertTrue(values.contains("value2"));
113+
114+
// Test iterator
115+
var iterator = values.iterator();
116+
assertTrue(iterator.hasNext());
117+
assertNotNull(iterator.next());
118+
assertTrue(iterator.hasNext());
119+
assertNotNull(iterator.next());
120+
assertFalse(iterator.hasNext());
121+
}
122+
123+
@Test
124+
void testEntrySet() {
125+
JsonMap map = new JsonMap();
126+
map.put("key1", "value1");
127+
map.put("key2", "value2");
128+
129+
var entries = map.entrySet();
130+
assertEquals(2, entries.size());
131+
132+
// Test iterator and entry operations
133+
var iterator = entries.iterator();
134+
assertTrue(iterator.hasNext());
135+
var entry = iterator.next();
136+
assertNotNull(entry);
137+
assertTrue(entry.getKey().equals("key1") || entry.getKey().equals("key2"));
138+
String oldValue = (String) entry.getValue();
139+
assertEquals(oldValue, entry.setValue("newValue"));
140+
assertEquals("newValue", map.get(entry.getKey()));
141+
}
142+
143+
@Test
144+
void testIteratorRemove() {
145+
JsonMap map = new JsonMap();
146+
map.put("key1", "value1");
147+
map.put("key2", "value2");
148+
149+
// Test keySet iterator remove
150+
var keyIterator = map.keySet().iterator();
151+
keyIterator.next();
152+
keyIterator.remove();
153+
assertEquals(1, map.size());
154+
155+
// Test values iterator remove
156+
map.put("key3", "value3");
157+
var valueIterator = map.values().iterator();
158+
valueIterator.next();
159+
valueIterator.remove();
160+
assertEquals(1, map.size());
161+
162+
// Test entrySet iterator remove
163+
map.put("key4", "value4");
164+
var entryIterator = map.entrySet().iterator();
165+
entryIterator.next();
166+
entryIterator.remove();
167+
assertEquals(1, map.size());
168+
}
169+
170+
@Test
171+
void testIteratorExceptions() {
172+
JsonMap map = new JsonMap();
173+
174+
// Test empty iterators
175+
var keyIterator = map.keySet().iterator();
176+
assertFalse(keyIterator.hasNext());
177+
assertThrows(NoSuchElementException.class, keyIterator::next);
178+
179+
var valueIterator = map.values().iterator();
180+
assertFalse(valueIterator.hasNext());
181+
assertThrows(NoSuchElementException.class, valueIterator::next);
182+
183+
var entryIterator = map.entrySet().iterator();
184+
assertFalse(entryIterator.hasNext());
185+
assertThrows(NoSuchElementException.class, entryIterator::next);
186+
187+
// Test illegal remove
188+
map.put("key1", "value1");
189+
keyIterator = map.keySet().iterator();
190+
assertThrows(IllegalStateException.class, keyIterator::remove);
191+
}
192+
}

here-naksha-lib-core/src/jvmMain/java/com/here/naksha/lib/core/util/json/JsonMap.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@
4848
* A map that uses {@link String} key and arbitrary values. The map is thread safe for concurrent
4949
* access. All keys are deduplicated as intrinsic feature of the map. This reduces the memory
5050
* consumption when many instance with the same keys are used.
51+
* <p>
52+
* Not to be confused with the class of the same name in {@link naksha.base}.
5153
*
5254
* @since 2.0.0
5355
*/

0 commit comments

Comments
 (0)