Immutable Attributes and Labels (#1304)
* prototype for class to replace Map<String, AttributeValue) * safely copy the builder's data * add the empty constant * optimization for attributes with a single key/value * Add an iterator/iterable and some simple tests * fix animalsniffer complaint * tests for de-duping and order-independent equality, plus removal of possibly unneeded access methods. * clean up the sort&filter method a tad * replace the iterator with a foreach method * Make the Attributes parameterized by the value type. * Add basic javadoc * remove helper class; add a simple test for the builder; make the tests more robust * Add a varargs method for creating an arbitrary number of key/value pairs. * static import the check method, for consistency * Refactor to have an interface and two implementations, with some shared logic. * fix an accidental rename * really fix it for real * add a few more tests * preserve the `setAttribute` names from existing Span API * Replace the treemap sorting and filtering with a quicksort and post-filter. * remove an unneeded list. * switch to an abstract base class to remove some code duplication * Updated docs based on feedback. * Small change to use the builder for the empty implementations.
This commit is contained in:
parent
afabbd1e06
commit
ee435201b5
|
@ -0,0 +1,240 @@
|
|||
/*
|
||||
* Copyright 2020, OpenTelemetry Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.opentelemetry.common;
|
||||
|
||||
import static io.opentelemetry.common.AttributeValue.arrayAttributeValue;
|
||||
import static io.opentelemetry.common.AttributeValue.booleanAttributeValue;
|
||||
import static io.opentelemetry.common.AttributeValue.doubleAttributeValue;
|
||||
import static io.opentelemetry.common.AttributeValue.longAttributeValue;
|
||||
import static io.opentelemetry.common.AttributeValue.stringAttributeValue;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* An immutable container for attributes.
|
||||
*
|
||||
* <p>The keys are {@link String}s and the values are {@link AttributeValue} instances.
|
||||
*/
|
||||
@Immutable
|
||||
public abstract class Attributes extends ImmutableKeyValuePairs<AttributeValue> {
|
||||
private static final Attributes EMPTY = Attributes.newBuilder().build();
|
||||
|
||||
@AutoValue
|
||||
@Immutable
|
||||
abstract static class ArrayBackedAttributes extends Attributes {
|
||||
ArrayBackedAttributes() {}
|
||||
|
||||
@Override
|
||||
abstract List<Object> data();
|
||||
}
|
||||
|
||||
/** Returns a {@link Attributes} instance with no attributes. */
|
||||
public static Attributes empty() {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
/** Returns a {@link Attributes} instance with a single key-value pair. */
|
||||
public static Attributes of(String key, AttributeValue value) {
|
||||
return sortAndFilterToAttributes(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Attributes} instance with two key-value pairs. Order of the keys is not
|
||||
* preserved. Duplicate keys will be removed.
|
||||
*/
|
||||
public static Attributes of(
|
||||
String key1, AttributeValue value1, String key2, AttributeValue value2) {
|
||||
return sortAndFilterToAttributes(key1, value1, key2, value2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Attributes} instance with three key-value pairs. Order of the keys is not
|
||||
* preserved. Duplicate keys will be removed.
|
||||
*/
|
||||
public static Attributes of(
|
||||
String key1,
|
||||
AttributeValue value1,
|
||||
String key2,
|
||||
AttributeValue value2,
|
||||
String key3,
|
||||
AttributeValue value3) {
|
||||
return sortAndFilterToAttributes(key1, value1, key2, value2, key3, value3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Attributes} instance with four key-value pairs. Order of the keys is not
|
||||
* preserved. Duplicate keys will be removed.
|
||||
*/
|
||||
public static Attributes of(
|
||||
String key1,
|
||||
AttributeValue value1,
|
||||
String key2,
|
||||
AttributeValue value2,
|
||||
String key3,
|
||||
AttributeValue value3,
|
||||
String key4,
|
||||
AttributeValue value4) {
|
||||
return sortAndFilterToAttributes(key1, value1, key2, value2, key3, value3, key4, value4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Attributes} instance with five key-value pairs. Order of the keys is not
|
||||
* preserved. Duplicate keys will be removed.
|
||||
*/
|
||||
public static Attributes of(
|
||||
String key1,
|
||||
AttributeValue value1,
|
||||
String key2,
|
||||
AttributeValue value2,
|
||||
String key3,
|
||||
AttributeValue value3,
|
||||
String key4,
|
||||
AttributeValue value4,
|
||||
String key5,
|
||||
AttributeValue value5) {
|
||||
return sortAndFilterToAttributes(
|
||||
key1, value1,
|
||||
key2, value2,
|
||||
key3, value3,
|
||||
key4, value4,
|
||||
key5, value5);
|
||||
}
|
||||
|
||||
private static Attributes sortAndFilterToAttributes(Object... data) {
|
||||
return new AutoValue_Attributes_ArrayBackedAttributes(sortAndFilter(data));
|
||||
}
|
||||
|
||||
/** Creates a new {@link Builder} instance for creating arbitrary {@link Attributes}. */
|
||||
public static Builder newBuilder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables the creation of an {@link Attributes} instance with an arbitrary number of key-value
|
||||
* pairs.
|
||||
*/
|
||||
public static class Builder {
|
||||
private final List<Object> data = new ArrayList<>();
|
||||
|
||||
/** Create the {@link Attributes} from this. */
|
||||
public Attributes build() {
|
||||
return sortAndFilterToAttributes(data.toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a bare {@link AttributeValue} into this.
|
||||
*
|
||||
* @return this Builder
|
||||
*/
|
||||
public Builder setAttribute(String key, AttributeValue value) {
|
||||
data.add(key);
|
||||
data.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a String {@link AttributeValue} into this.
|
||||
*
|
||||
* @return this Builder
|
||||
*/
|
||||
public Builder setAttribute(String key, String value) {
|
||||
data.add(key);
|
||||
data.add(stringAttributeValue(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a long {@link AttributeValue} into this.
|
||||
*
|
||||
* @return this Builder
|
||||
*/
|
||||
public Builder setAttribute(String key, long value) {
|
||||
data.add(key);
|
||||
data.add(longAttributeValue(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a double {@link AttributeValue} into this.
|
||||
*
|
||||
* @return this Builder
|
||||
*/
|
||||
public Builder setAttribute(String key, double value) {
|
||||
data.add(key);
|
||||
data.add(doubleAttributeValue(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a boolean {@link AttributeValue} into this.
|
||||
*
|
||||
* @return this Builder
|
||||
*/
|
||||
public Builder setAttribute(String key, boolean value) {
|
||||
data.add(key);
|
||||
data.add(booleanAttributeValue(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a String array {@link AttributeValue} into this.
|
||||
*
|
||||
* @return this Builder
|
||||
*/
|
||||
public Builder setAttribute(String key, String... value) {
|
||||
data.add(key);
|
||||
data.add(arrayAttributeValue(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a Long array {@link AttributeValue} into this.
|
||||
*
|
||||
* @return this Builder
|
||||
*/
|
||||
public Builder setAttribute(String key, Long... value) {
|
||||
data.add(key);
|
||||
data.add(arrayAttributeValue(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a Double array {@link AttributeValue} into this.
|
||||
*
|
||||
* @return this Builder
|
||||
*/
|
||||
public Builder setAttribute(String key, Double... value) {
|
||||
data.add(key);
|
||||
data.add(arrayAttributeValue(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a Boolean array {@link AttributeValue} into this.
|
||||
*
|
||||
* @return this Builder
|
||||
*/
|
||||
public Builder setAttribute(String key, Boolean... value) {
|
||||
data.add(key);
|
||||
data.add(arrayAttributeValue(value));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* Copyright 2020, OpenTelemetry Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.opentelemetry.common;
|
||||
|
||||
import static io.opentelemetry.internal.Utils.checkArgument;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* An immutable set of key-value pairs. Keys are only {@link String} typed. Can be iterated over
|
||||
* using the {@link #forEach(KeyValueConsumer)} method.
|
||||
*
|
||||
* @param <V> The type of the values contained in this.
|
||||
* @see Labels
|
||||
* @see Attributes
|
||||
*/
|
||||
@Immutable
|
||||
abstract class ImmutableKeyValuePairs<V> {
|
||||
private static final Logger logger = Logger.getLogger(ImmutableKeyValuePairs.class.getName());
|
||||
|
||||
List<Object> data() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/** Iterates over all the key-value pairs of attributes contained by this instance. */
|
||||
@SuppressWarnings("unchecked")
|
||||
public void forEach(KeyValueConsumer<V> consumer) {
|
||||
for (int i = 0; i < data().size(); i += 2) {
|
||||
consumer.consume((String) data().get(i), (V) data().get(i + 1));
|
||||
}
|
||||
}
|
||||
|
||||
static List<Object> sortAndFilter(Object[] data) {
|
||||
checkArgument(
|
||||
data.length % 2 == 0, "You must provide an even number of key/value pair arguments.");
|
||||
|
||||
quickSort(data, 0, data.length - 2);
|
||||
return dedupe(data);
|
||||
}
|
||||
|
||||
private static void quickSort(Object[] data, int leftIndex, int rightIndex) {
|
||||
if (leftIndex >= rightIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
String pivotKey = (String) data[rightIndex];
|
||||
int counter = leftIndex;
|
||||
|
||||
for (int i = leftIndex; i <= rightIndex; i += 2) {
|
||||
if (((String) data[i]).compareTo(pivotKey) <= 0) {
|
||||
swap(data, counter, i);
|
||||
counter += 2;
|
||||
}
|
||||
}
|
||||
|
||||
quickSort(data, leftIndex, counter - 4);
|
||||
quickSort(data, counter, rightIndex);
|
||||
}
|
||||
|
||||
private static List<Object> dedupe(Object[] data) {
|
||||
List<Object> result = new ArrayList<>(data.length);
|
||||
Object previousKey = null;
|
||||
|
||||
for (int i = 0; i < data.length; i += 2) {
|
||||
Object key = data[i];
|
||||
Object value = data[i + 1];
|
||||
if (key == null) {
|
||||
logger.warning("Ignoring null key.");
|
||||
continue;
|
||||
}
|
||||
if (key.equals(previousKey)) {
|
||||
continue;
|
||||
}
|
||||
previousKey = key;
|
||||
result.add(key);
|
||||
result.add(value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void swap(Object[] data, int a, int b) {
|
||||
Object keyA = data[a];
|
||||
Object valueA = data[a + 1];
|
||||
data[a] = data[b];
|
||||
data[a + 1] = data[b + 1];
|
||||
|
||||
data[b] = keyA;
|
||||
data[b + 1] = valueA;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright 2020, OpenTelemetry Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.opentelemetry.common;
|
||||
|
||||
/**
|
||||
* Used for iterating over the key-value pairs in a key-value pair container, such as {@link
|
||||
* Attributes} or {@link Labels}. The key is always a {@link String}.
|
||||
*/
|
||||
public interface KeyValueConsumer<T> {
|
||||
void consume(String key, T value);
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* Copyright 2020, OpenTelemetry Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.opentelemetry.common;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/** An immutable container for labels, which are pairs of {@link String}. */
|
||||
@Immutable
|
||||
public abstract class Labels extends ImmutableKeyValuePairs<String> {
|
||||
|
||||
private static final Labels EMPTY = Labels.newBuilder().build();
|
||||
|
||||
@AutoValue
|
||||
@Immutable
|
||||
abstract static class ArrayBackedLabels extends Labels {
|
||||
ArrayBackedLabels() {}
|
||||
|
||||
@Override
|
||||
abstract List<Object> data();
|
||||
}
|
||||
|
||||
/** Returns a {@link Labels} instance with no attributes. */
|
||||
public static Labels empty() {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
/** Returns a {@link Labels} instance with a single key-value pair. */
|
||||
public static Labels of(String key, String value) {
|
||||
return sortAndFilterToLabels(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Labels} instance with two key-value pairs. Order of the keys is not preserved.
|
||||
* Duplicate keys will be removed.
|
||||
*/
|
||||
public static Labels of(String key1, String value1, String key2, String value2) {
|
||||
return sortAndFilterToLabels(key1, value1, key2, value2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Labels} instance with three key-value pairs. Order of the keys is not
|
||||
* preserved. Duplicate keys will be removed.
|
||||
*/
|
||||
public static Labels of(
|
||||
String key1, String value1, String key2, String value2, String key3, String value3) {
|
||||
return sortAndFilterToLabels(key1, value1, key2, value2, key3, value3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Labels} instance with four key-value pairs. Order of the keys is not
|
||||
* preserved. Duplicate keys will be removed.
|
||||
*/
|
||||
public static Labels of(
|
||||
String key1,
|
||||
String value1,
|
||||
String key2,
|
||||
String value2,
|
||||
String key3,
|
||||
String value3,
|
||||
String key4,
|
||||
String value4) {
|
||||
return sortAndFilterToLabels(key1, value1, key2, value2, key3, value3, key4, value4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Labels} instance with five key-value pairs. Order of the keys is not
|
||||
* preserved. Duplicate keys will be removed.
|
||||
*/
|
||||
public static Labels of(
|
||||
String key1,
|
||||
String value1,
|
||||
String key2,
|
||||
String value2,
|
||||
String key3,
|
||||
String value3,
|
||||
String key4,
|
||||
String value4,
|
||||
String key5,
|
||||
String value5) {
|
||||
return sortAndFilterToLabels(
|
||||
key1, value1,
|
||||
key2, value2,
|
||||
key3, value3,
|
||||
key4, value4,
|
||||
key5, value5);
|
||||
}
|
||||
|
||||
private static Labels sortAndFilterToLabels(Object... data) {
|
||||
return new AutoValue_Labels_ArrayBackedLabels(sortAndFilter(data));
|
||||
}
|
||||
|
||||
/** Creates a new {@link Builder} instance for creating arbitrary {@link Labels}. */
|
||||
public static Builder newBuilder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables the creation of an {@link Labels} instance with an arbitrary number of key-value pairs.
|
||||
*/
|
||||
public static class Builder {
|
||||
private final List<Object> data = new ArrayList<>();
|
||||
|
||||
/** Create the {@link Labels} from this. */
|
||||
public Labels build() {
|
||||
return sortAndFilterToLabels(data.toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a single label into this Builder.
|
||||
*
|
||||
* @return this Builder
|
||||
*/
|
||||
public Builder setLabel(String key, String value) {
|
||||
data.add(key);
|
||||
data.add(value);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* Copyright 2020, OpenTelemetry Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.opentelemetry.common;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static io.opentelemetry.common.AttributeValue.arrayAttributeValue;
|
||||
import static io.opentelemetry.common.AttributeValue.booleanAttributeValue;
|
||||
import static io.opentelemetry.common.AttributeValue.doubleAttributeValue;
|
||||
import static io.opentelemetry.common.AttributeValue.longAttributeValue;
|
||||
import static io.opentelemetry.common.AttributeValue.stringAttributeValue;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
/** Unit tests for {@link Attributes}s. */
|
||||
public class AttributesTest {
|
||||
@Rule public final ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void forEach() {
|
||||
final Map<String, AttributeValue> entriesSeen = new HashMap<>();
|
||||
|
||||
Attributes attributes =
|
||||
Attributes.of(
|
||||
"key1", stringAttributeValue("value1"),
|
||||
"key2", AttributeValue.longAttributeValue(333));
|
||||
|
||||
attributes.forEach(
|
||||
new KeyValueConsumer<AttributeValue>() {
|
||||
@Override
|
||||
public void consume(String key, AttributeValue value) {
|
||||
entriesSeen.put(key, value);
|
||||
}
|
||||
});
|
||||
|
||||
assertThat(entriesSeen)
|
||||
.containsExactly("key1", stringAttributeValue("value1"), "key2", longAttributeValue(333));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forEach_singleAttribute() {
|
||||
final Map<String, AttributeValue> entriesSeen = new HashMap<>();
|
||||
|
||||
Attributes attributes = Attributes.of("key", stringAttributeValue("value"));
|
||||
attributes.forEach(
|
||||
new KeyValueConsumer<AttributeValue>() {
|
||||
@Override
|
||||
public void consume(String key, AttributeValue value) {
|
||||
entriesSeen.put(key, value);
|
||||
}
|
||||
});
|
||||
assertThat(entriesSeen).containsExactly("key", stringAttributeValue("value"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forEach_empty() {
|
||||
final AtomicBoolean sawSomething = new AtomicBoolean(false);
|
||||
Attributes emptyAttributes = Attributes.empty();
|
||||
emptyAttributes.forEach(
|
||||
new KeyValueConsumer<AttributeValue>() {
|
||||
@Override
|
||||
public void consume(String key, AttributeValue value) {
|
||||
sawSomething.set(true);
|
||||
}
|
||||
});
|
||||
assertThat(sawSomething.get()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void orderIndependentEquality() {
|
||||
Attributes one =
|
||||
Attributes.of(
|
||||
"key1", stringAttributeValue("value1"),
|
||||
"key2", stringAttributeValue("value2"));
|
||||
Attributes two =
|
||||
Attributes.of(
|
||||
"key2", stringAttributeValue("value2"),
|
||||
"key1", stringAttributeValue("value1"));
|
||||
|
||||
assertThat(one).isEqualTo(two);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deduplication() {
|
||||
Attributes one =
|
||||
Attributes.of(
|
||||
"key1", stringAttributeValue("value1"),
|
||||
"key1", stringAttributeValue("valueX"));
|
||||
Attributes two = Attributes.of("key1", stringAttributeValue("value1"));
|
||||
|
||||
assertThat(one).isEqualTo(two);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void builder() {
|
||||
Attributes attributes =
|
||||
Attributes.newBuilder()
|
||||
.setAttribute("string", "value1")
|
||||
.setAttribute("long", 100)
|
||||
.setAttribute("double", 33.44)
|
||||
.setAttribute("boolean", false)
|
||||
.setAttribute("boolean", "duplicateShouldBeRemoved")
|
||||
.build();
|
||||
|
||||
assertThat(attributes)
|
||||
.isEqualTo(
|
||||
Attributes.of(
|
||||
"string", stringAttributeValue("value1"),
|
||||
"long", longAttributeValue(100),
|
||||
"double", doubleAttributeValue(33.44),
|
||||
"boolean", booleanAttributeValue(false)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void builder_arrayTypes() {
|
||||
Attributes attributes =
|
||||
Attributes.newBuilder()
|
||||
.setAttribute("string", "value1", "value2")
|
||||
.setAttribute("long", 100L, 200L)
|
||||
.setAttribute("double", 33.44, -44.33)
|
||||
.setAttribute("boolean", false, true)
|
||||
.setAttribute("boolean", "duplicateShouldBeRemoved")
|
||||
.setAttribute("boolean", stringAttributeValue("dropped"))
|
||||
.build();
|
||||
|
||||
assertThat(attributes)
|
||||
.isEqualTo(
|
||||
Attributes.of(
|
||||
"string", arrayAttributeValue("value1", "value2"),
|
||||
"long", arrayAttributeValue(100L, 200L),
|
||||
"double", arrayAttributeValue(33.44, -44.33),
|
||||
"boolean", arrayAttributeValue(false, true)));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* Copyright 2020, OpenTelemetry Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.opentelemetry.common;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
/** Unit tests for {@link Labels}s. */
|
||||
public class LabelsTest {
|
||||
@Rule public final ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void forEach() {
|
||||
final Map<String, String> entriesSeen = new HashMap<>();
|
||||
|
||||
Labels labels =
|
||||
Labels.of(
|
||||
"key1", "value1",
|
||||
"key2", "value2");
|
||||
|
||||
labels.forEach(
|
||||
new KeyValueConsumer<String>() {
|
||||
@Override
|
||||
public void consume(String key, String value) {
|
||||
entriesSeen.put(key, value);
|
||||
}
|
||||
});
|
||||
|
||||
assertThat(entriesSeen).containsExactly("key1", "value1", "key2", "value2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forEach_singleAttribute() {
|
||||
final Map<String, String> entriesSeen = new HashMap<>();
|
||||
|
||||
Labels labels = Labels.of("key", "value");
|
||||
labels.forEach(
|
||||
new KeyValueConsumer<String>() {
|
||||
@Override
|
||||
public void consume(String key, String value) {
|
||||
entriesSeen.put(key, value);
|
||||
}
|
||||
});
|
||||
|
||||
assertThat(entriesSeen).containsExactly("key", "value");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forEach_empty() {
|
||||
final AtomicBoolean sawSomething = new AtomicBoolean(false);
|
||||
Labels emptyLabels = Labels.empty();
|
||||
emptyLabels.forEach(
|
||||
new KeyValueConsumer<String>() {
|
||||
@Override
|
||||
public void consume(String key, String value) {
|
||||
sawSomething.set(true);
|
||||
}
|
||||
});
|
||||
assertThat(sawSomething.get()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void orderIndependentEquality() {
|
||||
Labels one =
|
||||
Labels.of(
|
||||
"key3", "value3",
|
||||
"key1", "value1",
|
||||
"key2", "value2");
|
||||
Labels two =
|
||||
Labels.of(
|
||||
"key2", "value2",
|
||||
"key3", "value3",
|
||||
"key1", "value1");
|
||||
|
||||
assertThat(one).isEqualTo(two);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deduplication() {
|
||||
Labels one =
|
||||
Labels.of(
|
||||
"key1", "value1",
|
||||
"key1", "valueX");
|
||||
Labels two = Labels.of("key1", "value1");
|
||||
|
||||
assertThat(one).isEqualTo(two);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void threeLabels() {
|
||||
Labels one =
|
||||
Labels.of(
|
||||
"key1", "value1",
|
||||
"key3", "value3",
|
||||
"key2", "value2");
|
||||
assertThat(one).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fourLabels() {
|
||||
Labels one =
|
||||
Labels.of(
|
||||
"key1", "value1",
|
||||
"key2", "value2",
|
||||
"key3", "value3",
|
||||
"key4", "value4");
|
||||
assertThat(one).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void builder() {
|
||||
Labels labels =
|
||||
Labels.newBuilder()
|
||||
.setLabel("key1", "value1")
|
||||
.setLabel("key2", "value2")
|
||||
.setLabel("key1", "duplicateShouldBeIgnored")
|
||||
.build();
|
||||
|
||||
assertThat(labels)
|
||||
.isEqualTo(
|
||||
Labels.of(
|
||||
"key1", "value1",
|
||||
"key2", "value2"));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue