Skip to content
Open
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 .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@
/dd-java-agent/instrumentation/jacoco-0.8.9/ @DataDog/ci-app-libraries
/dd-java-agent/instrumentation/junit @DataDog/ci-app-libraries
/dd-java-agent/instrumentation/karate-1.0/ @DataDog/ci-app-libraries
/dd-java-agent/instrumentation/robolectric-4.13/ @DataDog/ci-app-libraries
/dd-java-agent/instrumentation/scalatest-3.0.8/ @DataDog/ci-app-libraries
/dd-java-agent/instrumentation/selenium-3.13/ @DataDog/ci-app-libraries
/dd-java-agent/instrumentation/testng/ @DataDog/ci-app-libraries
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import datadog.trace.api.civisibility.telemetry.tag.EventType;
import datadog.trace.api.civisibility.telemetry.tag.FailedTestReplayEnabled;
import datadog.trace.api.civisibility.telemetry.tag.HasFailedAllRetries;
import datadog.trace.api.civisibility.telemetry.tag.IsAndroidEmulated;
import datadog.trace.api.civisibility.telemetry.tag.IsAttemptToFix;
import datadog.trace.api.civisibility.telemetry.tag.IsDisabled;
import datadog.trace.api.civisibility.telemetry.tag.IsModified;
Expand Down Expand Up @@ -323,7 +324,8 @@ public void end(@Nullable Long endTime) {
span.getTag(Tags.TEST_IS_RUM_ACTIVE) != null ? IsRum.TRUE : null,
CIConstants.SELENIUM_BROWSER_DRIVER.equals(span.getTag(Tags.TEST_BROWSER_DRIVER))
? BrowserDriver.SELENIUM
: null);
: null,
span.getTag(Tags.TEST_ANDROID_API_LEVEL) != null ? IsAndroidEmulated.TRUE : null);
}

