Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions .claude/skills/migrate-groovy-to-java/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,6 @@ When converting Groovy code to Java code, make sure that:
- When translating Spock `Mock(...)` usage, use `libs.bundles.mockito` instead of writing manual recording/stub implementations

TableTest usage
Dependency, if missing add:
- Groovy: testImplementation libs.tabletest
- Kotlin: testImplementation(libs.tabletest)

Import: `import org.tabletest.junit.TableTest;`

JDK 8 rules:
Expand Down
13 changes: 7 additions & 6 deletions .claude/skills/migrate-junit-source-to-tabletest/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,6 @@ Process (do in this order):
4) Write each modified file once in full using Write (no incremental per-test edits).
5) Run module tests once and verify "BUILD SUCCESSFUL". If failed, inspect JUnit XML report.

Dependency:
- If missing, add:
- Groovy: testImplementation libs.tabletest
- Kotlin: testImplementation(libs.tabletest)

Import: `import org.tabletest.junit.TableTest;`

JDK 8 rules:
Expand All @@ -42,7 +37,8 @@ A) @CsvSource
- If delimiter is ',' (default): replace ',' with '|' in rows.

B) @ValueSource
- Convert to @TableTest with header from parameter name.
- Keep single-parameter tests on `@ValueSource` (and `@NullSource` when null cases are needed).
- Otherwise convert to @TableTest with header from parameter name.
- Each value becomes one row.
- Add "scenario" column using common sense for name.

Expand All @@ -60,6 +56,11 @@ C) @MethodSource (convert only if values are representable as strings)
- '' = empty string.
- For String params that start with '[' or '{', quote to avoid collection parsing (prefer '[]'/'{}').

D) @TypeConverter
- Use `@TypeConverter` for symbolic constants used by migrated table rows (e.g. `Long.MAX_VALUE`, `DDSpanId.MAX`).
- Prefer explicit one-case-one-return mappings.
- Prefer shared converter utilities (e.g. in `utils/test-utils`) when reuse across modules is likely.

Scenario handling:
- If MethodSource includes a leading description string OR @ParameterizedTest(name=...) uses {0}, convert that to a scenario column and remove that parameter from method signature.

Expand Down
4 changes: 0 additions & 4 deletions components/json/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,3 @@ apply(from = "$rootDir/gradle/java.gradle")
jmh {
jmhVersion = libs.versions.jmh.get()
}

dependencies {
testImplementation(libs.tabletest)
}
29 changes: 7 additions & 22 deletions components/json/src/test/java/datadog/json/JsonMapperTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.NullSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.tabletest.junit.Scenario;
import org.tabletest.junit.TableTest;

Expand Down Expand Up @@ -77,28 +79,16 @@ static Stream<Arguments> testMappingToJsonObjectArguments() {
"{\"key1\":null,\"key2\":\"bar\",\"key3\":3,\"key4\":3456789123,\"key5\":3.142,\"key6\":3.141592653589793,\"key7\":true,\"key8\":\"toString\"}"));
}

@TableTest({
"Scenario | Json ",
"null | ",
"null string | 'null'",
"empty string | '' ",
"empty object | '{}' "
})
@ParameterizedTest(name = "test mapping to Map from empty JSON object: {0}")
@NullSource
@ValueSource(strings = {"null", "", "{}"})
void testMappingToMapFromEmptyJsonObject(String json) throws IOException {
Map<String, Object> parsed = JsonMapper.fromJsonToMap(json);
assertEquals(emptyMap(), parsed);
}

// temporary disable spotless, will open issue to fix this.
// spotless:off
@TableTest({
"Scenario | Json ",
"integer | 1 ",
"array | [1, 2]"
})
// spotless:on
@ParameterizedTest(name = "test mapping to Map from non-object JSON: {0}")
@ValueSource(strings = {"1", "[1, 2]"})
void testMappingToMapFromNonObjectJson(String json) {
assertThrows(IOException.class, () -> JsonMapper.fromJsonToMap(json));
}
Expand Down Expand Up @@ -138,14 +128,9 @@ void testMappingArrayToJsonArray(String ignoredScenario, String[] input, String
assertArrayEquals(input != null ? input : new String[] {}, parsed);
}

