Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ plugins {
spotlessPredeclare {
java {
googleJavaFormat()
tableTestFormatter()
}
}

Expand Down
2 changes: 2 additions & 0 deletions buildSrc/src/main/kotlin/java-common.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ dependencies {
compileOnly(libs.spotbugs.annotations)
testCompileOnly(libs.spotbugs.annotations)
testImplementation(libs.junit.jupiter)
testImplementation(libs.tabletest)
testImplementation(libs.assertj.core)
testRuntimeOnly(libs.junit.launcher)
}
Expand Down Expand Up @@ -76,6 +77,7 @@ spotless {
importOrder()
removeUnusedImports()
googleJavaFormat()
tableTestFormatter()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@

import java.io.PrintWriter;
import java.io.StringWriter;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.util.CheckClassAdapter;
import org.tabletest.junit.TableTest;

class DefineClassGlueGeneratorTest {

@ParameterizedTest
@ValueSource(strings = {"sun/misc", "jdk/internal/misc"})
@TableTest({
"unsafeNamespace ",
"'sun/misc' ",
"'jdk/internal/misc'"
})
void generatedBytecodePassesVerification(String unsafeNamespace) {
byte[] bytecode = DefineClassGlueGenerator.generateBytecode(unsafeNamespace);
StringWriter errors = new StringWriter();
Expand All @@ -25,8 +27,11 @@ void generatedBytecodePassesVerification(String unsafeNamespace) {
}

/** ClassInjector inlines the glue unpacking, so check the packed glue decodes as expected. */
@ParameterizedTest
@ValueSource(strings = {"sun/misc", "jdk/internal/misc"})
@TableTest({
"unsafeNamespace ",
"'sun/misc' ",
"'jdk/internal/misc'"
})
void packedGlueUnpacksToGeneratedBytecode(String unsafeNamespace) {
byte[] originalBytecode = DefineClassGlueGenerator.generateBytecode(unsafeNamespace);
String packedBytecode =
Expand Down
Original file line number Diff line number Diff line change
@@ -1,61 +1,53 @@
package datadog.instrument.classmatch;

import static datadog.instrument.classmatch.AccessMatcher.ABSTRACT;
import static datadog.instrument.classmatch.AccessMatcher.CLASS;
import static datadog.instrument.classmatch.AccessMatcher.CONCRETE;
import static datadog.instrument.classmatch.AccessMatcher.FINAL;
import static datadog.instrument.classmatch.AccessMatcher.INSTANCE;
import static datadog.instrument.classmatch.AccessMatcher.INTERFACE;
import static datadog.instrument.classmatch.AccessMatcher.NON_FINAL;
import static datadog.instrument.classmatch.AccessMatcher.PACKAGE_PRIVATE;
import static datadog.instrument.classmatch.AccessMatcher.PRIVATE;
import static datadog.instrument.classmatch.AccessMatcher.PROTECTED;
import static datadog.instrument.classmatch.AccessMatcher.PUBLIC;
import static datadog.instrument.classmatch.AccessMatcher.STATIC;
import static datadog.instrument.classmatch.AccessMatcher.SYNCHRONIZED;
import static datadog.instrument.classmatch.AccessMatcher.TRANSIENT;
import static datadog.instrument.classmatch.AccessMatcher.VARARGS;
import static datadog.instrument.classmatch.AccessMatcher.VOLATILE;
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 static org.junit.jupiter.api.Assertions.fail;

import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.tabletest.junit.TableTest;
import org.tabletest.junit.TypeConverter;
import org.tabletest.junit.TypeConverterSources;

@TypeConverterSources(AccessMatcherTest.Converters.class)
class AccessMatcherTest {
static Stream<Arguments> flags() {
return Stream.of(
Arguments.of("public", PUBLIC, 0x0001, false),
Arguments.of("private", PRIVATE, 0x0002, false),
Arguments.of("protected", PROTECTED, 0x0004, false),
Arguments.of("package-private", PACKAGE_PRIVATE, 0x0007, true),
Arguments.of("static", STATIC, 0x0008, false),
Arguments.of("instance", INSTANCE, 0x0008, true),
Arguments.of("final", FINAL, 0x0010, false),
Arguments.of("non-final", NON_FINAL, 0x0010, true),
Arguments.of("synchronized", SYNCHRONIZED, 0x0020, false),
Arguments.of("volatile", VOLATILE, 0x0040, false),
Arguments.of("varargs", VARARGS, 0x0080, false),
Arguments.of("transient", TRANSIENT, 0x0080, false),
Arguments.of("interface", INTERFACE, 0x0200, false),
Arguments.of("class", CLASS, 0x0200, true),
Arguments.of("abstract", ABSTRACT, 0x0400, false),
Arguments.of("concrete", CONCRETE, 0x0400, true));
/** TableTest only calls public converters declared in public classes. */
public static final class Converters {
@TypeConverter
public static AccessMatcher accessMatcher(String name) throws ReflectiveOperationException {
return (AccessMatcher) AccessMatcher.class.getField(name).get(null);
}
}

@ParameterizedTest(name = "{0}")
@MethodSource("flags")
void flagsIgnoreUnrelatedBits(String name, AccessMatcher matcher, int flag, boolean inverted) {
@TableTest({
"Scenario | Matcher | Flag | Inverted",
"public | PUBLIC | 0x0001 | false ",
"private | PRIVATE | 0x0002 | false ",
"protected | PROTECTED | 0x0004 | false ",
"package-private | PACKAGE_PRIVATE | 0x0007 | true ",
"static | STATIC | 0x0008 | false ",
"instance | INSTANCE | 0x0008 | true ",
"final | FINAL | 0x0010 | false ",
"non-final | NON_FINAL | 0x0010 | true ",
"synchronized | SYNCHRONIZED | 0x0020 | false ",
"volatile | VOLATILE | 0x0040 | false ",
"varargs | VARARGS | 0x0080 | false ",
"transient | TRANSIENT | 0x0080 | false ",
"interface | INTERFACE | 0x0200 | false ",
"class | CLASS | 0x0200 | true ",
"abstract | ABSTRACT | 0x0400 | false ",
"concrete | CONCRETE | 0x0400 | true "
})
void flagsIgnoreUnrelatedBits(AccessMatcher matcher, int flag, boolean inverted) {
// Exercise every combination of the JVM's 16 access bits, including synthetic/bridge bits.
for (int access = 0; access <= 0xffff; access++) {
boolean expected = ((access & flag) != 0) != inverted;
if (matcher.test(access) != expected) {
fail(name + ": expected " + expected + " for access " + access);
fail("expected " + expected + " for access " + access);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,16 @@
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.tabletest.junit.TableTest;

class AnnotationRegistrationTest {
@TempDir Path temporary;

@ParameterizedTest
@ValueSource(strings = {"single", "collection"})
@TableTest({
"mode ",
"'single' ",
"'collection'"
})
void firstRegistrationWorksInAFreshJvm(String mode) throws Exception {
Path output = temporary.resolve("probe-" + mode + ".log");
Process process =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,18 @@
import static org.objectweb.asm.Opcodes.ACC_ABSTRACT;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.objectweb.asm.ClassWriter;
import org.tabletest.junit.TableTest;

class ClassFileUnicodeTest {
@ParameterizedTest
@ValueSource(strings = {"Café", "Grüße", "例クラス", "\uD801\uDC00", "Asciié例\uD801\uDC00End"})
@TableTest({
"Scenario | Name ",
"two-byte at end | 'Café' ",
"two-byte in middle | 'Grüße' ",
"three-byte only | '例クラス' ",
"surrogate pair only | '\uD801\uDC00' ",
"every encoded width | 'Asciié例\uD801\uDC00End'"
})
void modifiedUtf8NamesRoundTripAtEveryPositionAndAtAnOffset(String name) {
String className = "tests/" + name;
String descriptor = "L" + className + ";";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,10 @@

import java.lang.reflect.Modifier;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.tabletest.junit.TableTest;

class FieldMatcherTest {
@ParameterizedTest
@MethodSource("datadog.instrument.classmatch.MatcherTestSupport#types")
@TableTest(resource = "types.table")
void exactTypeOverloads(String name, Class<?> type, String descriptor) {
FieldOutline candidate = fieldOutline(descriptor);
assertMatches(field("value").type(name), candidate, true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,9 @@
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Type;
import org.tabletest.junit.TableTest;

class GeneratedSignatureTest {
private static final Class<?>[] TYPES = {
Expand All @@ -41,8 +40,12 @@ class GeneratedSignatureTest {
@SuppressFBWarnings(
value = "DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED",
justification = "Generated fixtures use an isolated test class loader")
@ParameterizedTest
@ValueSource(ints = {0, 1, 2})
@TableTest({
"offset",
"0 ",
"1 ",
"2 "
})
void generatedSignaturesAgreeWithReflection(int offset) throws Exception {
ClassWriter writer = classWriter("tests/GeneratedSignatures");
Class<?>[] returns = Arrays.copyOf(TYPES, TYPES.length + 1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,21 @@
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.util.Arrays;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
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.ValueSource;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ByteVector;
import org.objectweb.asm.ClassWriter;
import org.tabletest.junit.TableTest;

class MalformedClassFileTest {
static Stream<Arguments> incompleteDescriptors() {
return Stream.of(
Arguments.of("(I", new int[0]),
Arguments.of("(Ljava/lang/String", new int[0]),
Arguments.of("([", new int[0]),
Arguments.of("(ILjava/lang/String", new int[] {2}),
Arguments.of("(II[", new int[] {2, 3}));
}

@ParameterizedTest
@MethodSource("incompleteDescriptors")
@TableTest({
"Descriptor | Boundaries",
"'(I' | [] ",
"'(Ljava/lang/String' | [] ",
"'([' | [] ",
"'(ILjava/lang/String' | [2] ",
"'(II[' | [2, 3] "
})
void incompleteDescriptorsRetainOnlyPreviouslyParsedBoundaries(
String descriptor, int[] boundaries) {
MethodOutline method = methodOutline(descriptor);
Expand All @@ -38,8 +31,13 @@ void incompleteDescriptorsRetainOnlyPreviouslyParsedBoundaries(
}
}

@ParameterizedTest
@ValueSource(ints = {0, 2, 13, 99})
@TableTest({
"Scenario | Tag",
"never assigned | 0 ",
"unassigned gap after Utf8 | 2 ",
"unassigned gap after NameAndType | 13 ",
"beyond highest defined tag | 99 "
})
void invalidConstantPoolTagsAreRejected(int tag) {
byte[] bytes = classWriter("tests/BadConstantPool").toByteArray();
bytes[10] = (byte) tag; // first constant pool entry follows magic/version/count
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,11 @@
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.util.function.Predicate;
import java.util.stream.Stream;
import org.junit.jupiter.params.provider.Arguments;

/** Small fixtures with explicit JVM descriptors so expectations don't use production conversion. */
final class MatcherTestSupport {
private MatcherTestSupport() {}

static Stream<Arguments> types() {
return Stream.of(
Arguments.of("boolean", boolean.class, "Z"),
Arguments.of("byte", byte.class, "B"),
Arguments.of("char", char.class, "C"),
Arguments.of("short", short.class, "S"),
Arguments.of("int", int.class, "I"),
Arguments.of("long", long.class, "J"),
Arguments.of("float", float.class, "F"),
Arguments.of("double", double.class, "D"),
Arguments.of("java.lang.String", String.class, "Ljava/lang/String;"),
Arguments.of("java/lang/String", String.class, "Ljava/lang/String;"),
Arguments.of("int[][]", int[][].class, "[[I"),
Arguments.of("java.lang.String[][]", String[][].class, "[[Ljava/lang/String;"));
}

static FieldOutline fieldOutline(String descriptor) {
return fieldOutline(0, "value", descriptor);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,10 @@
import java.lang.reflect.Modifier;
import java.util.Collections;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.tabletest.junit.TableTest;

class MethodMatcherTest {
@ParameterizedTest
@MethodSource("datadog.instrument.classmatch.MatcherTestSupport#types")
@TableTest(resource = "types.table")
void exactSignatureOverloads(String name, Class<?> type, String descriptor) {
MethodOutline candidate = methodOutline("(" + descriptor + descriptor + ")" + descriptor);
assertMatches(method("execute").parameters(name, name).returning(name), candidate, true);
Expand All @@ -43,8 +40,7 @@ void exactSignatureOverloads(String name, Class<?> type, String descriptor) {
assertMatches(method().noParameters(), candidate, false);
}

@ParameterizedTest
@MethodSource("datadog.instrument.classmatch.MatcherTestSupport#types")
@TableTest(resource = "types.table")
void exactReturnTypesDistinguishArraysFromTheirComponents(
String name, Class<?> type, String descriptor) {
MethodOutline scalar = methodOutline("()" + descriptor);
Expand Down Expand Up @@ -121,9 +117,15 @@ void objectParameterPredicatesAndReturnPredicates() {
assertMatches(method().returning(type("java.lang.Object")), candidate, false);
}

@ParameterizedTest
@ValueSource(
strings = {"()V", "()I", "()[I", "()[[Ljava/lang/String;", "(I)V", "([Ljava/lang/String;)V"})
@TableTest({
"descriptor ",
"'()V' ",
"'()I' ",
"'()[I' ",
"'()[[Ljava/lang/String;'",
"'(I)V' ",
"'([Ljava/lang/String;)V'"
})
void unsupportedOrAbsentTypesDoNotInvokeTypePredicates(String descriptor) {
TypeMatcher unexpected =
t -> {
Expand Down
Loading
Loading