diff --git a/dd-java-agent/instrumentation/robolectric-4.13/build.gradle b/dd-java-agent/instrumentation/robolectric-4.13/build.gradle index 2ac5d08d097..c9cd551e9e1 100644 --- a/dd-java-agent/instrumentation/robolectric-4.13/build.gradle +++ b/dd-java-agent/instrumentation/robolectric-4.13/build.gradle @@ -7,10 +7,13 @@ muzzle { pass { group = 'org.robolectric' module = 'robolectric' - versions = '[4.13,4.17)' + 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' + // Robolectric loads the selected Android SDK dynamically, so android-all is not a transitive + // dependency even though android.os.Build is available when the helper runs. + extraDependency 'org.robolectric:android-all:14-robolectric-10818077' } } @@ -32,6 +35,7 @@ dependencies { compileOnly(group: 'org.robolectric', name: 'robolectric', version: '4.16.1') { exclude group: 'androidx.test', module: 'monitor' } + compileOnly group: 'org.robolectric', name: 'android-all', version: '14-robolectric-10818077' // 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' diff --git a/dd-java-agent/instrumentation/robolectric-4.13/gradle.lockfile b/dd-java-agent/instrumentation/robolectric-4.13/gradle.lockfile index 721b02f69ba..49f762c7d94 100644 --- a/dd-java-agent/instrumentation/robolectric-4.13/gradle.lockfile +++ b/dd-java-agent/instrumentation/robolectric-4.13/gradle.lockfile @@ -119,6 +119,7 @@ org.ow2.asm:asm-tree:9.8=compileClasspath org.ow2.asm:asm-util:9.10.1=spotbugs org.ow2.asm:asm-util:9.7.1=testRuntimeClasspath org.ow2.asm:asm:9.10.1=buildTimeInstrumentationPlugin,compileClasspath,muzzleTooling,runtimeClasspath,spotbugs,testCompileClasspath,testRuntimeClasspath +org.robolectric:android-all:14-robolectric-10818077=compileClasspath org.robolectric:annotations:4.16.1=compileClasspath org.robolectric:junit:4.16.1=compileClasspath org.robolectric:nativeruntime:4.16.1=compileClasspath diff --git a/dd-java-agent/instrumentation/robolectric-4.13/src/main/java/datadog/trace/instrumentation/robolectric/AndroidVersionUtils.java b/dd-java-agent/instrumentation/robolectric-4.13/src/main/java/datadog/trace/instrumentation/robolectric/AndroidVersionUtils.java new file mode 100644 index 00000000000..15ea07ab329 --- /dev/null +++ b/dd-java-agent/instrumentation/robolectric-4.13/src/main/java/datadog/trace/instrumentation/robolectric/AndroidVersionUtils.java @@ -0,0 +1,51 @@ +package datadog.trace.instrumentation.robolectric; + +public final class AndroidVersionUtils { + + private static final String[] CODENAMES = { + null, + "BASE", + "BASE_1_1", + "CUPCAKE", + "DONUT", + "ECLAIR", + "ECLAIR_0_1", + "ECLAIR_MR1", + "FROYO", + "GINGERBREAD", + "GINGERBREAD_MR1", + "HONEYCOMB", + "HONEYCOMB_MR1", + "HONEYCOMB_MR2", + "ICE_CREAM_SANDWICH", + "ICE_CREAM_SANDWICH_MR1", + "JELLY_BEAN", + "JELLY_BEAN_MR1", + "JELLY_BEAN_MR2", + "KITKAT", + "KITKAT_WATCH", + "LOLLIPOP", + "LOLLIPOP_MR1", + "M", + "N", + "N_MR1", + "O", + "O_MR1", + "P", + "Q", + "R", + "S", + "S_V2", + "TIRAMISU", + "UPSIDE_DOWN_CAKE", + "VANILLA_ICE_CREAM", + "BAKLAVA", + "CINNAMON_BUN" + }; + + private AndroidVersionUtils() {} + + public static String codename(int apiLevel) { + return apiLevel > 0 && apiLevel < CODENAMES.length ? CODENAMES[apiLevel] : null; + } +} diff --git a/dd-java-agent/instrumentation/robolectric-4.13/src/main/java/datadog/trace/instrumentation/robolectric/RobolectricInstrumentation.java b/dd-java-agent/instrumentation/robolectric-4.13/src/main/java/datadog/trace/instrumentation/robolectric/RobolectricInstrumentation.java index d58de1dc64b..f6302b05a8c 100644 --- a/dd-java-agent/instrumentation/robolectric-4.13/src/main/java/datadog/trace/instrumentation/robolectric/RobolectricInstrumentation.java +++ b/dd-java-agent/instrumentation/robolectric-4.13/src/main/java/datadog/trace/instrumentation/robolectric/RobolectricInstrumentation.java @@ -37,7 +37,9 @@ public ElementMatcher hierarchyMatcher() { @Override public String[] helperClassNames() { - return new String[] {packageName + ".RobolectricTestAnnotator"}; + return new String[] { + packageName + ".AndroidVersionUtils", packageName + ".RobolectricTestAnnotator" + }; } @Override diff --git a/dd-java-agent/instrumentation/robolectric-4.13/src/main/java/datadog/trace/instrumentation/robolectric/RobolectricTestAnnotator.java b/dd-java-agent/instrumentation/robolectric-4.13/src/main/java/datadog/trace/instrumentation/robolectric/RobolectricTestAnnotator.java index 8396ddc8c2d..ee118b46092 100644 --- a/dd-java-agent/instrumentation/robolectric-4.13/src/main/java/datadog/trace/instrumentation/robolectric/RobolectricTestAnnotator.java +++ b/dd-java-agent/instrumentation/robolectric-4.13/src/main/java/datadog/trace/instrumentation/robolectric/RobolectricTestAnnotator.java @@ -1,5 +1,6 @@ package datadog.trace.instrumentation.robolectric; +import android.os.Build; import datadog.trace.api.gateway.RequestContext; import datadog.trace.api.gateway.RequestContextSlot; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; @@ -13,7 +14,6 @@ import java.util.regex.Pattern; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; -import org.robolectric.versioning.AndroidVersions; public final class RobolectricTestAnnotator { @@ -40,10 +40,10 @@ public static void annotate() { } 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()); + span.setTag(Tags.TEST_ANDROID_RELEASE, Build.VERSION.RELEASE); + String androidCodename = AndroidVersionUtils.codename(apiLevel); + if (androidCodename != null) { + span.setTag(Tags.TEST_ANDROID_CODENAME, androidCodename); } String robolectricVersion = robolectricVersion(); if (robolectricVersion != null) { diff --git a/dd-smoke-tests/gradle/src/test/java/datadog/smoketest/GradleDaemonSmokeTest.java b/dd-smoke-tests/gradle/src/test/java/datadog/smoketest/GradleDaemonSmokeTest.java index 37cd8c8a99c..84b6f6a4ae8 100644 --- a/dd-smoke-tests/gradle/src/test/java/datadog/smoketest/GradleDaemonSmokeTest.java +++ b/dd-smoke-tests/gradle/src/test/java/datadog/smoketest/GradleDaemonSmokeTest.java @@ -144,14 +144,20 @@ void testNew( } @TableTest({ - "scenario | gradleVersion | projectName | expectedTraces", - "robolectric-latest | latest | test-succeed-robolectric | 7 " + "scenario | gradleVersion | robolectricVersion | expectedVersion | projectName | expectedTraces", + "robolectric-4.16 | latest | 4.16.1 | 4.16.1 | test-succeed-robolectric | 7 ", + "robolectric-latest | latest | + | any | test-succeed-robolectric | 7 " }) @ParameterizedTest - void testRobolectric(String gradleVersion, String projectName, int expectedTraces) + void testRobolectric( + String gradleVersion, + String robolectricVersion, + String expectedVersion, + String projectName, + int expectedTraces) throws IOException { Assumptions.assumeTrue( - JavaVirtualMachine.isJavaVersionBetween(17, 22), "Robolectric 4.16 supports JDK 17-21"); + JavaVirtualMachine.isJavaVersionBetween(17, 22), "Robolectric supports JDK 17-21"); Assumptions.assumeFalse( OperatingSystem.architecture().isArm64(), "Robolectric does not support arm64 (missing native runtime binaries, follow https://github.com/robolectric/robolectric/issues/9166)"); @@ -163,15 +169,43 @@ void testRobolectric(String gradleVersion, String projectName, int expectedTrace givenGradleProjectProperties(); ensureDependenciesDownloaded(gradleVersion); - BuildResult buildResult = runGradleTests(gradleVersion, true, false); + Map additionalEnvVars = + Collections.singletonMap("SMOKE_TEST_ROBOLECTRIC_VERSION", robolectricVersion); + BuildResult buildResult = runGradleTests(gradleVersion, true, false, additionalEnvVars); assertBuildSuccessful(buildResult); + List> events = mockBackend.waitForEvents(expectedTraces); + assertRobolectricVersion(events, expectedVersion); verifyEventsAndCoverages( projectName, "gradle", gradleVersion, - mockBackend.waitForEvents(expectedTraces), - mockBackend.waitForCoverages(0)); + events, + mockBackend.waitForCoverages(0), + Collections.singletonList("content.meta.['test.android.robolectric.version']")); + } + + private static void assertRobolectricVersion( + List> events, String expectedVersion) { + int taggedEvents = 0; + for (Map event : events) { + Object content = event.get("content"); + if (!(content instanceof Map)) { + continue; + } + Object meta = ((Map) content).get("meta"); + if (!(meta instanceof Map)) { + continue; + } + Object version = ((Map) meta).get("test.android.robolectric.version"); + if (version != null) { + if (!"any".equals(expectedVersion)) { + assertEquals(expectedVersion, version); + } + taggedEvents++; + } + } + assertEquals(2, taggedEvents); } @TableTest({ @@ -371,6 +405,16 @@ private void givenGradleProjectProperties(Map additionalArgs) th private BuildResult runGradleTests( String gradleVersion, boolean successExpected, boolean configurationCache) throws IOException { + return runGradleTests( + gradleVersion, successExpected, configurationCache, Collections.emptyMap()); + } + + private BuildResult runGradleTests( + String gradleVersion, + boolean successExpected, + boolean configurationCache, + Map additionalEnvVars) + throws IOException { List arguments = new java.util.ArrayList<>(Arrays.asList("test", "--stacktrace")); if (gradleVersion.compareTo("4.5") > 0) { // warning mode available starting from Gradle 4.5 @@ -379,7 +423,7 @@ private BuildResult runGradleTests( if (configurationCache) { arguments.addAll(Arrays.asList("--configuration-cache", "--rerun-tasks")); } - return runGradle(gradleVersion, arguments, successExpected); + return runGradle(gradleVersion, arguments, successExpected, additionalEnvVars); } /** @@ -417,6 +461,15 @@ private void ensureDependenciesDownloaded(String gradleVersion) { private BuildResult runGradle( String gradleVersion, List arguments, boolean successExpected) throws IOException { + return runGradle(gradleVersion, arguments, successExpected, Collections.emptyMap()); + } + + private BuildResult runGradle( + String gradleVersion, + List arguments, + boolean successExpected, + Map additionalEnvVars) + throws IOException { Map buildEnv = new HashMap<>(); buildEnv.put("GRADLE_ARGS", ""); buildEnv.put("GRADLE_OPTS", ""); @@ -425,6 +478,7 @@ private BuildResult runGradle( buildEnv.put( GradleDistribution.GRADLE_DISTRIBUTION_URL_ENV, GradleDistribution.uriFor(gradleVersion).toString()); + buildEnv.putAll(additionalEnvVars); String mavenRepositoryProxy = System.getenv("MAVEN_REPOSITORY_PROXY"); if (mavenRepositoryProxy != null) { diff --git a/dd-smoke-tests/gradle/src/test/resources/test-succeed-robolectric/build.gradleTest b/dd-smoke-tests/gradle/src/test/resources/test-succeed-robolectric/build.gradleTest index 566eba49ada..99f5d4d702e 100644 --- a/dd-smoke-tests/gradle/src/test/resources/test-succeed-robolectric/build.gradleTest +++ b/dd-smoke-tests/gradle/src/test/resources/test-succeed-robolectric/build.gradleTest @@ -96,7 +96,7 @@ configurations.configureEach { dependencies { testImplementation 'junit:junit:4.13.2' - testImplementation 'org.robolectric:robolectric:4.16.1' + testImplementation "org.robolectric:robolectric:${System.getenv('SMOKE_TEST_ROBOLECTRIC_VERSION')}" // Pre-built Android SDK jar for the level the fixtures configure. testImplementation 'org.robolectric:android-all:14-robolectric-10818077' // androidx.test:core pulls in androidx.test:monitor (InstrumentationRegistry); ext:junit provides diff --git a/dd-smoke-tests/gradle/src/test/resources/test-succeed-robolectric/events.ftl b/dd-smoke-tests/gradle/src/test/resources/test-succeed-robolectric/events.ftl index b08072b29ab..d0d7ed7d09d 100644 --- a/dd-smoke-tests/gradle/src/test/resources/test-succeed-robolectric/events.ftl +++ b/dd-smoke-tests/gradle/src/test/resources/test-succeed-robolectric/events.ftl @@ -387,9 +387,9 @@ "runtime.vendor" : ${content_meta_runtime_vendor}, "runtime.version" : ${content_meta_runtime_version}, "span.kind" : "test", - "test.android.codename" : "U", + "test.android.codename" : "UPSIDE_DOWN_CAKE", "test.android.release" : "14", - "test.android.robolectric.version" : "4.16.1", + "test.android.robolectric.version" : ${content_meta_test_android_robolectric_version}, "test.final_status" : "pass", "test.framework" : "junit4", "test.framework_version" : "4.13.2", @@ -505,9 +505,9 @@ "runtime.vendor" : ${content_meta_runtime_vendor}, "runtime.version" : ${content_meta_runtime_version}, "span.kind" : "test", - "test.android.codename" : "U", + "test.android.codename" : "UPSIDE_DOWN_CAKE", "test.android.release" : "14", - "test.android.robolectric.version" : "4.16.1", + "test.android.robolectric.version" : ${content_meta_test_android_robolectric_version}, "test.final_status" : "pass", "test.framework" : "junit4", "test.framework_version" : "4.13.2", @@ -688,4 +688,4 @@ }, "type" : "span", "version" : 1 -} ] \ No newline at end of file +} ]