/**
Expand Down
36 changes: 36 additions & 0 deletions dd-java-agent/instrumentation/robolectric-4.13/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
muzzle {
extraRepository('google', 'https://maven.google.com')
pass {
group = 'org.robolectric'
module = 'robolectric'
versions = '[4.13,)'
// androidx.test:monitor is an Android archive (.aar) that a JVM configuration cannot consume;
// it is not referenced by the advice/helper. Mirror the compileOnly exclusion below.
excludeDependency 'androidx.test:monitor'
}
}

apply from: "$rootDir/gradle/java.gradle"

repositories {
// Robolectric drags in androidx.test.* transitive dependencies that are published only to
// Google's Maven. Declared after java.gradle so Maven Central is tried first
maven {
url = 'https://maven.google.com'
content {
includeGroupAndSubgroups 'androidx'
}
}
}

dependencies {
// Instrumentation is validated through a scenario in `GradleDaemonSmokeTest`.
// Exclude androidx.test:monitor: it is an Android archive (.aar) published only to Google's Maven
// repository, is not referenced by the advice/helper.
compileOnly(group: 'org.robolectric', name: 'robolectric', version: '4.16.1') {
exclude group: 'androidx.test', module: 'monitor'
}
// RobolectricTestRunner extends JUnit's BlockJUnit4ClassRunner; JUnit must be on the compile
// classpath so its supertypes resolve (both javac and forbiddenApis walk the class hierarchy).
compileOnly group: 'junit', name: 'junit', version: '4.13.2'
}
150 changes: 150 additions & 0 deletions dd-java-agent/instrumentation/robolectric-4.13/gradle.lockfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
# To regenerate this file, run: ./gradlew :dd-java-agent:instrumentation:robolectric-4.13:dependencies --write-locks
cafe.cryptography:curve25519-elisabeth:0.1.0=testRuntimeClasspath
cafe.cryptography:ed25519-elisabeth:0.1.0=testRuntimeClasspath
ch.qos.logback:logback-classic:1.2.13=testCompileClasspath,testRuntimeClasspath
ch.qos.logback:logback-core:1.2.13=testCompileClasspath,testRuntimeClasspath
com.almworks.sqlite4java:sqlite4java:1.0.392=compileClasspath
com.blogspot.mydailyjava:weak-lock-free:0.17=buildTimeInstrumentationPlugin,compileClasspath,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.datadoghq.okhttp3:okhttp:3.12.15=testCompileClasspath,testRuntimeClasspath
com.datadoghq.okio:okio:1.17.6=testCompileClasspath,testRuntimeClasspath
com.datadoghq:dd-instrument-java:0.0.4=buildTimeInstrumentationPlugin,compileClasspath,muzzleBootstrap,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.datadoghq:dd-javac-plugin-client:0.2.2=buildTimeInstrumentationPlugin,compileClasspath,muzzleBootstrap,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.datadoghq:java-dogstatsd-client:4.4.5=testRuntimeClasspath
com.datadoghq:sketches-java:0.8.3=testRuntimeClasspath
com.github.javaparser:javaparser-core:3.25.6=codenarc
com.github.jnr:jffi:1.3.15=testRuntimeClasspath
com.github.jnr:jnr-a64asm:1.0.0=testRuntimeClasspath
com.github.jnr:jnr-constants:0.10.4=testRuntimeClasspath
com.github.jnr:jnr-enxio:0.32.20=testRuntimeClasspath
com.github.jnr:jnr-ffi:2.2.19=testRuntimeClasspath
com.github.jnr:jnr-posix:3.1.22=testRuntimeClasspath
com.github.jnr:jnr-unixsocket:0.38.25=testRuntimeClasspath
com.github.jnr:jnr-x86asm:1.0.2=testRuntimeClasspath
com.github.spotbugs:spotbugs-annotations:4.9.8=compileClasspath,spotbugs
com.github.spotbugs:spotbugs:4.9.8=spotbugs
com.github.stephenc.jcip:jcip-annotations:1.0-1=spotbugs
com.google.auto.service:auto-service-annotations:1.1.1=annotationProcessor,compileClasspath,testAnnotationProcessor,testCompileClasspath
com.google.auto.service:auto-service:1.1.1=annotationProcessor,testAnnotationProcessor
com.google.auto.value:auto-value-annotations:1.11.0=compileClasspath
com.google.auto:auto-common:1.2.1=annotationProcessor,testAnnotationProcessor
com.google.code.findbugs:jsr305:3.0.2=annotationProcessor,compileClasspath,spotbugs,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
com.google.code.gson:gson:2.13.2=spotbugs
com.google.errorprone:error_prone_annotations:2.18.0=annotationProcessor,testAnnotationProcessor
com.google.errorprone:error_prone_annotations:2.36.0=compileClasspath
com.google.errorprone:error_prone_annotations:2.41.0=spotbugs
com.google.errorprone:error_prone_annotations:2.47.0=testCompileClasspath,testRuntimeClasspath
com.google.guava:failureaccess:1.0.1=annotationProcessor,testAnnotationProcessor
com.google.guava:failureaccess:1.0.3=compileClasspath,testCompileClasspath,testRuntimeClasspath
com.google.guava:guava:32.0.1-jre=annotationProcessor,testAnnotationProcessor
com.google.guava:guava:33.4.8-jre=compileClasspath
com.google.guava:guava:33.6.0-jre=testCompileClasspath,testRuntimeClasspath
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=annotationProcessor,compileClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
com.google.j2objc:j2objc-annotations:2.8=annotationProcessor,testAnnotationProcessor
com.google.j2objc:j2objc-annotations:3.0.0=compileClasspath
com.google.j2objc:j2objc-annotations:3.1=testCompileClasspath,testRuntimeClasspath
com.google.re2j:re2j:1.8=testRuntimeClasspath
com.ibm.icu:icu4j:77.1=compileClasspath
com.squareup.moshi:moshi:1.11.0=testCompileClasspath,testRuntimeClasspath
com.squareup.okhttp3:logging-interceptor:3.12.12=testCompileClasspath,testRuntimeClasspath
com.squareup.okhttp3:okhttp:3.12.12=testCompileClasspath,testRuntimeClasspath
com.squareup.okio:okio:1.17.5=testCompileClasspath,testRuntimeClasspath
com.thoughtworks.qdox:qdox:1.12.1=codenarc
commons-fileupload:commons-fileupload:1.5=testCompileClasspath,testRuntimeClasspath
commons-io:commons-io:2.11.0=testCompileClasspath,testRuntimeClasspath
commons-io:commons-io:2.20.0=spotbugs
de.thetaphi:forbiddenapis:3.10=compileClasspath,testCompileClasspath,testRuntimeClasspath
io.leangen.geantyref:geantyref:1.3.16=testRuntimeClasspath
io.sqreen:libsqreen:17.3.0=testRuntimeClasspath
javax.annotation:javax.annotation-api:1.3.2=compileClasspath
javax.inject:javax.inject:1=compileClasspath
javax.servlet:javax.servlet-api:3.1.0=testCompileClasspath,testRuntimeClasspath
jaxen:jaxen:2.0.0=spotbugs
junit:junit:4.13.2=compileClasspath,testRuntimeClasspath
net.bytebuddy:byte-buddy-agent:1.18.10=buildTimeInstrumentationPlugin,compileClasspath,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
net.bytebuddy:byte-buddy:1.18.10=buildTimeInstrumentationPlugin,compileClasspath,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
net.java.dev.jna:jna-platform:5.8.0=testRuntimeClasspath
net.java.dev.jna:jna:5.8.0=testRuntimeClasspath
net.sf.saxon:Saxon-HE:12.9=spotbugs
org.apache.ant:ant-antlr:1.10.14=codenarc
org.apache.ant:ant-junit:1.10.14=codenarc
org.apache.bcel:bcel:6.11.0=spotbugs
org.apache.commons:commons-lang3:3.19.0=spotbugs
org.apache.commons:commons-text:1.14.0=spotbugs
org.apache.logging.log4j:log4j-api:2.25.2=spotbugs
org.apache.logging.log4j:log4j-core:2.25.2=spotbugs
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath,testRuntimeClasspath
org.bouncycastle:bcprov-jdk18on:1.81=compileClasspath
org.checkerframework:checker-qual:3.33.0=annotationProcessor,testAnnotationProcessor
org.codehaus.groovy:groovy-ant:3.0.23=codenarc
org.codehaus.groovy:groovy-docgenerator:3.0.23=codenarc
org.codehaus.groovy:groovy-groovydoc:3.0.23=codenarc
org.codehaus.groovy:groovy-json:3.0.23=codenarc
org.codehaus.groovy:groovy-json:3.0.25=testCompileClasspath,testRuntimeClasspath
org.codehaus.groovy:groovy-templates:3.0.23=codenarc
org.codehaus.groovy:groovy-xml:3.0.23=codenarc
org.codehaus.groovy:groovy:3.0.23=codenarc
org.codehaus.groovy:groovy:3.0.25=testCompileClasspath,testRuntimeClasspath
org.codenarc:CodeNarc:3.7.0=codenarc
org.dom4j:dom4j:2.2.0=spotbugs
org.gmetrics:GMetrics:2.1.0=codenarc
org.hamcrest:hamcrest-core:1.3=compileClasspath,testRuntimeClasspath
org.hamcrest:hamcrest:3.0=testCompileClasspath,testRuntimeClasspath
org.jctools:jctools-core-jdk11:4.0.6=testRuntimeClasspath
org.jctools:jctools-core:4.0.6=testRuntimeClasspath
org.jspecify:jspecify:1.0.0=compileClasspath,testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-api:5.14.1=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-engine:5.14.1=testRuntimeClasspath
org.junit.jupiter:junit-jupiter-params:5.14.1=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter:5.14.1=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-commons:1.14.1=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-engine:1.14.1=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-launcher:1.14.1=testRuntimeClasspath
org.junit.platform:junit-platform-runner:1.14.1=testRuntimeClasspath
org.junit.platform:junit-platform-suite-api:1.14.1=testRuntimeClasspath
org.junit.platform:junit-platform-suite-commons:1.14.1=testRuntimeClasspath
org.junit:junit-bom:5.14.0=spotbugs
org.junit:junit-bom:5.14.1=testCompileClasspath,testRuntimeClasspath
org.mockito:mockito-core:4.4.0=testRuntimeClasspath
org.objenesis:objenesis:3.3=testCompileClasspath,testRuntimeClasspath
org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath
org.ow2.asm:asm-analysis:9.7.1=testRuntimeClasspath
org.ow2.asm:asm-analysis:9.9=spotbugs
org.ow2.asm:asm-commons:9.8=compileClasspath
org.ow2.asm:asm-commons:9.9=spotbugs
org.ow2.asm:asm-commons:9.9.1=testRuntimeClasspath
org.ow2.asm:asm-tree:9.8=compileClasspath
org.ow2.asm:asm-tree:9.9=spotbugs
org.ow2.asm:asm-tree:9.9.1=testRuntimeClasspath
org.ow2.asm:asm-util:9.7.1=testRuntimeClasspath
org.ow2.asm:asm-util:9.9=spotbugs
org.ow2.asm:asm:9.9=spotbugs
org.ow2.asm:asm:9.9.1=buildTimeInstrumentationPlugin,compileClasspath,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.robolectric:annotations:4.16.1=compileClasspath
org.robolectric:junit:4.16.1=compileClasspath
org.robolectric:nativeruntime:4.16.1=compileClasspath
org.robolectric:pluginapi:4.16.1=compileClasspath
org.robolectric:plugins-maven-dependency-resolver:4.16.1=compileClasspath
org.robolectric:resources:4.16.1=compileClasspath
org.robolectric:robolectric:4.16.1=compileClasspath
org.robolectric:sandbox:4.16.1=compileClasspath
org.robolectric:shadowapi:4.16.1=compileClasspath
org.robolectric:shadows-framework:4.16.1=compileClasspath
org.robolectric:utils-reflector:4.16.1=compileClasspath
org.robolectric:utils:4.16.1=compileClasspath
org.slf4j:jcl-over-slf4j:1.7.30=testCompileClasspath,testRuntimeClasspath
org.slf4j:jul-to-slf4j:1.7.30=testCompileClasspath,testRuntimeClasspath
org.slf4j:log4j-over-slf4j:1.7.30=testCompileClasspath,testRuntimeClasspath
org.slf4j:slf4j-api:1.7.30=buildTimeInstrumentationPlugin,compileClasspath,muzzleBootstrap,muzzleTooling,runtimeClasspath
org.slf4j:slf4j-api:1.7.32=testCompileClasspath,testRuntimeClasspath
org.slf4j:slf4j-api:2.0.17=spotbugs,spotbugsSlf4j
org.slf4j:slf4j-simple:2.0.17=spotbugsSlf4j
org.snakeyaml:snakeyaml-engine:2.9=buildTimeInstrumentationPlugin,muzzleTooling,runtimeClasspath,testRuntimeClasspath
org.spockframework:spock-bom:2.4-groovy-3.0=testCompileClasspath,testRuntimeClasspath
org.spockframework:spock-core:2.4-groovy-3.0=testCompileClasspath,testRuntimeClasspath
org.tabletest:tabletest-junit:1.2.1=testCompileClasspath,testRuntimeClasspath
org.tabletest:tabletest-parser:1.2.0=testCompileClasspath,testRuntimeClasspath
org.xmlresolver:xmlresolver:5.3.3=spotbugs
empty=spotbugsPlugins
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package datadog.trace.instrumentation.robolectric;

import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.implementsInterface;
import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;

import com.google.auto.service.AutoService;
import datadog.trace.agent.tooling.Instrumenter;
import datadog.trace.agent.tooling.InstrumenterModule;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;

/**
* Captures the emulated Android SDK for tests running under Robolectric.
*
* <p>Robolectric establishes the emulated SDK in {@code TestEnvironment#setUpApplicationState},
* which runs on the per-SDK sandbox "main" thread right before the test body (the SDK is not yet
* set when the JUnit test-start event fires, and is torn down before the finish event).
*/
@AutoService(InstrumenterModule.class)
public class RobolectricInstrumentation extends InstrumenterModule.CiVisibility
implements Instrumenter.ForTypeHierarchy, Instrumenter.HasMethodAdvice {

public RobolectricInstrumentation() {
super("ci-visibility", "robolectric");
}

@Override
public String hierarchyMarkerType() {
return "org.robolectric.internal.TestEnvironment";
}

@Override
public ElementMatcher<TypeDescription> hierarchyMatcher() {
return implementsInterface(named(hierarchyMarkerType()));
}

@Override
public String[] helperClassNames() {
return new String[] {packageName + ".RobolectricTestAnnotator"};
}

@Override
public void methodAdvice(MethodTransformer transformer) {
transformer.applyAdvice(
named("setUpApplicationState"), getClass().getName() + "$SetUpApplicationStateAdvice");
}

public static class SetUpApplicationStateAdvice {
// onThrowable so the tags are still captured when setUpApplicationState fails (e.g. during
// manifest/resource initialization): the emulated SDK is set early in the method, and the test
// span still exists, so the failing test should carry the Android metadata too.
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void onExit() {
RobolectricTestAnnotator.annotate();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package datadog.trace.instrumentation.robolectric;

import datadog.trace.api.gateway.RequestContext;
import datadog.trace.api.gateway.RequestContextSlot;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import datadog.trace.bootstrap.instrumentation.api.AgentTracer;
import datadog.trace.bootstrap.instrumentation.api.Tags;
import java.io.File;
import java.net.URL;
import java.security.CodeSource;
import java.security.ProtectionDomain;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.versioning.AndroidVersions;

public final class RobolectricTestAnnotator {

/** Matches the version in a {@code robolectric-<version>.jar} file name. */
private static final Pattern ROBOLECTRIC_JAR = Pattern.compile("^robolectric-(.+)\\.jar$");

private RobolectricTestAnnotator() {}

public static void annotate() {
int apiLevel = RuntimeEnvironment.getApiLevel();
if (apiLevel <= 0) {
return;
}

AgentSpan span = AgentTracer.activeSpan();
if (span == null) {
return;
}
RequestContext requestContext = span.getRequestContext();
if (requestContext == null
|| requestContext.getData(RequestContextSlot.CI_VISIBILITY) == null) {
// The active span is not a CI Visibility test span; nothing to enrich.
return;
}

span.setTag(Tags.TEST_ANDROID_API_LEVEL, apiLevel);
AndroidVersions.AndroidRelease release = AndroidVersions.getReleaseForSdkInt(apiLevel);
if (release != null) {
span.setTag(Tags.TEST_ANDROID_RELEASE, release.getVersion());
span.setTag(Tags.TEST_ANDROID_CODENAME, release.getShortCode());
}
String robolectricVersion = robolectricVersion();
if (robolectricVersion != null) {
span.setTag(Tags.TEST_ANDROID_ROBOLECTRIC_VERSION, robolectricVersion);
}
}

private static String robolectricVersion() {
try {
// RuntimeEnvironment is re-loaded by the sandbox classloader with no CodeSource, but the
// runner runs outside the sandbox (it creates it), so it is delegated to the application
// classloader and its CodeSource points at the real robolectric-<version>.jar.
ProtectionDomain protectionDomain = RobolectricTestRunner.class.getProtectionDomain();
CodeSource codeSource = protectionDomain != null ? protectionDomain.getCodeSource() : null;
URL location = codeSource != null ? codeSource.getLocation() : null;
if (location == null) {
return null;
}
Matcher matcher = ROBOLECTRIC_JAR.matcher(new File(location.getPath()).getName());
return matcher.matches() ? matcher.group(1) : null;
} catch (Throwable t) {
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,34 @@ void testNew(
expectedCoverages);
}

@TableTest({
"scenario | gradleVersion | projectName | expectedTraces",
"robolectric-latest | latest | test-succeed-robolectric | 6 "
})
@ParameterizedTest
void testRobolectric(String gradleVersion, String projectName, int expectedTraces)
throws IOException {
Assumptions.assumeTrue(
JavaVirtualMachine.isJavaVersionBetween(17, 22), "Robolectric 4.16 supports JDK 17-21");

gradleVersion = resolveVersion(gradleVersion);
givenGradleVersionIsCompatibleWithCurrentJvm(gradleVersion);
givenGradleVersionIsSupportedByCurrentGradleTestKit(gradleVersion);
givenGradleProjectFiles(projectName);
givenGradleProjectProperties();
ensureDependenciesDownloaded(gradleVersion);

BuildResult buildResult = runGradleTests(gradleVersion, true, false);
assertBuildSuccessful(buildResult);

verifyEventsAndCoverages(
projectName,
"gradle",
gradleVersion,
mockBackend.waitForEvents(expectedTraces),
mockBackend.waitForCoverages(0));
}

// TODO: add back LATEST_GRADLE_VERSION after fixing ordering on Gradle 9.3.0
@TableTest({
"scenario | gradleVersion | projectName | flakyTests | expectedOrder | eventsNumber",
Expand Down
Loading