@TableTest({
"Scenario | Json ",
"null | ",
"null string | 'null'",
"empty string | '' ",
"empty array | '[]' "
})
@ParameterizedTest(name = "test mapping to List from empty JSON object: {0}")
@NullSource
@ValueSource(strings = {"null", "", "[]"})
void testMappingToListFromEmptyJsonObject(String json) throws IOException {
List<String> parsed = JsonMapper.fromJsonToList(json);
assertEquals(emptyList(), parsed);
Expand Down
152 changes: 58 additions & 94 deletions dd-trace-api/src/test/java/datadog/trace/api/DDSpanIdTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,58 +7,62 @@
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.math.BigInteger;
import datadog.trace.test.util.TableTestTypeConverters;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Stream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.NullSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.tabletest.junit.TableTest;
import org.tabletest.junit.TypeConverterSources;

@TypeConverterSources(TableTestTypeConverters.class)
class DDSpanIdTest {

@ParameterizedTest(name = "convert ids from/to String {0}")
@MethodSource("convertIdsFromToStringArguments")
void convertIdsFromToString(String displayName, String stringId, long expectedId) {
@TableTest({
"scenario | stringId | expectedId ",
"zero | '0' | 0 ",
"one | '1' | 1 ",
"max | '18446744073709551615' | DDSpanId.MAX ",
"long max | '9223372036854775807' | Long.MAX_VALUE ",
"long max plus one | '9223372036854775808' | Long.MIN_VALUE "
})
@ParameterizedTest
void convertIdsFromToString(String stringId, long expectedId) {
long ddid = DDSpanId.from(stringId);

assertEquals(expectedId, ddid);
assertEquals(stringId, DDSpanId.toString(ddid));
}

static Stream<Arguments> convertIdsFromToStringArguments() {
return Stream.of(
Arguments.of("zero", "0", 0L),
Arguments.of("one", "1", 1L),
Arguments.of("max", "18446744073709551615", DDSpanId.MAX),
Arguments.of("long max", String.valueOf(Long.MAX_VALUE), Long.MAX_VALUE),
Arguments.of(
"long max plus one",
BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE).toString(),
Long.MIN_VALUE));
}

@ParameterizedTest(name = "fail on illegal String {0}")
@MethodSource("failOnIllegalStringArguments")
@ParameterizedTest
@NullSource
@ValueSource(
strings = {
"",
"-1",
"18446744073709551616",
"18446744073709551625",
"184467440737095516150",
"18446744073709551a1",
"184467440737095511a"
})
void failOnIllegalString(String stringId) {
assertThrows(NumberFormatException.class, () -> DDSpanId.from(stringId));
}

static Stream<Arguments> failOnIllegalStringArguments() {
return Stream.of(
Arguments.of((Object) null),
Arguments.of(""),
Arguments.of("-1"),
Arguments.of("18446744073709551616"),
Arguments.of("18446744073709551625"),
Arguments.of("184467440737095516150"),
Arguments.of("18446744073709551a1"),
Arguments.of("184467440737095511a"));
}

@ParameterizedTest(name = "convert ids from/to hex String {0}")
@MethodSource("convertIdsFromToHexStringArguments")
@TableTest({
"scenario | hexId | expectedId ",
"zero | '0' | 0 ",
"one | '1' | 1 ",
"max | 'ffffffffffffffff' | DDSpanId.MAX ",
"long max | '7fffffffffffffff' | Long.MAX_VALUE ",
"long min | '8000000000000000' | Long.MIN_VALUE ",
"long min with leading zeros | '00008000000000000000' | Long.MIN_VALUE ",
"cafebabe | 'cafebabe' | 3405691582 ",
"fifteen hex digits | '123456789abcdef' | 81985529216486895 "
})
@ParameterizedTest
void convertIdsFromToHexString(String hexId, long expectedId) {
long ddid = DDSpanId.fromHex(hexId);
String padded16 =
Expand All @@ -73,27 +77,22 @@ void convertIdsFromToHexString(String hexId, long expectedId) {
assertEquals(padded16, DDSpanId.toHexStringPadded(ddid));
}

static Stream<Arguments> convertIdsFromToHexStringArguments() {
return Stream.of(
Arguments.of("0", 0L),
Arguments.of("1", 1L),
Arguments.of(repeat("f", 16), DDSpanId.MAX),
Arguments.of("7" + repeat("f", 15), Long.MAX_VALUE),
Arguments.of("8" + repeat("0", 15), Long.MIN_VALUE),
Arguments.of(repeat("0", 4) + "8" + repeat("0", 15), Long.MIN_VALUE),
Arguments.of("cafebabe", 3405691582L),
Arguments.of("123456789abcdef", 81985529216486895L));
}

@ParameterizedTest(name = "convert ids from part of hex String {0}")
@MethodSource("convertIdsFromPartOfHexStringArguments")
@TableTest({
"scenario | hexId | start | length | lowerCaseOnly | expectedId ",
"null input | | 1 | 1 | false | ",
"empty input | '' | 1 | 1 | false | ",
"negative start | '00' | -1 | 1 | false | ",
"zero length | '00' | 0 | 0 | false | ",
"single zero at index 0 | '00' | 0 | 1 | false | DDSpanId.ZERO",
"single zero at index 1 | '00' | 1 | 1 | false | DDSpanId.ZERO",
"single zero at index 1 duplicate | '00' | 1 | 1 | false | DDSpanId.ZERO",
"max lower-case | 'ffffffffffffffff' | 0 | 16 | true | DDSpanId.MAX ",
"upper-case rejected when lower-case only| 'ffffffffffffFfff' | 0 | 16 | true | ",
"upper-case accepted when lower disabled | 'ffffffffffffFfff' | 0 | 16 | false | DDSpanId.MAX "
})
@ParameterizedTest
void convertIdsFromPartOfHexString(
String displayName,
String hexId,
int start,
int length,
boolean lowerCaseOnly,
Long expectedId) {
String hexId, int start, int length, boolean lowerCaseOnly, Long expectedId) {
Long parsedId = null;
try {
parsedId = DDSpanId.fromHex(hexId, start, length, lowerCaseOnly);
Expand All @@ -108,49 +107,14 @@ void convertIdsFromPartOfHexString(
}
}

static Stream<Arguments> convertIdsFromPartOfHexStringArguments() {
return Stream.of(
Arguments.of("null input", null, 1, 1, false, null),
Arguments.of("empty input", "", 1, 1, false, null),
Arguments.of("negative start", "00", -1, 1, false, null),
Arguments.of("zero length", "00", 0, 0, false, null),
Arguments.of("single zero at index 0", "00", 0, 1, false, DDSpanId.ZERO),
Arguments.of("single zero at index 1", "00", 1, 1, false, DDSpanId.ZERO),
Arguments.of("single zero at index 1 duplicate", "00", 1, 1, false, DDSpanId.ZERO),
Arguments.of("max lower-case", repeat("f", 16), 0, 16, true, DDSpanId.MAX),
Arguments.of(
"upper-case rejected when lower-case only",
repeat("f", 12) + "Ffff",
0,
16,
true,
null),
Arguments.of(
"upper-case accepted when lower-case disabled",
repeat("f", 12) + "Ffff",
0,
16,
false,
DDSpanId.MAX));
}

@ParameterizedTest(name = "fail on illegal hex String {0}")
@MethodSource("failOnIllegalHexStringArguments")
@ParameterizedTest
@NullSource
@ValueSource(strings = {"", "-1", "10000000000000000", "ffffffffffffffzf", "fffffffffffffffz"})
void failOnIllegalHexString(String hexId) {
assertThrows(NumberFormatException.class, () -> DDSpanId.fromHex(hexId));
}

static Stream<Arguments> failOnIllegalHexStringArguments() {
return Stream.of(
Arguments.of((Object) null),
Arguments.of(""),
Arguments.of("-1"),
Arguments.of("1" + repeat("0", 16)),
Arguments.of(repeat("f", 14) + "zf"),
Arguments.of(repeat("f", 15) + "z"));
}

@ParameterizedTest(name = "generate id with {0}")
@ParameterizedTest
@ValueSource(strings = {"RANDOM", "SEQUENTIAL", "SECURE_RANDOM"})
void generateIdWithStrategy(String strategyName) {
IdGenerationStrategy strategy = IdGenerationStrategy.fromName(strategyName);
Expand Down
Loading
Loading