diff --git a/dd-trace-core/src/main/java/datadog/trace/core/taginterceptor/TagInterceptor.java b/dd-trace-core/src/main/java/datadog/trace/core/taginterceptor/TagInterceptor.java index 3da653c5398..f2c6640d721 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/taginterceptor/TagInterceptor.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/taginterceptor/TagInterceptor.java @@ -85,7 +85,7 @@ public TagInterceptor( } public boolean needsIntercept(TagMap map) { - for (TagMap.Entry entry : map) { + for (TagMap.EntryReader entry : map) { if (needsIntercept(entry.tag())) return true; } return false; diff --git a/internal-api/src/main/java/datadog/trace/api/TagMap.java b/internal-api/src/main/java/datadog/trace/api/TagMap.java index 49a1da970fa..10fe96c5fca 100644 --- a/internal-api/src/main/java/datadog/trace/api/TagMap.java +++ b/internal-api/src/main/java/datadog/trace/api/TagMap.java @@ -43,7 +43,7 @@ *
  • adaptive collision * */ -public interface TagMap extends Map, Iterable { +public interface TagMap extends Map, Iterable { /** Immutable empty TagMap - similar to {@link Collections#emptyMap()} */ TagMap EMPTY = TagMapFactory.INSTANCE.empty(); @@ -87,10 +87,14 @@ static Ledger ledger(int size) { @Deprecated Set keySet(); + Iterator tagIterator(); + /** Inefficiently implemented for optimized TagMap - requires boxing primitives */ @Deprecated Collection values(); + Iterator valueIterator(); + // @Deprecated -- not deprecated until OptimizedTagMap becomes the default Set> entrySet(); @@ -166,7 +170,7 @@ static Ledger ledger(int size) { void set(String tag, double value); - void set(Entry newEntry); + void set(EntryReader newEntry); /** sets the value while returning the prior Entry */ Entry getAndSet(String tag, Object value); @@ -237,14 +241,14 @@ static Ledger ledger(int size) { * , but with less allocation */ @Override - Iterator iterator(); + Iterator iterator(); - Stream stream(); + Stream stream(); /** * Visits each Entry in this TagMap This method is more efficient than {@link TagMap#iterator()} */ - void forEach(Consumer consumer); + void forEach(Consumer consumer); /** * Version of forEach that takes an extra context object that is passed as the first argument to @@ -252,7 +256,7 @@ static Ledger ledger(int size) { * *

    The intention is to use this method to avoid using a capturing lambda */ - void forEach(T thisObj, BiConsumer consumer); + void forEach(T thisObj, BiConsumer consumer); /** * Version of forEach that takes two extra context objects that are passed as the first two @@ -260,7 +264,8 @@ static Ledger ledger(int size) { * *

    The intention is to use this method to avoid using a capturing lambda */ - void forEach(T thisObj, U otherObj, TriConsumer consumer); + void forEach( + T thisObj, U otherObj, TriConsumer consumer); /** Clears the TagMap */ void clear(); @@ -307,30 +312,63 @@ public boolean isRemoval() { } } - final class Entry extends EntryChange implements Map.Entry { - /* - * Special value used for Objects that haven't been type checked yet. - * These objects might be primitive box objects. - */ - public static final byte ANY = 0; + interface EntryReader { public static final byte OBJECT = 1; /* * Non-numeric primitive types */ public static final byte BOOLEAN = 2; - public static final byte CHAR = 3; + static final byte CHAR_RESERVED = 3; /* * Numeric constants - deliberately arranged to allow for checking by using type >= BYTE */ - public static final byte BYTE = 4; - public static final byte SHORT = 5; + static final byte BYTE_RESERVED = 4; + static final byte SHORT_RESERVED = 5; public static final byte INT = 6; public static final byte LONG = 7; public static final byte FLOAT = 8; public static final byte DOUBLE = 9; + String tag(); + + byte type(); + + boolean is(byte type); + + boolean isNumericPrimitive(); + + boolean isNumber(); + + boolean isObject(); + + Object objectValue(); + + String stringValue(); + + boolean booleanValue(); + + int intValue(); + + long longValue(); + + float floatValue(); + + double doubleValue(); + + Map.Entry mapEntry(); + + Entry entry(); + } + + final class Entry extends EntryChange implements Map.Entry, EntryReader { + /* + * Special value used for Objects that haven't been type checked yet. + * These objects might be primitive box objects. + */ + static final byte ANY = 0; + static Entry newAnyEntry(Map.Entry entry) { return newAnyEntry(entry.getKey(), entry.getValue()); } @@ -400,7 +438,7 @@ static Entry newDoubleEntry(String tag, Double box) { // no type checks are done during construction. // Any Object entries are initially marked as type ANY, prim set to 0, and the Object put into // obj - // If an ANY entry is later type checked or request as a primitive, then the ANY will be + // If an ANY entry is later type checked or requested as a primitive, then the ANY will be // resolved // to the correct type. @@ -436,10 +474,22 @@ int hash() { return hash; } + @Override + public Entry entry() { + return this; + } + + @Override + public java.util.Map.Entry mapEntry() { + return this; + } + + @Override public byte type() { return this.resolveAny(); } + @Override public boolean is(byte type) { byte curType = this.rawType; if (curType == type) { @@ -451,6 +501,7 @@ public boolean is(byte type) { } } + @Override public boolean isNumericPrimitive() { byte curType = this.rawType; if (_isNumericPrimitive(curType)) { @@ -462,13 +513,14 @@ public boolean isNumericPrimitive() { } } + @Override public boolean isNumber() { byte curType = this.rawType; return _isNumericPrimitive(curType) || (this.rawObj instanceof Number); } static boolean _isNumericPrimitive(byte type) { - return (type >= BYTE); + return (type >= BYTE_RESERVED); } private byte resolveAny() { @@ -517,6 +569,7 @@ private void _setPrim(byte type, long prim) { this.rawType = type; } + @Override public boolean isObject() { return this.is(OBJECT); } @@ -525,6 +578,7 @@ public boolean isRemoval() { return false; } + @Override public Object objectValue() { if (this.rawObj != null) { return this.rawObj; @@ -562,6 +616,7 @@ public Object objectValue() { return this.rawObj; } + @Override public boolean booleanValue() { byte type = this.rawType; @@ -597,6 +652,7 @@ public boolean booleanValue() { return false; } + @Override public int intValue() { byte type = this.rawType; @@ -632,6 +688,7 @@ public int intValue() { return 0; } + @Override public long longValue() { byte type = this.rawType; @@ -667,6 +724,7 @@ public long longValue() { return 0; } + @Override public float floatValue() { byte type = this.rawType; @@ -702,6 +760,7 @@ public float floatValue() { return 0F; } + @Override public double doubleValue() { byte type = this.rawType; @@ -737,6 +796,7 @@ public double doubleValue() { return 0D; } + @Override public String stringValue() { String strCache = this.strCache; if (strCache != null) { @@ -1267,8 +1327,8 @@ public boolean containsKey(Object key) { @Override public boolean containsValue(Object value) { // This could be optimized - but probably isn't called enough to be worth it - for (Entry entry : this) { - if (entry.objectValue().equals(value)) return true; + for (EntryReader entryReader : this) { + if (entryReader.objectValue().equals(value)) return true; } return false; } @@ -1278,11 +1338,21 @@ public Set keySet() { return new Keys(this); } + @Override + public Iterator tagIterator() { + return new KeysIterator(this); + } + @Override public Collection values() { return new Values(this); } + @Override + public Iterator valueIterator() { + return new ValuesIterator(this); + } + @Override public Set> entrySet() { return new Entries(this); @@ -1318,8 +1388,8 @@ public Object put(String tag, Object value) { } @Override - public void set(TagMap.Entry newEntry) { - this.getAndSet(newEntry); + public void set(TagMap.EntryReader newEntryReader) { + this.getAndSet(newEntryReader.entry()); } @Override @@ -1728,17 +1798,17 @@ public TagMap immutableCopy() { } @Override - public Iterator iterator() { - return new EntryIterator(this); + public Iterator iterator() { + return new EntryReaderIterator(this); } @Override - public Stream stream() { + public Stream stream() { return StreamSupport.stream(spliterator(), false); } @Override - public void forEach(Consumer consumer) { + public void forEach(Consumer consumer) { Object[] thisBuckets = this.buckets; for (int i = 0; i < thisBuckets.length; ++i) { @@ -1757,7 +1827,7 @@ public void forEach(Consumer consumer) { } @Override - public void forEach(T thisObj, BiConsumer consumer) { + public void forEach(T thisObj, BiConsumer consumer) { Object[] thisBuckets = this.buckets; for (int i = 0; i < thisBuckets.length; ++i) { @@ -1777,7 +1847,7 @@ public void forEach(T thisObj, BiConsumer consumer) @Override public void forEach( - T thisObj, U otherObj, TriConsumer consumer) { + T thisObj, U otherObj, TriConsumer consumer) { Object[] thisBuckets = this.buckets; for (int i = 0; i < thisBuckets.length; ++i) { @@ -1933,14 +2003,14 @@ String toPrettyString() { StringBuilder ledger = new StringBuilder(128); ledger.append('{'); - for (Entry entry : this) { + for (EntryReader entry : this) { if (first) { first = false; } else { ledger.append(", "); } - ledger.append(entry.tag).append('=').append(entry.stringValue()); + ledger.append(entry.tag()).append('=').append(entry.stringValue()); } ledger.append('}'); return ledger.toString(); @@ -1974,7 +2044,7 @@ String toInternalString() { return ledger.toString(); } - abstract static class MapIterator implements Iterator { + abstract static class IteratorBase { private final Object[] buckets; private Entry nextEntry; @@ -1984,12 +2054,11 @@ abstract static class MapIterator implements Iterator { private BucketGroup group = null; private int groupIndex = 0; - MapIterator(OptimizedTagMap map) { + IteratorBase(OptimizedTagMap map) { this.buckets = map.buckets; } - @Override - public boolean hasNext() { + public final boolean hasNext() { if (this.nextEntry != null) return true; while (this.bucketIndex < this.buckets.length) { @@ -2000,7 +2069,7 @@ public boolean hasNext() { return false; } - Entry nextEntry() { + final Entry nextEntryOrThrowNoSuchElement() { if (this.nextEntry != null) { Entry nextEntry = this.nextEntry; this.nextEntry = null; @@ -2014,7 +2083,17 @@ Entry nextEntry() { } } - private Entry advance() { + final Entry nextEntryOrNull() { + if (this.nextEntry != null) { + Entry nextEntry = this.nextEntry; + this.nextEntry = null; + return nextEntry; + } + + return this.hasNext() ? this.nextEntry : null; + } + + private final Entry advance() { while (this.bucketIndex < this.buckets.length) { if (this.group != null) { for (++this.groupIndex; this.groupIndex < BucketGroup.LEN; ++this.groupIndex) { @@ -2048,14 +2127,14 @@ private Entry advance() { } } - static final class EntryIterator extends MapIterator { - EntryIterator(OptimizedTagMap map) { + static final class EntryReaderIterator extends IteratorBase implements Iterator { + EntryReaderIterator(OptimizedTagMap map) { super(map); } @Override - public Entry next() { - return this.nextEntry(); + public EntryReader next() { + return this.nextEntryOrThrowNoSuchElement(); } } @@ -2407,39 +2486,40 @@ Entry _remove(int hash, String tag) { return existingEntry; } - void forEachInChain(Consumer consumer) { + void forEachInChain(Consumer consumer) { for (BucketGroup curGroup = this; curGroup != null; curGroup = curGroup.prev) { curGroup._forEach(consumer); } } - void _forEach(Consumer consumer) { + void _forEach(Consumer consumer) { if (this.entry0 != null) consumer.accept(this.entry0); if (this.entry1 != null) consumer.accept(this.entry1); if (this.entry2 != null) consumer.accept(this.entry2); if (this.entry3 != null) consumer.accept(this.entry3); } - void forEachInChain(T thisObj, BiConsumer consumer) { + void forEachInChain(T thisObj, BiConsumer consumer) { for (BucketGroup curGroup = this; curGroup != null; curGroup = curGroup.prev) { curGroup._forEach(thisObj, consumer); } } - void _forEach(T thisObj, BiConsumer consumer) { + void _forEach(T thisObj, BiConsumer consumer) { if (this.entry0 != null) consumer.accept(thisObj, this.entry0); if (this.entry1 != null) consumer.accept(thisObj, this.entry1); if (this.entry2 != null) consumer.accept(thisObj, this.entry2); if (this.entry3 != null) consumer.accept(thisObj, this.entry3); } - void forEachInChain(T thisObj, U otherObj, TriConsumer consumer) { + void forEachInChain( + T thisObj, U otherObj, TriConsumer consumer) { for (BucketGroup curGroup = this; curGroup != null; curGroup = curGroup.prev) { curGroup._forEach(thisObj, otherObj, consumer); } } - void _forEach(T thisObj, U otherObj, TriConsumer consumer) { + void _forEach(T thisObj, U otherObj, TriConsumer consumer) { if (this.entry0 != null) consumer.accept(thisObj, otherObj, this.entry0); if (this.entry1 != null) consumer.accept(thisObj, otherObj, this.entry1); if (this.entry2 != null) consumer.accept(thisObj, otherObj, this.entry2); @@ -2575,14 +2655,14 @@ public Iterator iterator() { } } - static final class KeysIterator extends MapIterator { + static final class KeysIterator extends IteratorBase implements Iterator { KeysIterator(OptimizedTagMap map) { super(map); } @Override public String next() { - return this.nextEntry().tag(); + return this.nextEntryOrThrowNoSuchElement().tag(); } } @@ -2614,18 +2694,112 @@ public Iterator iterator() { } } - static final class ValuesIterator extends MapIterator { + static final class ValuesIterator extends IteratorBase implements Iterator { ValuesIterator(OptimizedTagMap map) { super(map); } @Override public Object next() { - return this.nextEntry().objectValue(); + return this.nextEntryOrThrowNoSuchElement().objectValue(); } } } +final class EntryReadingHelper implements TagMap.EntryReader { + private Map.Entry mapEntry; + private String tag; + private Object value; + + void set(String tag, Object value) { + this.mapEntry = null; + this.tag = tag; + this.value = value; + } + + void set(Map.Entry mapEntry) { + this.mapEntry = mapEntry; + this.tag = mapEntry.getKey(); + this.value = mapEntry.getValue(); + } + + @Override + public String tag() { + return this.tag; + } + + @Override + public byte type() { + return TagValueConversions.typeOf(this.value); + } + + @Override + public boolean is(byte type) { + return TagValueConversions.isA(this.value, type); + } + + @Override + public boolean isNumber() { + return TagValueConversions.isNumber(this.value); + } + + @Override + public boolean isNumericPrimitive() { + return TagValueConversions.isNumericPrimitive(this.value); + } + + @Override + public boolean isObject() { + return TagValueConversions.isObject(this.value); + } + + @Override + public boolean booleanValue() { + return TagValueConversions.toBoolean(this.value); + } + + @Override + public int intValue() { + return TagValueConversions.toInt(this.value); + } + + @Override + public long longValue() { + return TagValueConversions.toLong(this.value); + } + + @Override + public float floatValue() { + return TagValueConversions.toFloat(this.value); + } + + @Override + public double doubleValue() { + return TagValueConversions.toDouble(this.value); + } + + @Override + public String stringValue() { + return TagValueConversions.toString(this.value); + } + + @Override + public Object objectValue() { + return this.value; + } + + @Override + public TagMap.Entry entry() { + return TagMap.Entry.newAnyEntry(this.tag, this.value); + } + + @Override + public Map.Entry mapEntry() { + Map.Entry mapEntry = this.mapEntry; + return (mapEntry != null) ? mapEntry : this.entry(); + } +} + final class LegacyTagMap extends HashMap implements TagMap { private static final long serialVersionUID = 77473435283123683L; @@ -2676,6 +2850,16 @@ public TagMap copy() { return new LegacyTagMap(this); } + @Override + public Iterator tagIterator() { + return this.keySet().iterator(); + } + + @Override + public Iterator valueIterator() { + return this.values().iterator(); + } + @Override public void fillMap(Map map) { map.putAll(this); @@ -2689,25 +2873,37 @@ public void fillStringMap(Map stringMap) { } @Override - public void forEach(Consumer consumer) { + public void forEach(Consumer consumer) { + EntryReadingHelper entryReadingHelper = new EntryReadingHelper(); + + // TODO: optimize to take advantage of EntryReader for (Map.Entry entry : this.entrySet()) { - consumer.accept(TagMap.Entry.newAnyEntry(entry)); + entryReadingHelper.set(entry); + + consumer.accept(entryReadingHelper); } } @Override - public void forEach( - T thisObj, BiConsumer consumer) { + public void forEach(T thisObj, BiConsumer consumer) { + EntryReadingHelper entryReadingHelper = new EntryReadingHelper(); + for (Map.Entry entry : this.entrySet()) { - consumer.accept(thisObj, TagMap.Entry.newAnyEntry(entry)); + entryReadingHelper.set(entry); + + consumer.accept(thisObj, entryReadingHelper); } } @Override public void forEach( - T thisObj, U otherObj, TriConsumer consumer) { + T thisObj, U otherObj, TriConsumer consumer) { + EntryReadingHelper entryReadingHelper = new EntryReadingHelper(); + for (Map.Entry entry : this.entrySet()) { - consumer.accept(thisObj, otherObj, TagMap.Entry.newAnyEntry(entry)); + entryReadingHelper.set(entry); + + consumer.accept(thisObj, otherObj, entryReadingHelper); } } @@ -2912,8 +3108,8 @@ public void set(String tag, Object value) { } @Override - public void set(TagMap.Entry newEntry) { - this.put(newEntry.tag(), newEntry.objectValue()); + public void set(TagMap.EntryReader newEntryReader) { + this.put(newEntryReader.tag(), newEntryReader.objectValue()); } @Override @@ -2990,20 +3186,22 @@ public TagMap immutableCopy() { } @Override - public Iterator iterator() { + public Iterator iterator() { return new IteratorImpl(this); } @Override - public Stream stream() { + public Stream stream() { return StreamSupport.stream(this.spliterator(), false); } - private static final class IteratorImpl implements Iterator { + private static final class IteratorImpl implements Iterator { private final Iterator> wrappedIter; + private final EntryReadingHelper entryReadingHelper; IteratorImpl(LegacyTagMap legacyMap) { this.wrappedIter = legacyMap.entrySet().iterator(); + this.entryReadingHelper = new EntryReadingHelper(); } @Override @@ -3012,8 +3210,176 @@ public boolean hasNext() { } @Override - public TagMap.Entry next() { - return TagMap.Entry.newAnyEntry(this.wrappedIter.next()); + public TagMap.EntryReader next() { + Map.Entry entry = this.wrappedIter.next(); + this.entryReadingHelper.set(entry.getKey(), entry.getValue()); + + return this.entryReadingHelper; } } } + +final class TagValueConversions { + TagValueConversions() {} + + static byte typeOf(Object value) { + if (value instanceof Integer) { + return TagMap.EntryReader.INT; + } else if (value instanceof Long) { + return TagMap.EntryReader.LONG; + } else if (value instanceof Double) { + return TagMap.EntryReader.DOUBLE; + } else if (value instanceof Float) { + return TagMap.EntryReader.FLOAT; + } else if (value instanceof Boolean) { + return TagMap.EntryReader.BOOLEAN; + } else if (value instanceof Short) { + return TagMap.EntryReader.INT; + } else if (value instanceof Byte) { + return TagMap.EntryReader.INT; + } else if (value instanceof Character) { + return TagMap.EntryReader.OBJECT; + } else { + return TagMap.EntryReader.OBJECT; + } + } + + static boolean isA(Object value, byte type) { + switch (type) { + case TagMap.EntryReader.BOOLEAN: + return (value instanceof Boolean); + + case TagMap.EntryReader.INT: + return (value instanceof Integer) || (value instanceof Short) || (value instanceof Byte); + + case TagMap.EntryReader.LONG: + return (value instanceof Long); + + case TagMap.EntryReader.FLOAT: + return (value instanceof Float); + + case TagMap.EntryReader.DOUBLE: + return (value instanceof Double); + + case TagMap.EntryReader.OBJECT: + return true; + + default: + return false; + } + } + + static boolean isNumber(Object value) { + return (value instanceof Number); + } + + static boolean isNumericPrimitive(Object value) { + return (value instanceof Integer) + || (value instanceof Long) + || (value instanceof Double) + || (value instanceof Float) + || (value instanceof Short) + || (value instanceof Byte); + } + + static boolean isObject(Object value) { + boolean isSupportedPrimitive = + (value instanceof Integer) + || (value instanceof Long) + || (value instanceof Double) + || (value instanceof Float) + || (value instanceof Boolean) + || (value instanceof Short) + || (value instanceof Byte); + + // Char is just treated as Object + return !isSupportedPrimitive; + } + + static boolean toBooleanOrDefault(Object value, boolean defaultValue) { + return (value == null) ? defaultValue : toBoolean(value); + } + + static boolean toBoolean(Object value) { + if (value instanceof Boolean) { + return (Boolean) value; + } else if (value instanceof Number) { + // NOTE: This cannot be intValue() because intValue of larger types is 0 when + // the actual value would be less than Integer.MIN_VALUE or for floating point + // types is very close to zero, so using doubleValue instead. + + // While this is a bit ugly, coerced toBoolean is uncommon + return ((Number) value).doubleValue() != 0D; + } else { + return false; + } + } + + static int toIntOrDefault(Object value, int defaultValue) { + return (value == null) ? defaultValue : toInt(value); + } + + static int toInt(Object value) { + if (value instanceof Integer) { + return (Integer) value; + } else if (value instanceof Number) { + return ((Number) value).intValue(); + } else if (value instanceof Boolean) { + return (Boolean) value ? 1 : 0; + } else { + return 0; + } + } + + static long toLong(Object value) { + if (value instanceof Long) { + return (Long) value; + } else if (value instanceof Number) { + return ((Number) value).longValue(); + } else if (value instanceof Boolean) { + return (Boolean) value ? 1L : 0L; + } else { + return 0L; + } + } + + static long toLongOrDefault(Object value, long defaultValue) { + return (value == null) ? defaultValue : toLong(value); + } + + static float toFloat(Object value) { + if (value instanceof Float) { + return (Float) value; + } else if (value instanceof Number) { + return ((Number) value).floatValue(); + } else if (value instanceof Boolean) { + return (Boolean) value ? 1F : 0F; + } else { + return 0F; + } + } + + static float toFloatOrDefault(Object value, float defaultValue) { + return (value == null) ? defaultValue : toFloat(value); + } + + static double toDouble(Object value) { + if (value instanceof Double) { + return (Double) value; + } else if (value instanceof Number) { + return ((Number) value).doubleValue(); + } else if (value instanceof Boolean) { + return (Boolean) value ? 1D : 0D; + } else { + return 0D; + } + } + + static double toDoubleOrDefault(Object value, double defaultValue) { + return (value == null) ? defaultValue : toDouble(value); + } + + static String toString(Object value) { + return value.toString(); + } +} diff --git a/internal-api/src/test/java/datadog/trace/api/TagMapEntryTest.java b/internal-api/src/test/java/datadog/trace/api/TagMapEntryTest.java index 1fc401d4db3..13da8723b60 100644 --- a/internal-api/src/test/java/datadog/trace/api/TagMapEntryTest.java +++ b/internal-api/src/test/java/datadog/trace/api/TagMapEntryTest.java @@ -1,5 +1,6 @@ package datadog.trace.api; +import static org.junit.Assert.assertSame; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -36,11 +37,11 @@ public class TagMapEntryTest { public void isNumericPrimitive() { assertFalse(TagMap.Entry._isNumericPrimitive(TagMap.Entry.ANY)); assertFalse(TagMap.Entry._isNumericPrimitive(TagMap.Entry.BOOLEAN)); - assertFalse(TagMap.Entry._isNumericPrimitive(TagMap.Entry.CHAR)); + assertFalse(TagMap.Entry._isNumericPrimitive(TagMap.Entry.CHAR_RESERVED)); assertFalse(TagMap.Entry._isNumericPrimitive(TagMap.Entry.OBJECT)); - assertTrue(TagMap.Entry._isNumericPrimitive(TagMap.Entry.BYTE)); - assertTrue(TagMap.Entry._isNumericPrimitive(TagMap.Entry.SHORT)); + assertTrue(TagMap.Entry._isNumericPrimitive(TagMap.Entry.BYTE_RESERVED)); + assertTrue(TagMap.Entry._isNumericPrimitive(TagMap.Entry.SHORT_RESERVED)); assertTrue(TagMap.Entry._isNumericPrimitive(TagMap.Entry.INT)); assertTrue(TagMap.Entry._isNumericPrimitive(TagMap.Entry.LONG)); assertTrue(TagMap.Entry._isNumericPrimitive(TagMap.Entry.FLOAT)); @@ -88,6 +89,7 @@ public void booleanEntry(boolean value) { checkValue(value, entry), checkFalse(entry::isNumericPrimitive), checkFalse(entry::isNumber), + checkFalse(entry::isObject), checkType(TagMap.Entry.BOOLEAN, entry))); } @@ -103,6 +105,7 @@ public void booleanEntry_boxed(boolean value) { checkValue(value, entry), checkFalse(entry::isNumericPrimitive), checkFalse(entry::isNumber), + checkFalse(entry::isObject), checkType(TagMap.Entry.BOOLEAN, entry))); } @@ -118,6 +121,7 @@ public void anyEntry_boolean(boolean value) { checkValue(value, entry), checkFalse(entry::isNumericPrimitive), checkFalse(entry::isNumber), + checkFalse(entry::isObject), checkType(TagMap.Entry.BOOLEAN, entry), checkValue(value, entry))); } @@ -152,6 +156,36 @@ public void intEntry_boxed(int value) { checkType(TagMap.Entry.INT, entry))); } + @ParameterizedTest + @ValueSource(shorts = {Short.MIN_VALUE, -256, -128, -1, 0, 1, 128, 256, Short.MAX_VALUE}) + public void intEntry_boxedShort(short value) { + test( + () -> TagMap.Entry.newIntEntry("foo", Short.valueOf(value)), + TagMap.Entry.INT, + (entry) -> + multiCheck( + checkKey("foo", entry), + checkValue(value, entry), + checkIsNumericPrimitive(entry), + checkInstanceOf(Number.class, entry), + checkType(TagMap.Entry.INT, entry))); + } + + @ParameterizedTest + @ValueSource(bytes = {Byte.MIN_VALUE, -32, -1, 0, 1, 32, Byte.MAX_VALUE}) + public void intEntry_boxedByte(byte value) { + test( + () -> TagMap.Entry.newIntEntry("foo", Byte.valueOf(value)), + TagMap.Entry.INT, + (entry) -> + multiCheck( + checkKey("foo", entry), + checkValue(value, entry), + checkIsNumericPrimitive(entry), + checkInstanceOf(Number.class, entry), + checkType(TagMap.Entry.INT, entry))); + } + @ParameterizedTest @ValueSource(ints = {Integer.MIN_VALUE, -256, -128, -1, 0, 1, 128, 256, Integer.MAX_VALUE}) public void anyEntry_int(int value) { @@ -364,15 +398,24 @@ public Thread newThread(Runnable r) { }); static final void test( - Supplier entrySupplier, byte rawType, Function checks) { + Supplier entrySupplier, byte rawType, Function checkSupplier) { + + Function combinedCheckSupplier = + (entry) -> { + return multiCheck( + checkSupplier.apply(entry), + checkSame(entry, entry.entry()), + checkSame(entry, entry.mapEntry())); + }; + // repeat the test several times to exercise different orderings in this thread for (int i = 0; i < 10; ++i) { - testSingleThreaded(entrySupplier, rawType, checks); + testSingleThreaded(entrySupplier, rawType, combinedCheckSupplier); } // same for multi-threaded for (int i = 0; i < 5; ++i) { - testMultiThreaded(entrySupplier, rawType, checks); + testMultiThreaded(entrySupplier, rawType, combinedCheckSupplier); } } @@ -440,17 +483,64 @@ static final Check checkKey(String expected, TagMap.Entry entry) { return multiCheck(checkEquals(expected, entry::tag), checkEquals(expected, entry::getKey)); } + static final Supplier of(Supplier supplier, String identifier) { + return new Supplier() { + @Override + public T get() { + return supplier.get(); + } + + @Override + public String toString() { + return identifier; + } + }; + } + + static final Function of(Function func, String identifier) { + return new Function() { + @Override + public O apply(I input) { + return func.apply(input); + } + + @Override + public String toString() { + return identifier; + } + }; + } + + static final Supplier of(Function output, Supplier input) { + return of(output, input, output.toString() + "(" + input.toString() + ")"); + } + + static final Supplier of(Function output, Supplier input, String identifier) { + return of(() -> output.apply(input.get()), identifier); + } + static final Check checkIsNumericPrimitive(TagMap.Entry entry) { return multiCheck( - checkTrue(entry::isNumericPrimitive), - checkTrue(entry::isNumber), - checkInstanceOf(Number.class, entry)); + checkTrue(entry::isNumericPrimitive, "Entry::isNumericPrimitive"), + checkTrue(entry::isNumber, "Entry::isNumber"), + checkInstanceOf(Number.class, entry), + checkFalse(entry::isObject, "Entry::isObject"), + checkTrue(of(TagValueConversions::isNumber, entry::objectValue), "isNumber(Object)"), + checkTrue( + of(TagValueConversions::isNumericPrimitive, entry::objectValue), + "isNumericPrimitive(Object)"), + checkFalse(of(TagValueConversions::isObject, entry::objectValue), "isObject(Object)")); } static final Check checkIsBigNumber(TagMap.Entry entry) { return multiCheck( - checkFalse(entry::isNumericPrimitive), - checkTrue(entry::isNumber), + checkFalse(entry::isNumericPrimitive, "Entry::isNumericPrimitive"), + checkFalse( + of(TagValueConversions::isNumericPrimitive, entry::objectValue), + "isNumericPrimitive(Object)"), + checkTrue(entry::isNumber, "Entry::isNumber"), + checkTrue(entry::isObject, "Entry::isObject"), + checkTrue(of(TagValueConversions::isNumber, entry::objectValue), "isNumber(Object)"), checkInstanceOf(Number.class, entry)); } @@ -463,114 +553,243 @@ static final Check checkValue(Object expected, TagMap.Entry entry) { static final Check checkValue(boolean expected, TagMap.Entry entry) { return multiCheck( - checkEquals(expected, entry::booleanValue), - checkEquals(Boolean.valueOf(expected), entry::objectValue), - checkEquals(expected ? 1 : 0, entry::intValue), - checkEquals(expected ? 1L : 0L, entry::longValue), - checkEquals(expected ? 1D : 0D, entry::doubleValue), - checkEquals(expected ? 1F : 0F, entry::floatValue), - checkEquals(Boolean.toString(expected), entry::stringValue)); + checkEquals(expected, entry::booleanValue, "Entry::booleanValue"), + checkEquals(Boolean.valueOf(expected), entry::objectValue, "Entry::objectValue"), + checkEquals(expected ? 1 : 0, entry::intValue, "Entry::intValue"), + checkEquals( + expected ? 1 : 0, of(TagValueConversions::toInt, entry::objectValue), "toInt(Object)"), + checkEquals(expected ? 1L : 0L, entry::longValue, "Entry::longValue"), + checkEquals( + expected ? 1L : 0L, + of(TagValueConversions::toLong, entry::objectValue), + "toLong(Object)"), + checkEquals(expected ? 1D : 0D, entry::doubleValue, "Entry::doubleValue"), + checkEquals( + expected ? 1D : 0D, + of(TagValueConversions::toDouble, entry::objectValue), + "toDouble(Object)"), + checkEquals(expected ? 1F : 0F, entry::floatValue, "Entry::floatValue"), + checkEquals( + expected ? 1F : 0F, + of(TagValueConversions::toFloat, entry::objectValue), + "toFloat(Object)"), + checkEquals(Boolean.toString(expected), entry::stringValue, "Entry::stringValue"), + checkEquals( + Boolean.toString(expected), + of(TagValueConversions::toString, entry::objectValue), + "toString(Object)")); } static final Check checkValue(int expected, TagMap.Entry entry) { return multiCheck( - checkEquals(expected, entry::intValue), - checkEquals((long) expected, entry::longValue), - checkEquals((float) expected, entry::floatValue), - checkEquals((double) expected, entry::doubleValue), - checkEquals(Integer.valueOf(expected), entry::objectValue), - checkEquals(expected != 0, entry::booleanValue), - checkEquals(Integer.toString(expected), entry::stringValue)); + checkEquals(expected, entry::intValue, "Entry::intValue"), + checkEquals(Integer.valueOf(expected), entry::objectValue, "Entry::objectValue"), + checkEquals((long) expected, entry::longValue, "Entry::longValue"), + checkEquals( + (long) expected, of(TagValueConversions::toLong, entry::objectValue), "toLong(Object)"), + checkEquals((float) expected, entry::floatValue, "Entry::floatValue"), + checkEquals( + (float) expected, + of(TagValueConversions::toFloat, entry::objectValue), + "toFloat(Object)"), + checkEquals((double) expected, entry::doubleValue, "Entry::doubleValue"), + checkEquals( + (double) expected, + of(TagValueConversions::toDouble, entry::objectValue), + "toDouble(Object)"), + checkEquals(expected != 0, entry::booleanValue, "Entry::booleanValue"), + checkEquals( + expected != 0, + of(TagValueConversions::toBoolean, entry::objectValue), + "toBoolean(Object)"), + checkEquals(Integer.toString(expected), entry::stringValue, "Entry::stringValue"), + checkEquals( + Integer.toString(expected), + of(TagValueConversions::toString, entry::objectValue), + "toString(Object)")); } static final Check checkValue(long expected, TagMap.Entry entry) { return multiCheck( - checkEquals(expected, entry::longValue), - checkEquals((int) expected, entry::intValue), - checkEquals((float) expected, entry::floatValue), - checkEquals((double) expected, entry::doubleValue), - checkEquals(Long.valueOf(expected), entry::objectValue), - checkEquals(expected != 0L, entry::booleanValue), - checkEquals(Long.toString(expected), entry::stringValue)); + checkEquals(expected, entry::longValue, "Entry::longValue"), + checkEquals(Long.valueOf(expected), entry::objectValue, "Entry::objectValue"), + checkEquals((int) expected, entry::intValue, "Entry::intValue"), + checkEquals( + (int) expected, of(TagValueConversions::toInt, entry::objectValue), "toInt(Object)"), + checkEquals((float) expected, entry::floatValue, "Entry::floatValue"), + checkEquals( + (float) expected, + of(TagValueConversions::toFloat, entry::objectValue), + "toFloat(Object)"), + checkEquals((double) expected, entry::doubleValue, "Entry::doubleValue"), + checkEquals( + (double) expected, + of(TagValueConversions::toDouble, entry::objectValue), + "toDouble(Object)"), + checkEquals(expected != 0L, entry::booleanValue, "Entry::booleanValue"), + checkEquals( + expected != 0L, + of(TagValueConversions::toBoolean, entry::objectValue), + "toBoolean(Object)"), + checkEquals(Long.toString(expected), entry::stringValue, "Entry::stringValue"), + checkEquals( + Long.toString(expected), + of(TagValueConversions::toString, entry::objectValue), + "toString(Object)")); } static final Check checkValue(double expected, TagMap.Entry entry) { return multiCheck( - checkEquals(expected, entry::doubleValue), - checkEquals((int) expected, entry::intValue), - checkEquals((long) expected, entry::longValue), - checkEquals((float) expected, entry::floatValue), - checkEquals(Double.valueOf(expected), entry::objectValue), - checkEquals(expected != 0D, entry::booleanValue), - checkEquals(Double.toString(expected), entry::stringValue)); + checkEquals(expected, entry::doubleValue, "Entry::doubleValue"), + checkEquals(Double.valueOf(expected), entry::objectValue, "Entry::objectValue"), + checkEquals((int) expected, entry::intValue, "Entry::intValue"), + checkEquals( + (int) expected, of(TagValueConversions::toInt, entry::objectValue), "toInt(Object)"), + checkEquals((long) expected, entry::longValue, "Entry::longValue"), + checkEquals( + (long) expected, of(TagValueConversions::toLong, entry::objectValue), "toLong(Object)"), + checkEquals((float) expected, entry::floatValue, "Entry::floatValue"), + checkEquals( + (float) expected, + of(TagValueConversions::toFloat, entry::objectValue), + "toFloat(Object)"), + checkEquals(expected != 0D, entry::booleanValue, "Entry::booleanValue"), + checkEquals( + expected != 0D, + of(TagValueConversions::toBoolean, entry::objectValue), + "toBoolean(Object)"), + checkEquals(Double.toString(expected), entry::stringValue, "Entry::stringValue"), + checkEquals( + Double.toString(expected), + of(TagValueConversions::toString, entry::objectValue), + "toString(Object)")); + } + + static final Check checkValue(float expected, TagMap.Entry entry) { + return multiCheck( + checkEquals(expected, entry::floatValue, "Entry::floatValue"), + checkEquals(Float.valueOf(expected), entry::objectValue, "Entry::objectValue"), + checkEquals((int) expected, entry::intValue, "Entry::intValue"), + checkEquals( + (int) expected, of(TagValueConversions::toInt, entry::objectValue), "toInt(Object)"), + checkEquals((long) expected, entry::longValue, "Entry::longValue"), + checkEquals( + (long) expected, of(TagValueConversions::toLong, entry::objectValue), "toLong(Object)"), + checkEquals((double) expected, entry::doubleValue, "Entry::doubleValue"), + checkEquals( + (double) expected, + of(TagValueConversions::toDouble, entry::objectValue), + "toDouble(Object)"), + checkEquals(expected != 0F, entry::booleanValue, "Entry::booleanValue"), + checkEquals( + expected != 0F, + of(TagValueConversions::toBoolean, entry::objectValue), + "toBoolean(Object)"), + checkEquals(Float.toString(expected), entry::stringValue, "Entry::stringValue"), + checkEquals( + Float.toString(expected), + of(TagValueConversions::toString, entry::objectValue), + "toString(Object)")); } public static Check checkNumber(Number number, TagMap.Entry entry) { return multiCheck( checkEquals(number, entry::objectValue), checkEquals(number.intValue(), entry::intValue), + checkEquals(number.intValue(), of(TagValueConversions::toInt, entry::objectValue)), checkEquals(number.longValue(), entry::longValue), + checkEquals(number.longValue(), of(TagValueConversions::toLong, entry::objectValue)), checkEquals(number.floatValue(), entry::floatValue), + checkEquals(number.floatValue(), of(TagValueConversions::toFloat, entry::objectValue)), checkEquals(number.doubleValue(), entry::doubleValue), - checkEquals(number.toString(), entry::stringValue)); - } - - static final Check checkValue(float expected, TagMap.Entry entry) { - return multiCheck( - checkEquals(expected, entry::floatValue), - checkEquals((int) expected, entry::intValue), - checkEquals((long) expected, entry::longValue), - checkEquals((double) expected, entry::doubleValue), - checkEquals(expected != 0F, entry::booleanValue), - checkEquals(Float.valueOf(expected), entry::objectValue), - checkEquals(Float.toString(expected), entry::stringValue)); + checkEquals(number.doubleValue(), of(TagValueConversions::toDouble, entry::objectValue)), + checkEquals(number.toString(), entry::stringValue), + checkEquals(number.toString(), of(TagValueConversions::toString, entry::objectValue))); } static final Check checkInstanceOf(Class klass, TagMap.Entry entry) { - return () -> - assertTrue( - klass.isAssignableFrom(entry.objectValue().getClass()), - "instanceof " + klass.getSimpleName()); + return checkTrue( + () -> klass.isAssignableFrom(entry.objectValue().getClass()), + "instanceof " + klass.getSimpleName()); } static final Check checkType(byte entryType, TagMap.Entry entry) { - return () -> assertTrue(entry.is(entryType), "type is " + entryType); + // TODO: TVC checks + return multiCheck( + checkTrue(() -> entry.is(entryType), "type is " + entryType), + checkEquals(entryType, entry::type)); } static final Check multiCheck(Check... checks) { return new MultipartCheck(checks); } + static final Check checkSame(Object expected, Object actual) { + return () -> assertSame(expected, actual); + } + static final Check checkFalse(Supplier actual) { - return () -> assertFalse(actual.get(), actual.toString()); + return checkFalse(actual, actual.toString()); + } + + static final Check checkFalse(Supplier actual, String identifier) { + return () -> assertFalse(actual.get(), identifier); } static final Check checkTrue(Supplier actual) { - return () -> assertTrue(actual.get(), actual.toString()); + return checkTrue(actual, actual.toString()); + } + + static final Check checkTrue(Supplier actual, String identifier) { + return () -> assertTrue(actual.get(), identifier); } static final Check checkEquals(float expected, Supplier actual) { - return () -> assertEquals(expected, actual.get().floatValue(), actual.toString()); + return checkEquals(expected, actual, actual.toString()); + } + + static final Check checkEquals(float expected, Supplier actual, String identifier) { + return () -> assertEquals(expected, actual.get().floatValue(), identifier); } static final Check checkEquals(int expected, Supplier actual) { - return () -> assertEquals(expected, actual.get().intValue(), actual.toString()); + return checkEquals(expected, actual, actual.toString()); + } + + static final Check checkEquals(int expected, Supplier actual, String identifier) { + return () -> assertEquals(expected, actual.get().intValue(), identifier); } static final Check checkEquals(double expected, Supplier actual) { - return () -> assertEquals(expected, actual.get().doubleValue(), actual.toString()); + return checkEquals(expected, actual, actual.toString()); + } + + static final Check checkEquals(double expected, Supplier actual, String identifier) { + return () -> assertEquals(expected, actual.get().doubleValue(), identifier); } static final Check checkEquals(long expected, Supplier actual) { - return () -> assertEquals(expected, actual.get().longValue(), actual.toString()); + return checkEquals(expected, actual, actual.toString()); + } + + static final Check checkEquals(long expected, Supplier actual, String identifier) { + return () -> assertEquals(expected, actual.get().longValue(), identifier); } static final Check checkEquals(boolean expected, Supplier actual) { - return () -> assertEquals(expected, actual.get().booleanValue(), actual.toString()); + return checkEquals(expected, actual, actual.toString()); + } + + static final Check checkEquals(boolean expected, Supplier actual, String identifier) { + return () -> assertEquals(expected, actual.get().booleanValue(), identifier); + } + + static final Check checkEquals(T expected, Supplier actual) { + return checkEquals(expected, actual, actual.toString()); } - static final Check checkEquals(Object expected, Supplier actual) { - return () -> assertEquals(expected, actual.get(), actual.toString()); + static final Check checkEquals(T expected, Supplier actual, String identifier) { + return () -> assertEquals(expected, actual.get(), identifier); } @FunctionalInterface diff --git a/internal-api/src/test/java/datadog/trace/api/TagMapFuzzTest.java b/internal-api/src/test/java/datadog/trace/api/TagMapFuzzTest.java index 692aaaf37f3..48254ae9bd1 100644 --- a/internal-api/src/test/java/datadog/trace/api/TagMapFuzzTest.java +++ b/internal-api/src/test/java/datadog/trace/api/TagMapFuzzTest.java @@ -1023,7 +1023,7 @@ static final void assertMapEquals(Map expected, OptimizedTagMap assertEquals(expectedEntry.getValue(), actualEntry.getValue()); } - for (TagMap.Entry actualEntry : actual) { + for (TagMap.EntryReader actualEntry : actual) { Object expectedValue = expected.get(actualEntry.tag()); assertEquals(expectedValue, actualEntry.objectValue()); } @@ -1378,7 +1378,7 @@ protected void _applyToExpectedMap(Map expectedMap) { @Override public void verifyTestMap(TagMap expectedMap) { - for (TagMap.Entry entry : this.tagMap) { + for (TagMap.EntryReader entry : this.tagMap) { assertEquals(entry.objectValue(), expectedMap.get(entry.tag()), "key=" + entry.tag()); } } @@ -1417,7 +1417,7 @@ public void verifyTestMap(TagMap expectedMap) { // ledger may contain multiple updates of the same key // easier to produce a TagMap and check against it - for (TagMap.Entry entry : this.ledger.buildImmutable()) { + for (TagMap.EntryReader entry : this.ledger.buildImmutable()) { assertEquals(entry.objectValue(), expectedMap.get(entry.tag()), "key=" + entry.tag()); } } diff --git a/internal-api/src/test/java/datadog/trace/api/TagMapTest.java b/internal-api/src/test/java/datadog/trace/api/TagMapTest.java index 0c89086d84e..ce5a6551164 100644 --- a/internal-api/src/test/java/datadog/trace/api/TagMapTest.java +++ b/internal-api/src/test/java/datadog/trace/api/TagMapTest.java @@ -787,7 +787,7 @@ public void iterator(TagMapType mapType) { TagMap map = createTagMap(mapType, size); Set keys = new HashSet<>(); - for (TagMap.Entry entry : map) { + for (TagMap.EntryReader entry : map) { // makes sure that each key is visited once and only once assertTrue(keys.add(entry.tag())); } @@ -1064,7 +1064,11 @@ static final void assertSize(int size, TagMap map) { assertEquals(size, count(map)); assertEquals(size, map.keySet().size()); assertEquals(size, map.values().size()); + assertEquals(size, count(map.keySet())); + assertEquals(size, count(map.tagIterator())); + + assertEquals(size, count(map.values().iterator())); assertEquals(size, count(map.values())); } diff --git a/internal-api/src/test/java/datadog/trace/api/TagValueConversionsTest.java b/internal-api/src/test/java/datadog/trace/api/TagValueConversionsTest.java new file mode 100644 index 00000000000..48b04419bfc --- /dev/null +++ b/internal-api/src/test/java/datadog/trace/api/TagValueConversionsTest.java @@ -0,0 +1,166 @@ +package datadog.trace.api; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +public class TagValueConversionsTest { + @ParameterizedTest + @ValueSource(booleans = {false, true}) + public void boolean_(boolean value) { + Boolean box = Boolean.valueOf(value); + + assertEquals(TagMap.EntryReader.BOOLEAN, TagValueConversions.typeOf(box)); + assertTrue(TagValueConversions.isA(box, TagMap.EntryReader.BOOLEAN)); + assertFalse(TagValueConversions.isNumericPrimitive(box)); + assertFalse(TagValueConversions.isNumber(box)); + assertFalse(TagValueConversions.isObject(box)); + + assertEquals(value, TagValueConversions.toBoolean(box)); + assertEquals(value ? 1 : 0, TagValueConversions.toInt(box)); + assertEquals(value ? 1L : 0L, TagValueConversions.toLong(box)); + assertEquals(value ? 1F : 0F, TagValueConversions.toFloat(box)); + assertEquals(value ? 1D : 0D, TagValueConversions.toDouble(box)); + + assertEquals(Boolean.toString(value), TagValueConversions.toString(box)); + } + + @ParameterizedTest + @ValueSource(ints = {Integer.MIN_VALUE, -256, -128, -1, 0, 1, 128, 256, Integer.MAX_VALUE}) + public void int_(int value) { + Integer box = Integer.valueOf(value); + + assertEquals(TagMap.EntryReader.INT, TagValueConversions.typeOf(box)); + assertTrue(TagValueConversions.isA(box, TagMap.EntryReader.INT)); + assertTrue(TagValueConversions.isNumericPrimitive(box)); + assertTrue(TagValueConversions.isNumber(box)); + assertFalse(TagValueConversions.isObject(box)); + + assertEquals(value, TagValueConversions.toInt(box)); + assertEquals((long) value, TagValueConversions.toLong(box)); + assertEquals((float) value, TagValueConversions.toFloat(box)); + assertEquals((double) value, TagValueConversions.toDouble(box)); + + assertEquals(value != 0, TagValueConversions.toBoolean(box)); + assertEquals(Integer.toString(value), TagValueConversions.toString(box)); + } + + @ParameterizedTest + @ValueSource(bytes = {Byte.MIN_VALUE, -32, -1, 0, 1, 32, Byte.MAX_VALUE}) + public void byte_(byte value) { + Byte box = Byte.valueOf(value); + + assertEquals(TagMap.EntryReader.INT, TagValueConversions.typeOf(box)); + assertTrue(TagValueConversions.isA(box, TagMap.EntryReader.INT)); + assertTrue(TagValueConversions.isNumericPrimitive(box)); + assertTrue(TagValueConversions.isNumber(box)); + assertFalse(TagValueConversions.isObject(box)); + + assertEquals((int) value, TagValueConversions.toInt(box)); + assertEquals((long) value, TagValueConversions.toLong(box)); + assertEquals((float) value, TagValueConversions.toFloat(box)); + assertEquals((double) value, TagValueConversions.toDouble(box)); + + assertEquals(value != 0, TagValueConversions.toBoolean(box)); + assertEquals(Byte.toString(value), TagValueConversions.toString(box)); + } + + @ParameterizedTest + @ValueSource(shorts = {Short.MIN_VALUE, -256, -128, -1, 0, 1, 128, 256, Short.MAX_VALUE}) + public void short_(short value) { + Short box = Short.valueOf(value); + + assertEquals(TagMap.EntryReader.INT, TagValueConversions.typeOf(box)); + assertTrue(TagValueConversions.isA(box, TagMap.EntryReader.INT)); + assertTrue(TagValueConversions.isNumericPrimitive(box)); + assertTrue(TagValueConversions.isNumber(box)); + assertFalse(TagValueConversions.isObject(box)); + + assertEquals((int) value, TagValueConversions.toInt(box)); + assertEquals((long) value, TagValueConversions.toLong(box)); + assertEquals((float) value, TagValueConversions.toFloat(box)); + assertEquals((double) value, TagValueConversions.toDouble(box)); + + assertEquals(value != 0, TagValueConversions.toBoolean(box)); + assertEquals(Short.toString(value), TagValueConversions.toString(box)); + } + + @ParameterizedTest + @ValueSource( + longs = { + Long.MIN_VALUE, + Integer.MIN_VALUE, + -1_048_576L, + -256L, + -128L, + -1L, + 0L, + 1L, + 128L, + 256L, + 1_048_576L, + Integer.MAX_VALUE, + Long.MAX_VALUE + }) + public void long_(long value) { + Long box = Long.valueOf(value); + + assertEquals(TagMap.EntryReader.LONG, TagValueConversions.typeOf(box)); + assertTrue(TagValueConversions.isA(box, TagMap.EntryReader.LONG)); + assertTrue(TagValueConversions.isNumericPrimitive(box)); + assertTrue(TagValueConversions.isNumber(box)); + assertFalse(TagValueConversions.isObject(box)); + + assertEquals(value, TagValueConversions.toLong(box)); + assertEquals((int) value, TagValueConversions.toInt(box)); + assertEquals((float) value, TagValueConversions.toFloat(box)); + assertEquals((double) value, TagValueConversions.toDouble(box)); + + assertEquals(value != 0L, TagValueConversions.toBoolean(box)); + assertEquals(Long.toString(value), TagValueConversions.toString(box)); + } + + @ParameterizedTest + @ValueSource(floats = {Float.MIN_VALUE, -1F, 0F, 1F, 2.171828F, 3.1415F, Float.MAX_VALUE}) + public void float_(float value) { + Float box = Float.valueOf(value); + + assertEquals(TagMap.EntryReader.FLOAT, TagValueConversions.typeOf(box)); + assertTrue(TagValueConversions.isA(box, TagMap.EntryReader.FLOAT)); + assertTrue(TagValueConversions.isNumericPrimitive(box)); + assertTrue(TagValueConversions.isNumber(box)); + assertFalse(TagValueConversions.isObject(box)); + + assertEquals(value, TagValueConversions.toFloat(box)); + assertEquals((int) value, TagValueConversions.toInt(box)); + assertEquals((long) value, TagValueConversions.toLong(box)); + assertEquals((double) value, TagValueConversions.toDouble(box)); + + assertEquals(value != 0F, TagValueConversions.toBoolean(box)); + assertEquals(Float.toString(value), TagValueConversions.toString(box)); + } + + @ParameterizedTest + @ValueSource( + doubles = {Double.MIN_VALUE, Float.MIN_VALUE, -1D, 0D, 1D, Math.E, Math.PI, Double.MAX_VALUE}) + public void double_(double value) { + Double box = Double.valueOf(value); + + assertEquals(TagMap.EntryReader.DOUBLE, TagValueConversions.typeOf(box)); + assertTrue(TagValueConversions.isA(box, TagMap.EntryReader.DOUBLE)); + assertTrue(TagValueConversions.isNumericPrimitive(box)); + assertTrue(TagValueConversions.isNumber(box)); + assertFalse(TagValueConversions.isObject(box)); + + assertEquals(value, TagValueConversions.toDouble(box)); + assertEquals((int) value, TagValueConversions.toInt(box)); + assertEquals((long) value, TagValueConversions.toLong(box)); + assertEquals((float) value, TagValueConversions.toFloat(box)); + + assertEquals(value != 0D, TagValueConversions.toBoolean(box)); + assertEquals(Double.toString(value), TagValueConversions.toString(box)); + } +}