Skip to content

Commit 0d98aba

Browse files
authored
Migrate tests from TagsTest to KeyValuesTest (#3525)
This commit also resolves some discrepancies between KeyValues and Tags that have been spotted by the tests. Closes gh-3502
1 parent e34b93e commit 0d98aba

File tree

2 files changed

+360
-3
lines changed

2 files changed

+360
-3
lines changed

micrometer-commons/src/main/java/io/micrometer/common/KeyValues.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ public <E> KeyValues and(@Nullable Iterable<E> elements, Function<E, String> key
132132
* @return a new {@code KeyValues} instance
133133
*/
134134
public KeyValues and(@Nullable Iterable<? extends KeyValue> keyValues) {
135-
if (keyValues == null || !keyValues.iterator().hasNext()) {
135+
if (keyValues == null || keyValues == EMPTY || !keyValues.iterator().hasNext()) {
136136
return this;
137137
}
138138

@@ -256,7 +256,7 @@ public static <E> KeyValues of(@Nullable Iterable<E> elements, Function<E, Strin
256256
* @return a new {@code KeyValues} instance
257257
*/
258258
public static KeyValues of(@Nullable Iterable<? extends KeyValue> keyValues) {
259-
if (keyValues == null || !keyValues.iterator().hasNext()) {
259+
if (keyValues == null || keyValues == EMPTY || !keyValues.iterator().hasNext()) {
260260
return KeyValues.empty();
261261
}
262262
else if (keyValues instanceof KeyValues) {

micrometer-commons/src/test/java/io/micrometer/common/KeyValuesTest.java

Lines changed: 358 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,374 @@
1616

1717
package io.micrometer.common;
1818

19-
import java.util.Map;
19+
import java.lang.management.ManagementFactory;
20+
import java.util.*;
21+
import java.util.stream.Stream;
2022

23+
import com.sun.management.ThreadMXBean;
2124
import org.junit.jupiter.api.Test;
25+
import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
26+
import org.junit.jupiter.api.condition.EnabledForJreRange;
27+
import org.junit.jupiter.api.condition.EnabledIf;
28+
import org.junit.jupiter.api.condition.JRE;
2229

2330
import static org.assertj.core.api.Assertions.assertThat;
31+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
2432

2533
/**
2634
* Tests for {@link KeyValues}.
35+
*
36+
* @author Phil Webb
37+
* @author Maciej Walkowiak
38+
* @author Jon Schneider
39+
* @author Johnny Lim
2740
*/
2841
class KeyValuesTest {
2942

43+
// Should match "Eclipse OpenJ9 VM" and "IBM J9 VM"
44+
private static final String JAVA_VM_NAME_J9_REGEX = ".*J9 VM$";
45+
46+
@Test
47+
void dedup() {
48+
assertThat(KeyValues.of("k1", "v1", "k2", "v2")).containsExactly(KeyValue.of("k1", "v1"),
49+
KeyValue.of("k2", "v2"));
50+
assertThat(KeyValues.of("k1", "v1", "k1", "v2")).containsExactly(KeyValue.of("k1", "v2"));
51+
assertThat(KeyValues.of("k1", "v1", "k1", "v2", "k3", "v3")).containsExactly(KeyValue.of("k1", "v2"),
52+
KeyValue.of("k3", "v3"));
53+
assertThat(KeyValues.of("k1", "v1", "k2", "v2", "k2", "v3")).containsExactly(KeyValue.of("k1", "v1"),
54+
KeyValue.of("k2", "v3"));
55+
}
56+
57+
@Test
58+
void stream() {
59+
KeyValues keyValues = KeyValues.of(KeyValue.of("k1", "v1"), KeyValue.of("k1", "v1"), KeyValue.of("k2", "v2"));
60+
assertThat(keyValues.stream()).hasSize(2);
61+
}
62+
63+
@Test
64+
void spliterator() {
65+
KeyValues keyValues = KeyValues.of("k1", "v1", "k2", "v2", "k3", "v4");
66+
Spliterator<KeyValue> spliterator = keyValues.spliterator();
67+
assertThat(spliterator).hasCharacteristics(Spliterator.IMMUTABLE, Spliterator.ORDERED, Spliterator.SORTED,
68+
Spliterator.DISTINCT);
69+
assertThat(spliterator.getExactSizeIfKnown()).isEqualTo(3);
70+
}
71+
72+
@Test
73+
void keyValuesHashCode() {
74+
KeyValues keyValues = KeyValues.of(KeyValue.of("k1", "v1"), KeyValue.of("k1", "v1"), KeyValue.of("k2", "v2"));
75+
KeyValues keyValues2 = KeyValues.of(KeyValue.of("k1", "v1"), KeyValue.of("k2", "v2"));
76+
assertThat(keyValues.hashCode()).isEqualTo(keyValues2.hashCode());
77+
}
78+
79+
@Test
80+
void keyValuesToString() {
81+
KeyValues keyValues = KeyValues.of(KeyValue.of("k1", "v1"), KeyValue.of("k1", "v1"), KeyValue.of("k2", "v2"));
82+
assertThat(keyValues.toString()).isEqualTo("[keyValue(k1=v1),keyValue(k2=v2)]");
83+
}
84+
85+
@Test
86+
void keyValuesEquality() {
87+
KeyValues keyValues = KeyValues.of(KeyValue.of("k1", "v1"), KeyValue.of("k1", "v1"), KeyValue.of("k2", "v2"));
88+
KeyValues keyValues2 = KeyValues.of(KeyValue.of("k1", "v1"), KeyValue.of("k2", "v2"));
89+
assertThat(keyValues).isEqualTo(keyValues2);
90+
}
91+
92+
@Test
93+
void createsListWithSingleKeyValue() {
94+
Iterable<KeyValue> keyValues = KeyValues.of("k1", "v1");
95+
assertThat(keyValues).containsExactly(KeyValue.of("k1", "v1"));
96+
}
97+
98+
@Test
99+
void nullKeyValueIterableShouldProduceEmptyKeyValues() {
100+
assertThat(KeyValues.of((Iterable<KeyValue>) null)).isSameAs(KeyValues.empty());
101+
}
102+
103+
@Test
104+
void nullKeyValueStringArrayShouldProduceEmptyKeyValues() {
105+
assertThat(KeyValues.of((String[]) null)).isSameAs(KeyValues.empty());
106+
}
107+
108+
@Test
109+
void nullKeyValueArrayShouldProduceEmptyKeyValues() {
110+
assertThat(KeyValues.of((KeyValue[]) null)).isSameAs(KeyValues.empty());
111+
}
112+
113+
@Test
114+
void emptyKeyValueIterableShouldProduceEmptyKeyValues() {
115+
assertThat(KeyValues.of(new ArrayList<>())).isSameAs(KeyValues.empty());
116+
}
117+
118+
@Test
119+
void emptyKeyValueStringArrayShouldProduceEmptyKeyValues() {
120+
String[] emptyStrings = {};
121+
assertThat(KeyValues.of(emptyStrings)).isSameAs(KeyValues.empty());
122+
}
123+
124+
@Test
125+
void emptyKeyValueArrayShouldProduceEmptyKeyValues() {
126+
KeyValue[] emptyKeyValues = {};
127+
assertThat(KeyValues.of(emptyKeyValues)).isSameAs(KeyValues.empty());
128+
}
129+
130+
@Test
131+
void concatOnTwoKeyValuesWithSameKeyAreMergedIntoOneKeyValue() {
132+
Iterable<KeyValue> keyValues = KeyValues.concat(KeyValues.of("k", "v1"), "k", "v2");
133+
assertThat(keyValues).containsExactly(KeyValue.of("k", "v2"));
134+
}
135+
136+
@Test
137+
void zipOnTwoKeyValuesWithSameKeyAreMergedIntoOneKeyValue() {
138+
Iterable<KeyValue> keyValues = KeyValues.of("k", "v1", "k", "v2");
139+
assertThat(keyValues).containsExactly(KeyValue.of("k", "v2"));
140+
}
141+
142+
@Test
143+
void andKeyValueShouldReturnNewInstanceWithAddedKeyValues() {
144+
KeyValues source = KeyValues.of("t1", "v1");
145+
KeyValues merged = source.and("t2", "v2");
146+
assertThat(source).isNotSameAs(merged);
147+
assertKeyValues(source, "t1", "v1");
148+
assertKeyValues(merged, "t1", "v1", "t2", "v2");
149+
}
150+
151+
@Test
152+
void andKeyValuesShouldReturnNewInstanceWithAddedKeyValues() {
153+
KeyValues source = KeyValues.of("t1", "v1");
154+
KeyValues merged = source.and("t2", "v2", "t3", "v3");
155+
assertThat(source).isNotSameAs(merged);
156+
assertKeyValues(source, "t1", "v1");
157+
assertKeyValues(merged, "t1", "v1", "t2", "v2", "t3", "v3");
158+
}
159+
160+
@Test
161+
void andKeyValuesWhenKeyValuesAreOddShouldThrowException() {
162+
assertThatThrownBy(() -> KeyValues.empty().and("t1", "v1", "t2")).isInstanceOf(IllegalArgumentException.class);
163+
}
164+
165+
@Test
166+
void andKeyValuesStringArrayWhenKeyValuesAreEmptyShouldReturnCurrentInstance() {
167+
KeyValues source = KeyValues.of("t1", "v1");
168+
KeyValues merged = source.and(new String[0]);
169+
assertThat(source).isSameAs(merged);
170+
}
171+
172+
@Test
173+
void andKeyValuesStringArrayWhenKeyValuesAreNullShouldReturnCurrentInstance() {
174+
KeyValues source = KeyValues.of("t1", "v1");
175+
KeyValues merged = source.and((String[]) null);
176+
assertThat(source).isSameAs(merged);
177+
}
178+
179+
@Test
180+
void andKeyValuesShouldReturnANewInstanceWithKeyValues() {
181+
KeyValues source = KeyValues.of("t1", "v1");
182+
KeyValues merged = source.and(KeyValue.of("t2", "v2"));
183+
assertThat(source).isNotSameAs(merged);
184+
assertKeyValues(source, "t1", "v1");
185+
assertKeyValues(merged, "t1", "v1", "t2", "v2");
186+
}
187+
188+
@Test
189+
void andKeyValuesWhenKeyValuesAreEmptyShouldReturnCurrentInstance() {
190+
KeyValues source = KeyValues.of("t1", "v1");
191+
KeyValues merged = source.and(new KeyValue[0]);
192+
assertThat(source).isSameAs(merged);
193+
}
194+
195+
@Test
196+
void andKeyValuesWhenKeyValuesAreNullShouldReturnCurrentInstance() {
197+
KeyValues source = KeyValues.of("t1", "v1");
198+
KeyValues merged = source.and((KeyValue[]) null);
199+
assertThat(source).isSameAs(merged);
200+
}
201+
202+
@Test
203+
void andKeyValuesMultipleTimesShouldWork() {
204+
KeyValues keyValues = KeyValues.empty().and(KeyValue.of("t1", "v1"));
205+
206+
KeyValues firstAnd = keyValues.and(KeyValue.of("t1", "v1"));
207+
assertThat(firstAnd).isEqualTo(keyValues);
208+
209+
KeyValues secondAnd = firstAnd.and(KeyValue.of("t1", "v1"));
210+
assertThat(secondAnd).isEqualTo(keyValues);
211+
}
212+
213+
@Test
214+
void andIterableShouldReturnNewInstanceWithKeyValues() {
215+
KeyValues source = KeyValues.of("t1", "v1");
216+
KeyValues merged = source.and(Collections.singleton(KeyValue.of("t2", "v2")));
217+
assertThat(source).isNotSameAs(merged);
218+
assertKeyValues(source, "t1", "v1");
219+
assertKeyValues(merged, "t1", "v1", "t2", "v2");
220+
}
221+
222+
@Test
223+
void andIterableWhenIterableIsNullShouldReturnCurrentInstance() {
224+
KeyValues source = KeyValues.of("t1", "v1");
225+
KeyValues merged = source.and((Iterable<KeyValue>) null);
226+
assertThat(source).isSameAs(merged);
227+
}
228+
229+
@Test
230+
void andWhenAlreadyContainsKeyShouldReplaceValue() {
231+
KeyValues source = KeyValues.of("t1", "v1");
232+
KeyValues merged = source.and("t2", "v2", "t1", "v3");
233+
assertThat(source).isNotSameAs(merged);
234+
assertKeyValues(source, "t1", "v1");
235+
assertKeyValues(merged, "t1", "v3", "t2", "v2");
236+
}
237+
238+
@Test
239+
void iteratorShouldIterateKeyValues() {
240+
KeyValues keyValues = KeyValues.of("t1", "v1");
241+
Iterator<KeyValue> iterator = keyValues.iterator();
242+
assertThat(iterator).toIterable().containsExactly(KeyValue.of("t1", "v1"));
243+
}
244+
245+
@Test
246+
void streamShouldStreamKeyValues() {
247+
KeyValues keyValues = KeyValues.of("t1", "v1");
248+
Stream<KeyValue> iterator = keyValues.stream();
249+
assertThat(iterator).containsExactly(KeyValue.of("t1", "v1"));
250+
}
251+
252+
@Test
253+
void concatIterableShouldReturnNewInstanceWithAddedKeyValues() {
254+
KeyValues source = KeyValues.of("t1", "v1");
255+
KeyValues merged = KeyValues.concat(source, Collections.singleton(KeyValue.of("t2", "v2")));
256+
assertThat(source).isNotSameAs(merged);
257+
assertKeyValues(source, "t1", "v1");
258+
assertKeyValues(merged, "t1", "v1", "t2", "v2");
259+
}
260+
261+
@Test
262+
void concatStringsShouldReturnNewInstanceWithAddedKeyValues() {
263+
KeyValues source = KeyValues.of("t1", "v1");
264+
KeyValues merged = KeyValues.concat(source, "t2", "v2");
265+
assertThat(source).isNotSameAs(merged);
266+
assertKeyValues(source, "t1", "v1");
267+
assertKeyValues(merged, "t1", "v1", "t2", "v2");
268+
}
269+
270+
@Test
271+
@Deprecated
272+
void zipShouldReturnNewInstanceWithKeyValues() {
273+
KeyValues keyValues = KeyValues.of("t1", "v1", "t2", "v2");
274+
assertKeyValues(keyValues, "t1", "v1", "t2", "v2");
275+
}
276+
277+
@Test
278+
void ofIterableShouldReturnNewInstanceWithKeyValues() {
279+
KeyValues keyValues = KeyValues.of(Collections.singleton(KeyValue.of("t1", "v1")));
280+
assertKeyValues(keyValues, "t1", "v1");
281+
}
282+
283+
@Test
284+
void ofIterableWhenIterableIsKeyValuesShouldReturnSameInstance() {
285+
KeyValues source = KeyValues.of("t1", "v1");
286+
KeyValues keyValues = KeyValues.of(source);
287+
assertThat(keyValues).isSameAs(source);
288+
}
289+
290+
@Test
291+
void ofKeyValueShouldReturnNewInstance() {
292+
KeyValues keyValues = KeyValues.of("t1", "v1");
293+
assertKeyValues(keyValues, "t1", "v1");
294+
}
295+
296+
@Test
297+
void ofKeyValuesShouldReturnNewInstance() {
298+
KeyValues keyValues = KeyValues.of("t1", "v1", "t2", "v2");
299+
assertKeyValues(keyValues, "t1", "v1", "t2", "v2");
300+
}
301+
302+
@Test
303+
void emptyShouldNotContainKeyValues() {
304+
assertThat(KeyValues.empty().iterator()).isExhausted();
305+
}
306+
307+
// gh-3313
308+
@Test
309+
@DisabledIfSystemProperty(named = "java.vm.name", matches = JAVA_VM_NAME_J9_REGEX,
310+
disabledReason = "Sun ThreadMXBean with allocation counter not available")
311+
@EnabledForJreRange(max = JRE.JAVA_18)
312+
void andEmptyDoesNotAllocate() {
313+
andEmptyDoesNotAllocate(0);
314+
}
315+
316+
// gh-3313
317+
// See https://github.com/micrometer-metrics/micrometer/issues/3436
318+
@Test
319+
@DisabledIfSystemProperty(named = "java.vm.name", matches = JAVA_VM_NAME_J9_REGEX,
320+
disabledReason = "Sun ThreadMXBean with allocation counter not available")
321+
@EnabledIf("java19")
322+
void andEmptyDoesNotAllocateOnJava19() {
323+
andEmptyDoesNotAllocate(16);
324+
}
325+
326+
static boolean java19() {
327+
return "19".equals(System.getProperty("java.version"));
328+
}
329+
330+
private void andEmptyDoesNotAllocate(int expectedAllocatedBytes) {
331+
ThreadMXBean threadMXBean = (ThreadMXBean) ManagementFactory.getThreadMXBean();
332+
long currentThreadId = Thread.currentThread().getId();
333+
KeyValues keyValues = KeyValues.of("a", "b");
334+
KeyValues extraKeyValues = KeyValues.empty();
335+
336+
long allocatedBytesBefore = threadMXBean.getThreadAllocatedBytes(currentThreadId);
337+
KeyValues combined = keyValues.and(extraKeyValues);
338+
long allocatedBytes = threadMXBean.getThreadAllocatedBytes(currentThreadId) - allocatedBytesBefore;
339+
340+
assertThat(combined).isEqualTo(keyValues);
341+
assertThat(allocatedBytes).isEqualTo(expectedAllocatedBytes);
342+
}
343+
344+
// gh-3313
345+
@Test
346+
@DisabledIfSystemProperty(named = "java.vm.name", matches = JAVA_VM_NAME_J9_REGEX,
347+
disabledReason = "Sun ThreadMXBean with allocation counter not available")
348+
@EnabledForJreRange(max = JRE.JAVA_18)
349+
void ofEmptyDoesNotAllocate() {
350+
ofEmptyDoesNotAllocate(0);
351+
}
352+
353+
// gh-3313
354+
// See https://github.com/micrometer-metrics/micrometer/issues/3436
355+
@Test
356+
@DisabledIfSystemProperty(named = "java.vm.name", matches = JAVA_VM_NAME_J9_REGEX,
357+
disabledReason = "Sun ThreadMXBean with allocation counter not available")
358+
@EnabledIf("java19")
359+
void ofEmptyDoesNotAllocateOnJava19() {
360+
ofEmptyDoesNotAllocate(16);
361+
}
362+
363+
private void ofEmptyDoesNotAllocate(int expectedAllocatedBytes) {
364+
ThreadMXBean threadMXBean = (ThreadMXBean) ManagementFactory.getThreadMXBean();
365+
long currentThreadId = Thread.currentThread().getId();
366+
KeyValues extraKeyValues = KeyValues.empty();
367+
368+
long allocatedBytesBefore = threadMXBean.getThreadAllocatedBytes(currentThreadId);
369+
KeyValues of = KeyValues.of(extraKeyValues);
370+
long allocatedBytes = threadMXBean.getThreadAllocatedBytes(currentThreadId) - allocatedBytesBefore;
371+
372+
assertThat(of).isEqualTo(KeyValues.empty());
373+
assertThat(allocatedBytes).isEqualTo(expectedAllocatedBytes);
374+
}
375+
376+
private void assertKeyValues(KeyValues keyValues, String... expectedKeyValues) {
377+
Iterator<KeyValue> actual = keyValues.iterator();
378+
Iterator<String> expected = Arrays.asList(expectedKeyValues).iterator();
379+
while (actual.hasNext()) {
380+
KeyValue keyValue = actual.next();
381+
assertThat(keyValue.getKey()).isEqualTo(expected.next());
382+
assertThat(keyValue.getValue()).isEqualTo(expected.next());
383+
}
384+
assertThat(expected.hasNext()).isFalse();
385+
}
386+
30387
@Test
31388
void ofExtractingFromElementsReturnsKeyValues() {
32389
Map<String, String> map = Map.of("micrometer", "tracing", "can", "trace");

0 commit comments

Comments
 (0)