diff --git a/dd-java-agent/instrumentation/gradle/gradle-8.3/build.gradle b/dd-java-agent/instrumentation/gradle/gradle-8.3/build.gradle index 22fa1b97ca4..d21d33c0e5f 100644 --- a/dd-java-agent/instrumentation/gradle/gradle-8.3/build.gradle +++ b/dd-java-agent/instrumentation/gradle/gradle-8.3/build.gradle @@ -8,4 +8,6 @@ repositories { dependencies { compileOnly gradleApi() + testImplementation gradleApi() + testImplementation libs.junit.jupiter } diff --git a/dd-java-agent/instrumentation/gradle/gradle-8.3/src/main/groovy/datadog/trace/instrumentation/gradle/AndroidGradleUtils.groovy b/dd-java-agent/instrumentation/gradle/gradle-8.3/src/main/groovy/datadog/trace/instrumentation/gradle/AndroidGradleUtils.groovy index e45d581b14b..8ca6c921e56 100644 --- a/dd-java-agent/instrumentation/gradle/gradle-8.3/src/main/groovy/datadog/trace/instrumentation/gradle/AndroidGradleUtils.groovy +++ b/dd-java-agent/instrumentation/gradle/gradle-8.3/src/main/groovy/datadog/trace/instrumentation/gradle/AndroidGradleUtils.groovy @@ -1,66 +1,210 @@ package datadog.trace.instrumentation.gradle - import datadog.trace.api.civisibility.domain.BuildModuleLayout import datadog.trace.api.civisibility.domain.SourceSet +import kotlin.Unit +import kotlin.jvm.functions.Function1 +import org.gradle.api.Action import org.gradle.api.Project +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.Directory import org.gradle.api.file.FileTree +import org.gradle.api.file.RegularFile +import org.gradle.api.provider.ListProperty +import org.gradle.api.tasks.util.PatternSet import org.gradle.api.tasks.testing.Test import org.slf4j.Logger import org.slf4j.LoggerFactory -import java.nio.file.Files -import java.nio.file.Paths - +/** + * Captures Android production sources and classes during variant configuration and attaches them to + * the exact Gradle test task. The stored layout is materialized when that task starts. + */ class AndroidGradleUtils { private static final Logger LOGGER = LoggerFactory.getLogger(AndroidGradleUtils) - static BuildModuleLayout getAndroidModuleLayout(Project project, Test task) { - try { - def variant = getVariant(project, task) - if (variant == null) { - return null + private static final String MODULE_LAYOUT_EXTENSION_NAME = 'ddAndroidModuleLayout' + private static final String ANDROID_COMPONENTS_CONFIGURED = 'ddAndroidComponentsConfigured' + + private static final ANDROID_PLUGIN_IDS = [ + 'com.android.application', + 'com.android.library', + 'com.android.dynamic-feature', + 'com.android.test', + 'com.android.kotlin.multiplatform.library' + ] + + static void configure(Project project) { + for (String pluginId : ANDROID_PLUGIN_IDS) { + project.pluginManager.withPlugin(pluginId, new ConfigureAndroidPluginAction(project)) + } + } + + private static void configureAndroidComponents(Project project) { + def extraProperties = project.extensions.extraProperties + if (extraProperties.has(ANDROID_COMPONENTS_CONFIGURED)) { + return + } + + def androidComponents = project.extensions.findByName('androidComponents') + if (androidComponents == null) { + return + } + + extraProperties.set(ANDROID_COMPONENTS_CONFIGURED, true) + // AGP 8.x KMP exposes onVariant(Action), while standard Android plugins expose onVariants. + if (supportsMethod(androidComponents, 'selector', null)) { + androidComponents.onVariants( + androidComponents.selector().all(), new ConfigureVariantAction(project, false)) + } else { + androidComponents.onVariant(new ConfigureVariantAction(project, true)) + } + } + + private static void configureVariant( + Project project, variant, boolean useKmpCompilationSources) { + def unitTest = getProperty(variant, 'unitTest') + if (unitTest != null) { + configureTestComponent(project, variant, unitTest, useKmpCompilationSources) + } + + def hostTests = getProperty(variant, 'hostTests') + if (hostTests instanceof Map) { + for (def hostTest : hostTests.values()) { + configureTestComponent(project, variant, hostTest, useKmpCompilationSources) } + } + } - LOGGER.debug("Found Android plugin variant: {} for task {}", variant.name, task.path) - def sources = getSources(variant) - def destinations = getDestinations(variant, project) - return new BuildModuleLayout(Collections.singletonList(new SourceSet(SourceSet.Type.CODE, sources, destinations))) - } catch (Exception e) { - LOGGER.error("Could not get Android module layout for ${project.name} and ${task.path}", e) - return null + private static void configureTestComponent( + Project project, variant, testComponent, boolean useKmpCompilationSources) { + if (!supportsMethod(testComponent, 'configureTestTask', Function1)) { + return } + + // Let AGP identify the test task instead of deriving its variant relationship from task names. + testComponent.configureTestTask( + new ConfigureTestTaskFunction(project, variant, useKmpCompilationSources)) } - private static getVariant(Project project, Test task) { - def androidPlugin = project.plugins.findPlugin('android') - ?: project.plugins.findPlugin('android-library') - ?: project.plugins.findPlugin('com.android.application') - ?: project.plugins.findPlugin('com.android.library') + private static void configureTestTask( + Project project, variant, Test testTask, boolean useKmpCompilationSources) { + if (testTask.extensions.findByName(MODULE_LAYOUT_EXTENSION_NAME) != null) { + return + } - LOGGER.debug("Found Android plugin: {}", androidPlugin.getClass().getName()) - def variants - if (androidPlugin.class.simpleName == 'LibraryPlugin') { - variants = project.android.libraryVariants + def layout = new AndroidModuleLayout(project) + testTask.extensions.add(MODULE_LAYOUT_EXTENSION_NAME, layout) + if (useKmpCompilationSources) { + addKmpSourceDirectories(layout.sourceDirectories, project, variant) } else { - variants = project.android.applicationVariants + addSourceDirectories(layout.sourceDirectories, variant.sources, 'java') + addSourceDirectories(layout.sourceDirectories, variant.sources, 'kotlin') + } + + def artifactsClassLoader = variant.artifacts.class.classLoader + def scopeClass = artifactsClassLoader.loadClass('com.android.build.api.variant.ScopedArtifacts$Scope') + def projectScope + for (def scope : scopeClass.enumConstants) { + if (scope.name() == 'PROJECT') { + projectScope = scope + break + } + } + def classesArtifact = artifactsClassLoader + .loadClass('com.android.build.api.artifact.ScopedArtifact$CLASSES') + .getField('INSTANCE') + .get(null) + + // Use the production variant's PROJECT-scoped classes to exclude dependencies and test outputs. + def taskProvider = project.tasks.named(testTask.name, Test) + variant.artifacts.forScope(projectScope).use(taskProvider).toGet( + classesArtifact, + new ClassJarsFunction(), + new ClassDirectoriesFunction()) + + testTask.inputs.files(layout.classJars, layout.classDirectories) + .withPropertyName('ddAndroidProductionClasses') + } + + private static void addSourceDirectories( + ConfigurableFileCollection destination, sources, String language) { + def languageSources = getProperty(sources, language) + if (languageSources != null) { + destination.from(languageSources.all) + } + } + + private static void addKmpSourceDirectories( + ConfigurableFileCollection destination, Project project, variant) { + // AGP 8.x KMP variants have no sources API. Their roots come from the matching main compilation. + def kotlinExtension = project.extensions.findByName('kotlin') + def targets = kotlinExtension != null ? getProperty(kotlinExtension, 'targets') : null + if (targets == null) { + return + } + + def targetClass = variant.artifacts.class.classLoader + .loadClass('com.android.build.api.dsl.KotlinMultiplatformAndroidTarget') + for (def target : targets) { + if (!targetClass.isInstance(target)) { + continue + } + + def mainCompilation = target.compilations.findByName('main') + if (mainCompilation == null || mainCompilation.defaultSourceSet.name != variant.name) { + continue + } + + for (def sourceSet : mainCompilation.allKotlinSourceSets) { + destination.from(sourceSet.kotlin.srcDirs) + } + break } + } - for (def v : variants) { - if (task.path.endsWith("test${v.name.capitalize()}UnitTest") || task.path.endsWith("test${v.name.capitalize()}")) { - return v + private static boolean supportsMethod(Object object, String name, Class parameterType) { + for (def method : object.class.methods) { + if (method.name != name) { + continue + } + if (parameterType == null && method.parameterCount == 0) { + return true + } + if (method.parameterCount == 1 && method.parameterTypes[0] == parameterType) { + return true } } - return null + return false + } + + private static getProperty(object, String name) { + return object.hasProperty(name) ? object."$name" : null } - static Collection getSources(variant) { - List sources = [] - for (def srcs : variant.sourceSets.java.srcDirs) { - sources.addAll(srcs) + static BuildModuleLayout getAndroidModuleLayout(Project project, Test task) { + try { + def layout = getModuleLayout(task) + if (layout == null) { + return null + } + + def sources = layout.sourceDirectories.files + def destinations = getDestinations(project, layout) + if (sources.isEmpty() || destinations.isEmpty()) { + return null + } + + return new BuildModuleLayout(Collections.singletonList(new SourceSet(SourceSet.Type.CODE, sources, destinations))) + } catch (Exception e) { + LOGGER.error("Could not get Android module layout for ${project.name} and ${task.path}", e) + return null } - return sources + } + + private static AndroidModuleLayout getModuleLayout(Test task) { + return task.extensions.findByName(MODULE_LAYOUT_EXTENSION_NAME) as AndroidModuleLayout } private static final EXCLUDES = [ @@ -79,33 +223,107 @@ class AndroidGradleUtils { '**/*Module_*Factory.class' ] - private static Collection getDestinations(variant, Project project) { - def javaDestinations = getJavaDestinations(variant) - FileTree javaTree = project.fileTree(dir: javaDestinations, excludes: EXCLUDES) + private static Collection getDestinations(Project project, AndroidModuleLayout layout) { + PatternSet classFilePatterns = new PatternSet() + classFilePatterns.include('**/*.class') + classFilePatterns.exclude(EXCLUDES) - FileTree destinationsTree - def kotlinDestinations = "${project.buildDir}/tmp/kotlin-classes/${variant.name}" - if (Files.exists(Paths.get(kotlinDestinations))) { - def kotlinTree = project.fileTree(dir: kotlinDestinations, excludes: EXCLUDES) - destinationsTree = javaTree + kotlinTree - } else { - destinationsTree = javaTree + Set destinations = [] + for (Directory classDirectory : layout.classDirectories.get()) { + FileTree classFiles = project.fileTree(classDirectory.asFile) + destinations.addAll(classFiles.matching(classFilePatterns).files) + } + for (RegularFile classJar : layout.classJars.get()) { + FileTree classFiles = project.zipTree(classJar.asFile) + destinations.addAll(classFiles.matching(classFilePatterns).files) } - LOGGER.debug("Using destination tree: {}", destinationsTree.files) - return destinationsTree.files + LOGGER.debug("Using Android class destinations: {}", destinations) + return destinations } - private static getJavaDestinations(variant) { - def javaCompileTask - if (variant.hasProperty('javaCompileProvider')) { - javaCompileTask = variant.javaCompileProvider.get() - } else { - javaCompileTask = variant.javaCompile + private static class ConfigureAndroidPluginAction implements Action { + private final Project project + + ConfigureAndroidPluginAction(Project project) { + this.project = project + } + + @Override + void execute(Object ignored) { + try { + configureAndroidComponents(project) + } catch (Exception e) { + LOGGER.error("Could not configure Android module layout for ${project.name}", e) + } + } + } + + private static class ConfigureVariantAction implements Action { + private final Project project + private final boolean useKmpCompilationSources + + ConfigureVariantAction(Project project, boolean useKmpCompilationSources) { + this.project = project + this.useKmpCompilationSources = useKmpCompilationSources + } + + @Override + void execute(Object variant) { + try { + configureVariant(project, variant, useKmpCompilationSources) + } catch (Exception e) { + LOGGER.error("Could not configure Android variant for ${project.name}", e) + } + } + } + + private static class ConfigureTestTaskFunction implements Function1 { + private final Project project + private final Object variant + private final boolean useKmpCompilationSources + + ConfigureTestTaskFunction( + Project project, Object variant, boolean useKmpCompilationSources) { + this.project = project + this.variant = variant + this.useKmpCompilationSources = useKmpCompilationSources } - if (javaCompileTask.hasProperty('destinationDirectory')) { - return javaCompileTask.destinationDirectory.asFile.get() + + @Override + Unit invoke(Test testTask) { + try { + configureTestTask(project, variant, testTask, useKmpCompilationSources) + } catch (Exception e) { + LOGGER.error("Could not configure Android module layout for ${testTask.path}", e) + } + return Unit.INSTANCE + } + } + + private static class ClassJarsFunction implements Function1> { + @Override + ListProperty invoke(Test testTask) { + return getModuleLayout(testTask).classJars + } + } + + private static class ClassDirectoriesFunction implements Function1> { + @Override + ListProperty invoke(Test testTask) { + return getModuleLayout(testTask).classDirectories + } + } + + private static class AndroidModuleLayout { + final ConfigurableFileCollection sourceDirectories + final ListProperty classJars + final ListProperty classDirectories + + AndroidModuleLayout(Project project) { + sourceDirectories = project.objects.fileCollection() + classJars = project.objects.listProperty(RegularFile) + classDirectories = project.objects.listProperty(Directory) } - return javaCompileTask.destinationDir } } diff --git a/dd-java-agent/instrumentation/gradle/gradle-8.3/src/main/groovy/datadog/trace/instrumentation/gradle/CiVisibilityGradleListener.java b/dd-java-agent/instrumentation/gradle/gradle-8.3/src/main/groovy/datadog/trace/instrumentation/gradle/CiVisibilityGradleListener.java index 8e3f6220073..e325b54a5d9 100644 --- a/dd-java-agent/instrumentation/gradle/gradle-8.3/src/main/groovy/datadog/trace/instrumentation/gradle/CiVisibilityGradleListener.java +++ b/dd-java-agent/instrumentation/gradle/gradle-8.3/src/main/groovy/datadog/trace/instrumentation/gradle/CiVisibilityGradleListener.java @@ -16,6 +16,8 @@ import org.gradle.StartParameter; import org.gradle.api.Named; import org.gradle.api.Project; +import org.gradle.api.ProjectEvaluationListener; +import org.gradle.api.ProjectState; import org.gradle.api.internal.BuildDefinition; import org.gradle.api.internal.StartParameterInternal; import org.gradle.api.internal.project.taskfactory.TaskIdentity; @@ -28,6 +30,7 @@ import org.gradle.build.event.BuildEventsListenerRegistry; import org.gradle.execution.taskgraph.TaskListenerInternal; import org.gradle.internal.InternalBuildListener; +import org.gradle.internal.InternalListener; import org.gradle.internal.build.BuildState; import org.gradle.internal.build.NestedBuildState; import org.gradle.internal.build.RootBuildState; @@ -83,6 +86,11 @@ public CiVisibilityGradleListener( BuildEventsListenerRegistry buildEventsListenerRegistry) { this.gradle = gradle; + if (config.isCiVisibilityAutoConfigurationEnabled()) { + // AGP finalizes variant callbacks during project evaluation, before projectsEvaluated runs. + gradle.addProjectEvaluationListener(new AndroidProjectEvaluationListener()); + } + BuildServiceRegistry sharedServices = gradle.getSharedServices(); Provider ciVisibilityServiceProvider = sharedServices.registerIfAbsent( @@ -194,9 +202,12 @@ public void beforeExecute(TaskIdentity taskIdentity) { Map inputProperties = task.getInputs().getProperties(); BuildModuleLayout moduleLayout = (BuildModuleLayout) inputProperties.get(CiVisibilityPluginExtension.MODULE_LAYOUT_PROPERTY); - if ((moduleLayout == null || moduleLayout.getSourceSets().isEmpty()) - && project.getExtensions().findByName("android") != null) { - moduleLayout = AndroidGradleUtils.getAndroidModuleLayout(project, task); + if (isAndroid) { + BuildModuleLayout androidModuleLayout = + AndroidGradleUtils.getAndroidModuleLayout(project, task); + if (androidModuleLayout != null) { + moduleLayout = androidModuleLayout; + } } JavaAgent jacocoAgent = getJacocoAgent(task); @@ -255,4 +266,16 @@ public void afterExecute(TaskIdentity taskIdentity, TaskState state) { public void buildFinished(BuildResult result) { ciVisibilityService.onBuildFinish(result.getFailure()); } + + private static final class AndroidProjectEvaluationListener + implements ProjectEvaluationListener, InternalListener { + + @Override + public void beforeEvaluate(Project project) { + AndroidGradleUtils.configure(project); + } + + @Override + public void afterEvaluate(Project project, ProjectState state) {} + } } diff --git a/dd-java-agent/instrumentation/gradle/gradle-8.3/src/main/groovy/datadog/trace/instrumentation/gradle/GradlePluginInjectorInstrumentation.java b/dd-java-agent/instrumentation/gradle/gradle-8.3/src/main/groovy/datadog/trace/instrumentation/gradle/GradlePluginInjectorInstrumentation.java index 66d072b4509..85efeb05d29 100644 --- a/dd-java-agent/instrumentation/gradle/gradle-8.3/src/main/groovy/datadog/trace/instrumentation/gradle/GradlePluginInjectorInstrumentation.java +++ b/dd-java-agent/instrumentation/gradle/gradle-8.3/src/main/groovy/datadog/trace/instrumentation/gradle/GradlePluginInjectorInstrumentation.java @@ -50,7 +50,14 @@ public String[] helperClassNames() { packageName + ".CiVisibilityService", packageName + ".JavaCompilerPluginArgumentsProvider", packageName + ".TracerArgumentsProvider", + packageName + ".AndroidGradleUtils$AndroidModuleLayout", + packageName + ".AndroidGradleUtils$ClassDirectoriesFunction", + packageName + ".AndroidGradleUtils$ClassJarsFunction", + packageName + ".AndroidGradleUtils$ConfigureAndroidPluginAction", + packageName + ".AndroidGradleUtils$ConfigureTestTaskFunction", + packageName + ".AndroidGradleUtils$ConfigureVariantAction", packageName + ".AndroidGradleUtils", + packageName + ".CiVisibilityGradleListener$AndroidProjectEvaluationListener", packageName + ".CiVisibilityGradleListener", packageName + ".CiVisibilityPluginExtension", packageName + ".CiVisibilityPlugin" diff --git a/dd-java-agent/instrumentation/gradle/gradle-8.3/src/test/java/com/android/build/api/artifact/ScopedArtifact.java b/dd-java-agent/instrumentation/gradle/gradle-8.3/src/test/java/com/android/build/api/artifact/ScopedArtifact.java new file mode 100644 index 00000000000..c83d1d563e5 --- /dev/null +++ b/dd-java-agent/instrumentation/gradle/gradle-8.3/src/test/java/com/android/build/api/artifact/ScopedArtifact.java @@ -0,0 +1,11 @@ +package com.android.build.api.artifact; + +/** Test stand-in for the AGP classes artifact that AndroidGradleUtils loads reflectively. */ +public abstract class ScopedArtifact { + + public static final class CLASSES { + public static final CLASSES INSTANCE = new CLASSES(); + + private CLASSES() {} + } +} diff --git a/dd-java-agent/instrumentation/gradle/gradle-8.3/src/test/java/com/android/build/api/dsl/KotlinMultiplatformAndroidTarget.java b/dd-java-agent/instrumentation/gradle/gradle-8.3/src/test/java/com/android/build/api/dsl/KotlinMultiplatformAndroidTarget.java new file mode 100644 index 00000000000..08bdd0af2f8 --- /dev/null +++ b/dd-java-agent/instrumentation/gradle/gradle-8.3/src/test/java/com/android/build/api/dsl/KotlinMultiplatformAndroidTarget.java @@ -0,0 +1,4 @@ +package com.android.build.api.dsl; + +/** Test stand-in for the AGP target type used to identify Kotlin Multiplatform Android targets. */ +public interface KotlinMultiplatformAndroidTarget {} diff --git a/dd-java-agent/instrumentation/gradle/gradle-8.3/src/test/java/com/android/build/api/variant/ScopedArtifacts.java b/dd-java-agent/instrumentation/gradle/gradle-8.3/src/test/java/com/android/build/api/variant/ScopedArtifacts.java new file mode 100644 index 00000000000..29a57e35aae --- /dev/null +++ b/dd-java-agent/instrumentation/gradle/gradle-8.3/src/test/java/com/android/build/api/variant/ScopedArtifacts.java @@ -0,0 +1,10 @@ +package com.android.build.api.variant; + +/** Test stand-in for the AGP scope used to select only the current project's classes. */ +public interface ScopedArtifacts { + + enum Scope { + PROJECT, + ALL + } +} diff --git a/dd-java-agent/instrumentation/gradle/gradle-8.3/src/test/java/datadog/trace/instrumentation/gradle/AndroidGradleUtilsTest.java b/dd-java-agent/instrumentation/gradle/gradle-8.3/src/test/java/datadog/trace/instrumentation/gradle/AndroidGradleUtilsTest.java new file mode 100644 index 00000000000..b8b8f99c4eb --- /dev/null +++ b/dd-java-agent/instrumentation/gradle/gradle-8.3/src/test/java/datadog/trace/instrumentation/gradle/AndroidGradleUtilsTest.java @@ -0,0 +1,484 @@ +package datadog.trace.instrumentation.gradle; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; + +import com.android.build.api.artifact.ScopedArtifact; +import com.android.build.api.dsl.KotlinMultiplatformAndroidTarget; +import com.android.build.api.variant.ScopedArtifacts; +import datadog.trace.api.civisibility.domain.BuildModuleLayout; +import datadog.trace.api.civisibility.domain.SourceSet; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; +import kotlin.Unit; +import kotlin.jvm.functions.Function1; +import org.gradle.api.Action; +import org.gradle.api.Project; +import org.gradle.api.file.Directory; +import org.gradle.api.file.RegularFile; +import org.gradle.api.provider.ListProperty; +import org.gradle.api.provider.Provider; +import org.gradle.api.tasks.TaskProvider; +import org.gradle.api.tasks.testing.Test; +import org.gradle.testfixtures.ProjectBuilder; +import org.junit.jupiter.api.io.TempDir; + +class AndroidGradleUtilsTest { + + @TempDir Path projectDir; + + @org.junit.jupiter.api.Test + void usesSourcesAndClassesFromTestedVariant() throws IOException { + Project project = createProject(); + Test testTask = (Test) project.getTasks().getByName("test"); + + File javaSources = createDirectory("src/internalTest/java"); + File kotlinSources = createDirectory("src/internalTest/kotlin"); + File classDirectory = createDirectory("build/classes/internalTest"); + createFile(classDirectory, "datadog/smoke/Production.class"); + createFile(classDirectory, "datadog/smoke/BuildConfig.class"); + createFile(classDirectory, "config/application.properties"); + + File classJar = projectDir.resolve("build/libs/internalTest.jar").toFile(); + Map classJarEntries = new LinkedHashMap<>(); + classJarEntries.put("datadog/smoke/JarProduction.class", new byte[] {0}); + classJarEntries.put("datadog/smoke/R.class", new byte[] {0}); + classJarEntries.put("META-INF/application.properties", new byte[] {0}); + createJar(classJar, classJarEntries); + + File testFixturesJar = projectDir.resolve("build/libs/test-fixtures.jar").toFile(); + createJar( + testFixturesJar, + Collections.singletonMap("datadog/smoke/TestFixtureSupport.class", new byte[] {0})); + File copiedDependencyJar = projectDir.resolve("build/copied/dependency.jar").toFile(); + createJar( + copiedDependencyJar, Collections.singletonMap("dependency/External.class", new byte[] {0})); + testTask.setClasspath(project.files(testFixturesJar, copiedDependencyJar)); + + FakeVariant variant = + new FakeVariant( + project, + testTask, + Arrays.asList(javaSources), + Arrays.asList(kotlinSources), + Collections.singletonList(classJar), + Collections.singletonList(classDirectory)); + project + .getExtensions() + .add("androidComponents", new FakeAndroidComponents(Collections.singletonList(variant))); + + AndroidGradleUtils.configure(project); + project.getPluginManager().apply("com.android.application"); + BuildModuleLayout layout = AndroidGradleUtils.getAndroidModuleLayout(project, testTask); + + assertNotNull(layout); + SourceSet sourceSet = layout.getSourceSets().iterator().next(); + assertEquals(setOf(javaSources, kotlinSources), new HashSet<>(sourceSet.getSources())); + assertEquals( + setOf("Production.class", "JarProduction.class"), fileNames(sourceSet.getDestinations())); + } + + @org.junit.jupiter.api.Test + void doesNotInferLayoutFromTestClasspath() throws IOException { + Project project = createProject(); + Test testTask = (Test) project.getTasks().getByName("test"); + File projectLocalJar = projectDir.resolve("build/libs/production-looking.jar").toFile(); + createJar( + projectLocalJar, + Collections.singletonMap("datadog/smoke/Production.class", new byte[] {0})); + testTask.setClasspath(project.files(projectLocalJar)); + + assertNull(AndroidGradleUtils.getAndroidModuleLayout(project, testTask)); + } + + @org.junit.jupiter.api.Test + void ignoresTestComponentsWithoutTaskConfigurationApi() { + Project project = createProject(); + Test testTask = (Test) project.getTasks().getByName("test"); + project + .getExtensions() + .add( + "androidComponents", + new FakeAndroidComponents(Collections.singletonList(new FakeLegacyVariant()))); + + AndroidGradleUtils.configure(project); + project.getPluginManager().apply("com.android.application"); + + assertNull(AndroidGradleUtils.getAndroidModuleLayout(project, testTask)); + } + + @org.junit.jupiter.api.Test + void usesKmp8VariantCallbackAndProductionCompilationSources() throws IOException { + Project project = createProject(); + Test testTask = (Test) project.getTasks().getByName("test"); + File kotlinSources = createDirectory("src/androidMain/kotlin"); + File classDirectory = createDirectory("build/classes/androidMain"); + createFile(classDirectory, "datadog/smoke/Production.class"); + + FakeKmpVariant variant = + new FakeKmpVariant( + project, testTask, Collections.emptyList(), Collections.singletonList(classDirectory)); + project.getExtensions().add("androidComponents", new FakeKmpAndroidComponents(variant)); + project + .getExtensions() + .add( + "kotlin", + new FakeKotlinExtension(Collections.singletonList(new FakeKmpTarget(kotlinSources)))); + + AndroidGradleUtils.configure(project); + project.getPluginManager().apply("com.android.kotlin.multiplatform.library"); + BuildModuleLayout layout = AndroidGradleUtils.getAndroidModuleLayout(project, testTask); + + assertNotNull(layout); + SourceSet sourceSet = layout.getSourceSets().iterator().next(); + assertEquals(Collections.singleton(kotlinSources), new HashSet<>(sourceSet.getSources())); + assertEquals(Collections.singleton("Production.class"), fileNames(sourceSet.getDestinations())); + } + + private Project createProject() { + Project project = ProjectBuilder.builder().withProjectDir(projectDir.toFile()).build(); + project.getPluginManager().apply("java"); + return project; + } + + private File createDirectory(String relativePath) throws IOException { + Path directory = projectDir.resolve(relativePath); + Files.createDirectories(directory); + return directory.toFile(); + } + + private static void createFile(File root, String path) throws IOException { + Path file = root.toPath().resolve(path); + Files.createDirectories(file.getParent()); + Files.write(file, new byte[] {0}); + } + + private static void createJar(File jar, Map entries) throws IOException { + Files.createDirectories(jar.toPath().getParent()); + try (JarOutputStream output = new JarOutputStream(new FileOutputStream(jar))) { + for (Map.Entry entry : entries.entrySet()) { + output.putNextEntry(new JarEntry(entry.getKey())); + output.write(entry.getValue()); + output.closeEntry(); + } + } + } + + private static Set fileNames(Collection files) { + Set names = new HashSet<>(); + for (File file : files) { + names.add(file.getName()); + } + return names; + } + + @SafeVarargs + private static Set setOf(T... values) { + return new HashSet<>(Arrays.asList(values)); + } + + private static final class FakeAndroidComponents { + private final Collection variants; + + private FakeAndroidComponents(Collection variants) { + this.variants = variants; + } + + public FakeSelector selector() { + return new FakeSelector(); + } + + public void onVariants(Object selector, Action action) { + for (Object variant : variants) { + action.execute(variant); + } + } + } + + private static final class FakeKmpAndroidComponents { + private final Object variant; + + private FakeKmpAndroidComponents(Object variant) { + this.variant = variant; + } + + public void onVariant(Action action) { + action.execute(variant); + } + } + + private static final class FakeSelector { + public Object all() { + return this; + } + } + + private static final class FakeLegacyVariant { + + public Object getUnitTest() { + return new Object(); + } + + public Map getHostTests() { + return Collections.emptyMap(); + } + } + + private static final class FakeVariant { + private final FakeSources sources; + private final FakeTestComponent unitTest; + private final Map hostTests; + private final FakeArtifacts artifacts; + + private FakeVariant( + Project project, + Test testTask, + Collection javaSources, + Collection kotlinSources, + Collection classJars, + Collection classDirectories) { + sources = new FakeSources(project, javaSources, kotlinSources); + unitTest = new FakeTestComponent(testTask); + hostTests = Collections.emptyMap(); + artifacts = new FakeArtifacts(project, testTask, classJars, classDirectories); + } + + public FakeSources getSources() { + return sources; + } + + public FakeTestComponent getUnitTest() { + return unitTest; + } + + public Map getHostTests() { + return hostTests; + } + + public FakeArtifacts getArtifacts() { + return artifacts; + } + } + + private static final class FakeKmpVariant { + private final Map hostTests; + private final FakeArtifacts artifacts; + + private FakeKmpVariant( + Project project, + Test testTask, + Collection classJars, + Collection classDirectories) { + hostTests = Collections.singletonMap("testOnJvm", new FakeTestComponent(testTask)); + artifacts = new FakeArtifacts(project, testTask, classJars, classDirectories); + } + + public String getName() { + return "androidMain"; + } + + public Object getUnitTest() { + return null; + } + + public Map getHostTests() { + return hostTests; + } + + public FakeArtifacts getArtifacts() { + return artifacts; + } + } + + private static final class FakeKotlinExtension { + private final Collection targets; + + private FakeKotlinExtension(Collection targets) { + this.targets = targets; + } + + public Collection getTargets() { + return targets; + } + } + + private static final class FakeKmpTarget implements KotlinMultiplatformAndroidTarget { + private final FakeKmpCompilations compilations; + + private FakeKmpTarget(File sourceDirectory) { + compilations = new FakeKmpCompilations(sourceDirectory); + } + + public FakeKmpCompilations getCompilations() { + return compilations; + } + } + + private static final class FakeKmpCompilations { + private final FakeKmpCompilation main; + + private FakeKmpCompilations(File sourceDirectory) { + main = new FakeKmpCompilation(sourceDirectory); + } + + public FakeKmpCompilation findByName(String name) { + return "main".equals(name) ? main : null; + } + } + + private static final class FakeKmpCompilation { + private final FakeKmpSourceSet defaultSourceSet; + private final Collection allKotlinSourceSets; + + private FakeKmpCompilation(File sourceDirectory) { + defaultSourceSet = new FakeKmpSourceSet("androidMain", sourceDirectory); + allKotlinSourceSets = Collections.singleton(defaultSourceSet); + } + + public FakeKmpSourceSet getDefaultSourceSet() { + return defaultSourceSet; + } + + public Collection getAllKotlinSourceSets() { + return allKotlinSourceSets; + } + } + + private static final class FakeKmpSourceSet { + private final String name; + private final FakeKmpSourceDirectories kotlin; + + private FakeKmpSourceSet(String name, File sourceDirectory) { + this.name = name; + kotlin = new FakeKmpSourceDirectories(sourceDirectory); + } + + public String getName() { + return name; + } + + public FakeKmpSourceDirectories getKotlin() { + return kotlin; + } + } + + private static final class FakeKmpSourceDirectories { + private final Set srcDirs; + + private FakeKmpSourceDirectories(File sourceDirectory) { + srcDirs = Collections.singleton(sourceDirectory); + } + + public Set getSrcDirs() { + return srcDirs; + } + } + + private static final class FakeSources { + private final FakeSourceDirectories java; + private final FakeSourceDirectories kotlin; + + private FakeSources( + Project project, Collection javaSources, Collection kotlinSources) { + java = new FakeSourceDirectories(project, javaSources); + kotlin = new FakeSourceDirectories(project, kotlinSources); + } + + public FakeSourceDirectories getJava() { + return java; + } + + public FakeSourceDirectories getKotlin() { + return kotlin; + } + } + + private static final class FakeSourceDirectories { + private final Provider> all; + + private FakeSourceDirectories(Project project, Collection sourceDirectories) { + all = + project.provider( + () -> { + Collection directories = new HashSet<>(); + for (File sourceDirectory : sourceDirectories) { + directories.add( + project.getLayout().dir(project.provider(() -> sourceDirectory)).get()); + } + return directories; + }); + } + + public Provider> getAll() { + return all; + } + } + + private static final class FakeTestComponent { + private final Test testTask; + + private FakeTestComponent(Test testTask) { + this.testTask = testTask; + } + + public void configureTestTask(Function1 action) { + action.invoke(testTask); + } + } + + private static final class FakeArtifacts { + private final Project project; + private final Test testTask; + private final Collection classJars; + private final Collection classDirectories; + + private FakeArtifacts( + Project project, + Test testTask, + Collection classJars, + Collection classDirectories) { + this.project = project; + this.testTask = testTask; + this.classJars = classJars; + this.classDirectories = classDirectories; + } + + public FakeArtifacts forScope(Object scope) { + assertSame(ScopedArtifacts.Scope.PROJECT, scope); + return this; + } + + public FakeArtifacts use(TaskProvider taskProvider) { + return this; + } + + public void toGet( + Object artifact, + Function1> jars, + Function1> directories) { + assertSame(ScopedArtifact.CLASSES.INSTANCE, artifact); + for (File classJar : classJars) { + jars.invoke(testTask).add(project.getLayout().file(project.provider(() -> classJar)).get()); + } + for (File classDirectory : classDirectories) { + directories + .invoke(testTask) + .add(project.getLayout().dir(project.provider(() -> classDirectory)).get()); + } + } + } +} diff --git a/dd-java-agent/instrumentation/gradle/gradle-8.3/src/test/java/datadog/trace/instrumentation/gradle/FakeAndroidApplicationPlugin.java b/dd-java-agent/instrumentation/gradle/gradle-8.3/src/test/java/datadog/trace/instrumentation/gradle/FakeAndroidApplicationPlugin.java new file mode 100644 index 00000000000..239aac45127 --- /dev/null +++ b/dd-java-agent/instrumentation/gradle/gradle-8.3/src/test/java/datadog/trace/instrumentation/gradle/FakeAndroidApplicationPlugin.java @@ -0,0 +1,10 @@ +package datadog.trace.instrumentation.gradle; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; + +public class FakeAndroidApplicationPlugin implements Plugin { + + @Override + public void apply(Project project) {} +} diff --git a/dd-java-agent/instrumentation/gradle/gradle-8.3/src/test/resources/META-INF/gradle-plugins/com.android.application.properties b/dd-java-agent/instrumentation/gradle/gradle-8.3/src/test/resources/META-INF/gradle-plugins/com.android.application.properties new file mode 100644 index 00000000000..6e80bdd1e18 --- /dev/null +++ b/dd-java-agent/instrumentation/gradle/gradle-8.3/src/test/resources/META-INF/gradle-plugins/com.android.application.properties @@ -0,0 +1 @@ +implementation-class=datadog.trace.instrumentation.gradle.FakeAndroidApplicationPlugin diff --git a/dd-java-agent/instrumentation/gradle/gradle-8.3/src/test/resources/META-INF/gradle-plugins/com.android.kotlin.multiplatform.library.properties b/dd-java-agent/instrumentation/gradle/gradle-8.3/src/test/resources/META-INF/gradle-plugins/com.android.kotlin.multiplatform.library.properties new file mode 100644 index 00000000000..6e80bdd1e18 --- /dev/null +++ b/dd-java-agent/instrumentation/gradle/gradle-8.3/src/test/resources/META-INF/gradle-plugins/com.android.kotlin.multiplatform.library.properties @@ -0,0 +1 @@ +implementation-class=datadog.trace.instrumentation.gradle.FakeAndroidApplicationPlugin 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 3aaf72fac1a..1c8bba860ab 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 @@ -100,6 +100,7 @@ void testLegacy( "succeed-new-8.3 | 8.3 | test-succeed-new-instrumentation | {false, true} | true | false | 5 | 1 ", "succeed-new-8.9 | 8.9 | test-succeed-new-instrumentation | {false, true} | true | false | 5 | 1 ", "succeed-new-latest | latest | test-succeed-new-instrumentation | {false, true} | true | false | 5 | 1 ", + "android-variant-api-latest | latest | test-android-variant-api | false | true | false | 22 | 1 ", "succeed-multi-module-new | latest | test-succeed-multi-module-new-instrumentation | false | true | false | 7 | 2 ", "succeed-multi-forks-new | latest | test-succeed-multi-forks-new-instrumentation | false | true | false | 6 | 2 ", "skip-new | latest | test-skip-new-instrumentation | false | true | false | 2 | 0 ", diff --git a/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/build.gradleTest b/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/build.gradleTest new file mode 100644 index 00000000000..e11330ee2d6 --- /dev/null +++ b/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/build.gradleTest @@ -0,0 +1,29 @@ +apply plugin: 'java' +apply plugin: 'java-test-fixtures' +apply plugin: 'com.android.application' + +repositories { + mavenLocal() + + def proxyUrl = System.getenv("MAVEN_REPOSITORY_PROXY") + if (proxyUrl) { + println "Using proxy repository: $proxyUrl" + maven { + url = proxyUrl + allowInsecureProtocol = true + } + } + + mavenCentral() +} + +dependencies { + testImplementation 'junit:junit:4.10' +} + +def productionClassesJar = tasks.named('jar') +def testFixturesJar = tasks.named('testFixturesJar') +tasks.named('test') { + dependsOn productionClassesJar, testFixturesJar + classpath = classpath - sourceSets.main.output - sourceSets.testFixtures.output + files(productionClassesJar, testFixturesJar) +} diff --git a/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/buildSrc/build.gradleTest b/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/buildSrc/build.gradleTest new file mode 100644 index 00000000000..c6eb3cffa39 --- /dev/null +++ b/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/buildSrc/build.gradleTest @@ -0,0 +1,19 @@ +apply plugin: 'java' + +repositories { + mavenLocal() + + def proxyUrl = System.getenv("MAVEN_REPOSITORY_PROXY") + if (proxyUrl) { + maven { + url = proxyUrl + allowInsecureProtocol = true + } + } + + mavenCentral() +} + +dependencies { + implementation gradleApi() +} diff --git a/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/buildSrc/src/main/java/com/android/build/api/artifact/ScopedArtifact.java b/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/buildSrc/src/main/java/com/android/build/api/artifact/ScopedArtifact.java new file mode 100644 index 00000000000..c83d1d563e5 --- /dev/null +++ b/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/buildSrc/src/main/java/com/android/build/api/artifact/ScopedArtifact.java @@ -0,0 +1,11 @@ +package com.android.build.api.artifact; + +/** Test stand-in for the AGP classes artifact that AndroidGradleUtils loads reflectively. */ +public abstract class ScopedArtifact { + + public static final class CLASSES { + public static final CLASSES INSTANCE = new CLASSES(); + + private CLASSES() {} + } +} diff --git a/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/buildSrc/src/main/java/com/android/build/api/variant/ScopedArtifacts.java b/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/buildSrc/src/main/java/com/android/build/api/variant/ScopedArtifacts.java new file mode 100644 index 00000000000..29a57e35aae --- /dev/null +++ b/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/buildSrc/src/main/java/com/android/build/api/variant/ScopedArtifacts.java @@ -0,0 +1,10 @@ +package com.android.build.api.variant; + +/** Test stand-in for the AGP scope used to select only the current project's classes. */ +public interface ScopedArtifacts { + + enum Scope { + PROJECT, + ALL + } +} diff --git a/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/buildSrc/src/main/java/datadog/smoke/gradle/AndroidApplicationStandInPlugin.java b/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/buildSrc/src/main/java/datadog/smoke/gradle/AndroidApplicationStandInPlugin.java new file mode 100644 index 00000000000..0ed7f5b160a --- /dev/null +++ b/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/buildSrc/src/main/java/datadog/smoke/gradle/AndroidApplicationStandInPlugin.java @@ -0,0 +1,215 @@ +package datadog.smoke.gradle; + +import com.android.build.api.artifact.ScopedArtifact; +import com.android.build.api.variant.ScopedArtifacts; +import java.io.File; +import java.util.Collection; +import java.util.Collections; +import kotlin.Unit; +import kotlin.jvm.functions.Function1; +import org.gradle.api.Action; +import org.gradle.api.GradleException; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.file.Directory; +import org.gradle.api.file.RegularFile; +import org.gradle.api.provider.ListProperty; +import org.gradle.api.provider.Provider; +import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.SourceSetContainer; +import org.gradle.api.tasks.TaskProvider; +import org.gradle.api.tasks.bundling.Jar; +import org.gradle.api.tasks.testing.Test; + +public class AndroidApplicationStandInPlugin implements Plugin { + + @Override + public void apply(Project project) { + project.getPluginManager().apply("com.android.base"); + AndroidComponentsStandIn androidComponents = new AndroidComponentsStandIn(project); + project.getExtensions().add("androidComponents", androidComponents); + project.afterEvaluate( + ignored -> { + if (!androidComponents.isConfigured()) { + throw new GradleException( + "Android Variant API callback was not configured: onVariants=" + + androidComponents.isVariantActionRegistered() + + ", configureTestTask=" + + androidComponents.isTestTaskConfigured()); + } + }); + } + + public static class AndroidComponentsStandIn { + private final VariantStandIn variant; + private boolean variantActionRegistered; + + public AndroidComponentsStandIn(Project project) { + variant = new VariantStandIn(project); + } + + public SelectorStandIn selector() { + return new SelectorStandIn(); + } + + public void onVariants(Object selector, Action action) { + variantActionRegistered = true; + action.execute(variant); + } + + public boolean isConfigured() { + return isVariantActionRegistered() && isTestTaskConfigured(); + } + + public boolean isVariantActionRegistered() { + return variantActionRegistered; + } + + public boolean isTestTaskConfigured() { + return variant.getUnitTest().isConfigured(); + } + } + + public static class SelectorStandIn { + public Object all() { + return this; + } + } + + public static class VariantStandIn { + private final SourcesStandIn sources; + private final UnitTestStandIn unitTest; + private final ArtifactsStandIn artifacts; + + public VariantStandIn(Project project) { + SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class); + TaskProvider testTask = project.getTasks().named("test", Test.class); + artifacts = new ArtifactsStandIn(project); + sources = new SourcesStandIn(project, sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME)); + unitTest = new UnitTestStandIn(testTask, artifacts); + } + + public SourcesStandIn getSources() { + return sources; + } + + public UnitTestStandIn getUnitTest() { + return unitTest; + } + + public ArtifactsStandIn getArtifacts() { + return artifacts; + } + } + + public static class SourcesStandIn { + private final SourceDirectoriesStandIn java; + + public SourcesStandIn(Project project, SourceSet sourceSet) { + java = new SourceDirectoriesStandIn(project, sourceSet.getJava().getSrcDirs()); + } + + public SourceDirectoriesStandIn getJava() { + return java; + } + + public Object getKotlin() { + return null; + } + } + + public static class SourceDirectoriesStandIn { + private final Provider> all; + + public SourceDirectoriesStandIn(Project project, Collection sourceDirectories) { + all = + project.provider( + () -> { + Collection directories = new java.util.ArrayList<>(); + for (File sourceDirectory : sourceDirectories) { + directories.add( + project.getLayout().dir(project.provider(() -> sourceDirectory)).get()); + } + return directories; + }); + } + + public Provider> getAll() { + return all; + } + } + + public static class UnitTestStandIn { + private final TaskProvider testTask; + private final ArtifactsStandIn artifacts; + private boolean configured; + + public UnitTestStandIn(TaskProvider testTask, ArtifactsStandIn artifacts) { + this.testTask = testTask; + this.artifacts = artifacts; + testTask.configure( + task -> + task.doFirst( + ignored -> { + if (!artifacts.isConfigured()) { + throw new GradleException("Project-scoped classes were not configured"); + } + })); + } + + public void configureTestTask(Function1 action) { + configured = true; + testTask.configure( + task -> { + artifacts.setTestTask(task); + action.invoke(task); + }); + } + + public boolean isConfigured() { + return configured; + } + } + + public static class ArtifactsStandIn { + private final Project project; + private Test testTask; + private boolean configured; + + public ArtifactsStandIn(Project project) { + this.project = project; + } + + public void setTestTask(Test testTask) { + this.testTask = testTask; + } + + public ArtifactsStandIn forScope(Object scope) { + if (scope != ScopedArtifacts.Scope.PROJECT) { + throw new GradleException("Expected project-scoped Android classes"); + } + return this; + } + + public ArtifactsStandIn use(TaskProvider taskProvider) { + return this; + } + + public void toGet( + Object artifact, + Function1> jars, + Function1> directories) { + if (artifact != ScopedArtifact.CLASSES.INSTANCE) { + throw new GradleException("Expected the Android classes artifact"); + } + configured = true; + TaskProvider jarTask = project.getTasks().named("jar", Jar.class); + jars.invoke(testTask).add(jarTask.flatMap(Jar::getArchiveFile)); + directories.invoke(testTask).set(Collections.emptyList()); + } + + public boolean isConfigured() { + return configured; + } + } +} diff --git a/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/buildSrc/src/main/java/datadog/smoke/gradle/AndroidBaseStandInPlugin.java b/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/buildSrc/src/main/java/datadog/smoke/gradle/AndroidBaseStandInPlugin.java new file mode 100644 index 00000000000..81d9a864205 --- /dev/null +++ b/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/buildSrc/src/main/java/datadog/smoke/gradle/AndroidBaseStandInPlugin.java @@ -0,0 +1,10 @@ +package datadog.smoke.gradle; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; + +public class AndroidBaseStandInPlugin implements Plugin { + + @Override + public void apply(Project project) {} +} diff --git a/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/buildSrc/src/main/resources/META-INF/gradle-plugins/com.android.application.properties b/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/buildSrc/src/main/resources/META-INF/gradle-plugins/com.android.application.properties new file mode 100644 index 00000000000..4277df6aefc --- /dev/null +++ b/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/buildSrc/src/main/resources/META-INF/gradle-plugins/com.android.application.properties @@ -0,0 +1 @@ +implementation-class=datadog.smoke.gradle.AndroidApplicationStandInPlugin diff --git a/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/buildSrc/src/main/resources/META-INF/gradle-plugins/com.android.base.properties b/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/buildSrc/src/main/resources/META-INF/gradle-plugins/com.android.base.properties new file mode 100644 index 00000000000..8f21ff1cce0 --- /dev/null +++ b/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/buildSrc/src/main/resources/META-INF/gradle-plugins/com.android.base.properties @@ -0,0 +1 @@ +implementation-class=datadog.smoke.gradle.AndroidBaseStandInPlugin diff --git a/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/coverages.ftl b/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/coverages.ftl new file mode 100644 index 00000000000..25169dfc58f --- /dev/null +++ b/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/coverages.ftl @@ -0,0 +1,12 @@ +[ { + "files" : [ { + "bitmap" : "ABg=", + "filename" : "src/test/java/datadog/smoke/TestSucceed.java" + }, { + "bitmap" : "IA==", + "filename" : "src/main/java/datadog/smoke/Calculator.java" + } ], + "span_id" : ${content_span_id_8}, + "test_session_id" : ${content_test_session_id}, + "test_suite_id" : ${content_test_suite_id} +} ] \ No newline at end of file diff --git a/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/events.ftl b/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/events.ftl new file mode 100644 index 00000000000..9a91ed79207 --- /dev/null +++ b/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/events.ftl @@ -0,0 +1,785 @@ +[ { + "content" : { + "duration" : ${content_duration}, + "error" : 0, + "meta" : { + "_dd.ci.itr.tests_skipped" : "true", + "_dd.p.tid" : ${content_meta__dd_p_tid}, + "_dd.test.is_user_provided_service" : "true", + "_dd.tracer_host" : ${content_meta__dd_tracer_host}, + "ci.workspace_path" : ${content_meta_ci_workspace_path}, + "component" : "gradle", + "env" : "integration-test", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "runtime-id" : ${content_meta_runtime_id}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version}, + "span.kind" : "test_session_end", + "test.code_coverage.backfilled" : "true", + "test.code_coverage.enabled" : "true", + "test.command" : "gradle test", + "test.framework" : "junit4", + "test.framework_version" : "4.10", + "test.is_android" : "true", + "test.itr.tests_skipping.enabled" : "true", + "test.itr.tests_skipping.type" : "test", + "test.status" : "pass", + "test.toolchain" : ${content_meta_test_toolchain}, + "test.type" : "test", + "test_session.name" : "gradle test" + }, + "metrics" : { + "_dd.host.vcpu_count" : ${content_metrics__dd_host_vcpu_count}, + "_dd.profiling.enabled" : 0, + "_dd.trace_span_attribute_schema" : 0, + "process_id" : ${content_metrics_process_id}, + "test.code_coverage.lines_pct" : 50, + "test.itr.tests_skipping.count" : 1 + }, + "name" : "gradle.test_session", + "resource" : ":", + "service" : "test-gradle-service", + "start" : ${content_start}, + "test_session_id" : ${content_test_session_id} + }, + "type" : "test_session_end", + "version" : 1 +}, { + "content" : { + "duration" : ${content_duration_2}, + "error" : 0, + "meta" : { + "_dd.p.tid" : ${content_meta__dd_p_tid_2}, + "_dd.test.is_user_provided_service" : "true", + "_dd.tracer_host" : ${content_meta__dd_tracer_host}, + "ci.workspace_path" : ${content_meta_ci_workspace_path}, + "component" : "gradle", + "env" : "integration-test", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "runtime-id" : ${content_meta_runtime_id}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version}, + "span.kind" : "test_session_end", + "test.command" : "gradle :buildSrc", + "test.gradle.nested_build" : "true", + "test.status" : "skip", + "test.toolchain" : ${content_meta_test_toolchain}, + "test.type" : "test", + "test_session.name" : "gradle :buildSrc" + }, + "metrics" : { + "_dd.host.vcpu_count" : ${content_metrics__dd_host_vcpu_count_2}, + "_dd.profiling.enabled" : 0, + "_dd.trace_span_attribute_schema" : 0, + "process_id" : ${content_metrics_process_id} + }, + "name" : "gradle.test_session", + "resource" : ":buildSrc", + "service" : "test-gradle-service", + "start" : ${content_start_2}, + "test_session_id" : ${content_test_session_id_2} + }, + "type" : "test_session_end", + "version" : 1 +}, { + "content" : { + "duration" : ${content_duration_3}, + "error" : 0, + "meta" : { + "_dd.ci.itr.tests_skipped" : "true", + "_dd.p.tid" : ${content_meta__dd_p_tid_3}, + "_dd.test.is_user_provided_service" : "true", + "ci.workspace_path" : ${content_meta_ci_workspace_path}, + "component" : "gradle", + "env" : "integration-test", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "runtime-id" : ${content_meta_runtime_id}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version}, + "span.kind" : "test_module_end", + "test.code_coverage.backfilled" : "true", + "test.code_coverage.enabled" : "true", + "test.command" : "gradle test", + "test.framework" : "junit4", + "test.framework_version" : "4.10", + "test.is_android" : "true", + "test.itr.tests_skipping.enabled" : "true", + "test.itr.tests_skipping.type" : "test", + "test.module" : ":test", + "test.status" : "pass", + "test.type" : "test", + "test_session.name" : "gradle test" + }, + "metrics" : { + "_dd.host.vcpu_count" : ${content_metrics__dd_host_vcpu_count_3}, + "test.code_coverage.lines_pct" : 50, + "test.itr.tests_skipping.count" : 1 + }, + "name" : "gradle.test_module", + "resource" : ":test", + "service" : "test-gradle-service", + "start" : ${content_start_3}, + "test_module_id" : ${content_test_module_id}, + "test_session_id" : ${content_test_session_id} + }, + "type" : "test_module_end", + "version" : 1 +}, { + "content" : { + "duration" : ${content_duration_4}, + "error" : 0, + "meta" : { + "_dd.p.tid" : ${content_meta__dd_p_tid_4}, + "_dd.test.is_user_provided_service" : "true", + "env" : "integration-test", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "runtime-id" : ${content_meta_runtime_id}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version} + }, + "metrics" : { }, + "name" : "classes", + "parent_id" : ${content_test_session_id_2}, + "resource" : "classes", + "service" : "test-gradle-service", + "span_id" : ${content_span_id}, + "start" : ${content_start_4}, + "trace_id" : ${content_test_session_id_2} + }, + "type" : "span", + "version" : 1 +}, { + "content" : { + "duration" : ${content_duration_5}, + "error" : 0, + "meta" : { + "_dd.p.tid" : ${content_meta__dd_p_tid_5}, + "_dd.test.is_user_provided_service" : "true", + "env" : "integration-test", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "runtime-id" : ${content_meta_runtime_id}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version} + }, + "metrics" : { }, + "name" : "classes", + "parent_id" : ${content_test_session_id}, + "resource" : "classes", + "service" : "test-gradle-service", + "span_id" : ${content_span_id_2}, + "start" : ${content_start_5}, + "trace_id" : ${content_test_session_id} + }, + "type" : "span", + "version" : 1 +}, { + "content" : { + "duration" : ${content_duration_6}, + "error" : 0, + "meta" : { + "_dd.p.tid" : ${content_meta__dd_p_tid_6}, + "_dd.test.is_user_provided_service" : "true", + "env" : "integration-test", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "runtime-id" : ${content_meta_runtime_id}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version} + }, + "metrics" : { }, + "name" : "compileGroovy", + "parent_id" : ${content_test_session_id_2}, + "resource" : "compileGroovy", + "service" : "test-gradle-service", + "span_id" : ${content_span_id_3}, + "start" : ${content_start_6}, + "trace_id" : ${content_test_session_id_2} + }, + "type" : "span", + "version" : 1 +}, { + "content" : { + "duration" : ${content_duration_7}, + "error" : 0, + "meta" : { + "_dd.p.tid" : ${content_meta__dd_p_tid_7}, + "_dd.test.is_user_provided_service" : "true", + "env" : "integration-test", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "runtime-id" : ${content_meta_runtime_id}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version} + }, + "metrics" : { }, + "name" : "compileJava", + "parent_id" : ${content_test_session_id_2}, + "resource" : "compileJava", + "service" : "test-gradle-service", + "span_id" : ${content_span_id_4}, + "start" : ${content_start_7}, + "trace_id" : ${content_test_session_id_2} + }, + "type" : "span", + "version" : 1 +}, { + "content" : { + "duration" : ${content_duration_8}, + "error" : 0, + "meta" : { + "_dd.p.tid" : ${content_meta__dd_p_tid_8}, + "_dd.test.is_user_provided_service" : "true", + "env" : "integration-test", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "runtime-id" : ${content_meta_runtime_id}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version} + }, + "metrics" : { }, + "name" : "compileJava", + "parent_id" : ${content_test_session_id}, + "resource" : "compileJava", + "service" : "test-gradle-service", + "span_id" : ${content_span_id_5}, + "start" : ${content_start_8}, + "trace_id" : ${content_test_session_id} + }, + "type" : "span", + "version" : 1 +}, { + "content" : { + "duration" : ${content_duration_9}, + "error" : 0, + "meta" : { + "_dd.p.tid" : ${content_meta__dd_p_tid_9}, + "_dd.test.is_user_provided_service" : "true", + "env" : "integration-test", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "runtime-id" : ${content_meta_runtime_id}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version} + }, + "metrics" : { }, + "name" : "compileTestFixturesJava", + "parent_id" : ${content_test_session_id}, + "resource" : "compileTestFixturesJava", + "service" : "test-gradle-service", + "span_id" : ${content_span_id_6}, + "start" : ${content_start_9}, + "trace_id" : ${content_test_session_id} + }, + "type" : "span", + "version" : 1 +}, { + "content" : { + "duration" : ${content_duration_10}, + "error" : 0, + "meta" : { + "_dd.p.tid" : ${content_meta__dd_p_tid_10}, + "_dd.test.is_user_provided_service" : "true", + "env" : "integration-test", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "runtime-id" : ${content_meta_runtime_id}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version} + }, + "metrics" : { }, + "name" : "compileTestJava", + "parent_id" : ${content_test_session_id}, + "resource" : "compileTestJava", + "service" : "test-gradle-service", + "span_id" : ${content_span_id_7}, + "start" : ${content_start_10}, + "trace_id" : ${content_test_session_id} + }, + "type" : "span", + "version" : 1 +}, { + "content" : { + "duration" : ${content_duration_11}, + "error" : 0, + "meta" : { + "_dd.p.tid" : ${content_meta__dd_p_tid_11}, + "_dd.test.is_user_provided_service" : "true", + "_dd.tracer_host" : ${content_meta__dd_tracer_host}, + "ci.workspace_path" : ${content_meta_ci_workspace_path}, + "component" : "junit4", + "env" : "integration-test", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "runtime-id" : ${content_meta_runtime_id_2}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version}, + "span.kind" : "test_suite_end", + "test.framework" : "junit4", + "test.framework_version" : "4.10", + "test.itr.tests_skipping.enabled" : "true", + "test.module" : ":test", + "test.source.file" : "src/test/java/datadog/smoke/TestSucceed.java", + "test.status" : "pass", + "test.suite" : "datadog.smoke.TestSucceed", + "test.type" : "test", + "test_session.name" : "gradle test" + }, + "metrics" : { + "_dd.host.vcpu_count" : ${content_metrics__dd_host_vcpu_count_4}, + "_dd.profiling.enabled" : 0, + "_dd.trace_span_attribute_schema" : 0, + "process_id" : ${content_metrics_process_id_2}, + "test.source.end" : 18, + "test.source.start" : 7 + }, + "name" : "junit4.test_suite", + "resource" : "datadog.smoke.TestSucceed", + "service" : "test-gradle-service", + "start" : ${content_start_11}, + "test_module_id" : ${content_test_module_id}, + "test_session_id" : ${content_test_session_id}, + "test_suite_id" : ${content_test_suite_id} + }, + "type" : "test_suite_end", + "version" : 1 +}, { + "content" : { + "duration" : ${content_duration_12}, + "error" : 0, + "meta" : { + "_dd.library_capabilities.auto_test_retries" : "1", + "_dd.library_capabilities.coverage_report_upload" : "1", + "_dd.library_capabilities.early_flake_detection" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", + "_dd.library_capabilities.impacted_tests" : "1", + "_dd.library_capabilities.test_impact_analysis" : "1", + "_dd.library_capabilities.test_management.attempt_to_fix" : "5", + "_dd.library_capabilities.test_management.disable" : "1", + "_dd.library_capabilities.test_management.quarantine" : "1", + "_dd.p.tid" : ${content_meta__dd_p_tid_12}, + "_dd.test.is_user_provided_service" : "true", + "_dd.tracer_host" : ${content_meta__dd_tracer_host}, + "ci.workspace_path" : ${content_meta_ci_workspace_path}, + "component" : "junit4", + "env" : "integration-test", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "runtime-id" : ${content_meta_runtime_id_2}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version}, + "span.kind" : "test", + "test.final_status" : "pass", + "test.framework" : "junit4", + "test.framework_version" : "4.10", + "test.itr.tests_skipping.enabled" : "true", + "test.module" : ":test", + "test.name" : "test_succeed", + "test.source.file" : "src/test/java/datadog/smoke/TestSucceed.java", + "test.source.method" : "test_succeed()V", + "test.status" : "pass", + "test.suite" : "datadog.smoke.TestSucceed", + "test.type" : "test", + "test_session.name" : "gradle test" + }, + "metrics" : { + "_dd.host.vcpu_count" : ${content_metrics__dd_host_vcpu_count_5}, + "_dd.profiling.enabled" : 0, + "_dd.trace_span_attribute_schema" : 0, + "process_id" : ${content_metrics_process_id_2}, + "test.source.end" : 12, + "test.source.start" : 9 + }, + "name" : "junit4.test", + "parent_id" : ${content_parent_id}, + "resource" : "datadog.smoke.TestSucceed.test_succeed", + "service" : "test-gradle-service", + "span_id" : ${content_span_id_8}, + "start" : ${content_start_12}, + "test_module_id" : ${content_test_module_id}, + "test_session_id" : ${content_test_session_id}, + "test_suite_id" : ${content_test_suite_id}, + "trace_id" : ${content_trace_id} + }, + "type" : "test", + "version" : 2 +}, { + "content" : { + "duration" : ${content_duration_13}, + "error" : 0, + "meta" : { + "_dd.library_capabilities.auto_test_retries" : "1", + "_dd.library_capabilities.coverage_report_upload" : "1", + "_dd.library_capabilities.early_flake_detection" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", + "_dd.library_capabilities.impacted_tests" : "1", + "_dd.library_capabilities.test_impact_analysis" : "1", + "_dd.library_capabilities.test_management.attempt_to_fix" : "5", + "_dd.library_capabilities.test_management.disable" : "1", + "_dd.library_capabilities.test_management.quarantine" : "1", + "_dd.p.tid" : ${content_meta__dd_p_tid_13}, + "_dd.test.is_user_provided_service" : "true", + "_dd.tracer_host" : ${content_meta__dd_tracer_host}, + "ci.workspace_path" : ${content_meta_ci_workspace_path}, + "component" : "junit4", + "env" : "integration-test", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "runtime-id" : ${content_meta_runtime_id_2}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version}, + "span.kind" : "test", + "test.final_status" : "skip", + "test.framework" : "junit4", + "test.framework_version" : "4.10", + "test.itr.tests_skipping.enabled" : "true", + "test.module" : ":test", + "test.name" : "test_to_skip_with_itr", + "test.skip_reason" : "Skipped by Datadog Test Impact Analysis", + "test.skipped_by_itr" : "true", + "test.source.file" : "src/test/java/datadog/smoke/TestSucceed.java", + "test.source.method" : "test_to_skip_with_itr()V", + "test.status" : "skip", + "test.suite" : "datadog.smoke.TestSucceed", + "test.type" : "test", + "test_session.name" : "gradle test" + }, + "metrics" : { + "_dd.host.vcpu_count" : ${content_metrics__dd_host_vcpu_count_6}, + "_dd.profiling.enabled" : 0, + "_dd.trace_span_attribute_schema" : 0, + "process_id" : ${content_metrics_process_id_2}, + "test.source.end" : 17, + "test.source.start" : 14 + }, + "name" : "junit4.test", + "parent_id" : ${content_parent_id}, + "resource" : "datadog.smoke.TestSucceed.test_to_skip_with_itr", + "service" : "test-gradle-service", + "span_id" : ${content_span_id_9}, + "start" : ${content_start_13}, + "test_module_id" : ${content_test_module_id}, + "test_session_id" : ${content_test_session_id}, + "test_suite_id" : ${content_test_suite_id}, + "trace_id" : ${content_trace_id_2} + }, + "type" : "test", + "version" : 2 +}, { + "content" : { + "duration" : ${content_duration_14}, + "error" : 0, + "meta" : { + "_dd.p.tid" : ${content_meta__dd_p_tid_14}, + "_dd.test.is_user_provided_service" : "true", + "env" : "integration-test", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "runtime-id" : ${content_meta_runtime_id}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version} + }, + "metrics" : { }, + "name" : "jar", + "parent_id" : ${content_test_session_id_2}, + "resource" : "jar", + "service" : "test-gradle-service", + "span_id" : ${content_span_id_10}, + "start" : ${content_start_14}, + "trace_id" : ${content_test_session_id_2} + }, + "type" : "span", + "version" : 1 +}, { + "content" : { + "duration" : ${content_duration_15}, + "error" : 0, + "meta" : { + "_dd.p.tid" : ${content_meta__dd_p_tid_15}, + "_dd.test.is_user_provided_service" : "true", + "env" : "integration-test", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "runtime-id" : ${content_meta_runtime_id}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version} + }, + "metrics" : { }, + "name" : "jar", + "parent_id" : ${content_test_session_id}, + "resource" : "jar", + "service" : "test-gradle-service", + "span_id" : ${content_span_id_11}, + "start" : ${content_start_15}, + "trace_id" : ${content_test_session_id} + }, + "type" : "span", + "version" : 1 +}, { + "content" : { + "duration" : ${content_duration_16}, + "error" : 0, + "meta" : { + "_dd.p.tid" : ${content_meta__dd_p_tid_16}, + "_dd.test.is_user_provided_service" : "true", + "env" : "integration-test", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "runtime-id" : ${content_meta_runtime_id}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version} + }, + "metrics" : { }, + "name" : "processResources", + "parent_id" : ${content_test_session_id_2}, + "resource" : "processResources", + "service" : "test-gradle-service", + "span_id" : ${content_span_id_12}, + "start" : ${content_start_16}, + "trace_id" : ${content_test_session_id_2} + }, + "type" : "span", + "version" : 1 +}, { + "content" : { + "duration" : ${content_duration_17}, + "error" : 0, + "meta" : { + "_dd.p.tid" : ${content_meta__dd_p_tid_17}, + "_dd.test.is_user_provided_service" : "true", + "env" : "integration-test", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "runtime-id" : ${content_meta_runtime_id}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version} + }, + "metrics" : { }, + "name" : "processResources", + "parent_id" : ${content_test_session_id}, + "resource" : "processResources", + "service" : "test-gradle-service", + "span_id" : ${content_span_id_13}, + "start" : ${content_start_17}, + "trace_id" : ${content_test_session_id} + }, + "type" : "span", + "version" : 1 +}, { + "content" : { + "duration" : ${content_duration_18}, + "error" : 0, + "meta" : { + "_dd.p.tid" : ${content_meta__dd_p_tid_18}, + "_dd.test.is_user_provided_service" : "true", + "env" : "integration-test", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "runtime-id" : ${content_meta_runtime_id}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version} + }, + "metrics" : { }, + "name" : "processTestFixturesResources", + "parent_id" : ${content_test_session_id}, + "resource" : "processTestFixturesResources", + "service" : "test-gradle-service", + "span_id" : ${content_span_id_14}, + "start" : ${content_start_18}, + "trace_id" : ${content_test_session_id} + }, + "type" : "span", + "version" : 1 +}, { + "content" : { + "duration" : ${content_duration_19}, + "error" : 0, + "meta" : { + "_dd.p.tid" : ${content_meta__dd_p_tid_19}, + "_dd.test.is_user_provided_service" : "true", + "env" : "integration-test", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "runtime-id" : ${content_meta_runtime_id}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version} + }, + "metrics" : { }, + "name" : "processTestResources", + "parent_id" : ${content_test_session_id}, + "resource" : "processTestResources", + "service" : "test-gradle-service", + "span_id" : ${content_span_id_15}, + "start" : ${content_start_19}, + "trace_id" : ${content_test_session_id} + }, + "type" : "span", + "version" : 1 +}, { + "content" : { + "duration" : ${content_duration_20}, + "error" : 0, + "meta" : { + "_dd.p.tid" : ${content_meta__dd_p_tid_20}, + "_dd.test.is_user_provided_service" : "true", + "env" : "integration-test", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "runtime-id" : ${content_meta_runtime_id}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version} + }, + "metrics" : { }, + "name" : "testClasses", + "parent_id" : ${content_test_session_id}, + "resource" : "testClasses", + "service" : "test-gradle-service", + "span_id" : ${content_span_id_16}, + "start" : ${content_start_20}, + "trace_id" : ${content_test_session_id} + }, + "type" : "span", + "version" : 1 +}, { + "content" : { + "duration" : ${content_duration_21}, + "error" : 0, + "meta" : { + "_dd.p.tid" : ${content_meta__dd_p_tid_21}, + "_dd.test.is_user_provided_service" : "true", + "env" : "integration-test", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "runtime-id" : ${content_meta_runtime_id}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version} + }, + "metrics" : { }, + "name" : "testFixturesClasses", + "parent_id" : ${content_test_session_id}, + "resource" : "testFixturesClasses", + "service" : "test-gradle-service", + "span_id" : ${content_span_id_17}, + "start" : ${content_start_21}, + "trace_id" : ${content_test_session_id} + }, + "type" : "span", + "version" : 1 +}, { + "content" : { + "duration" : ${content_duration_22}, + "error" : 0, + "meta" : { + "_dd.p.tid" : ${content_meta__dd_p_tid_22}, + "_dd.test.is_user_provided_service" : "true", + "env" : "integration-test", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "runtime-id" : ${content_meta_runtime_id}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version} + }, + "metrics" : { }, + "name" : "testFixturesJar", + "parent_id" : ${content_test_session_id}, + "resource" : "testFixturesJar", + "service" : "test-gradle-service", + "span_id" : ${content_span_id_18}, + "start" : ${content_start_22}, + "trace_id" : ${content_test_session_id} + }, + "type" : "span", + "version" : 1 +} ] \ No newline at end of file diff --git a/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/settings.gradleTest b/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/settings.gradleTest new file mode 100644 index 00000000000..6040f1decb8 --- /dev/null +++ b/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/settings.gradleTest @@ -0,0 +1 @@ +rootProject.name = 'gradle-instrumentation-test-project' diff --git a/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/src/main/java/datadog/smoke/BuildConfig.java b/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/src/main/java/datadog/smoke/BuildConfig.java new file mode 100644 index 00000000000..b16397c0577 --- /dev/null +++ b/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/src/main/java/datadog/smoke/BuildConfig.java @@ -0,0 +1,5 @@ +package datadog.smoke; + +public final class BuildConfig { + private BuildConfig() {} +} diff --git a/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/src/main/java/datadog/smoke/Calculator.java b/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/src/main/java/datadog/smoke/Calculator.java new file mode 100644 index 00000000000..acff45019ee --- /dev/null +++ b/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/src/main/java/datadog/smoke/Calculator.java @@ -0,0 +1,7 @@ +package datadog.smoke; + +public class Calculator { + public static int add(int a, int b) { + return a + b; + } +} diff --git a/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/src/test/java/datadog/smoke/TestSucceed.java b/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/src/test/java/datadog/smoke/TestSucceed.java new file mode 100644 index 00000000000..7f3074a5523 --- /dev/null +++ b/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/src/test/java/datadog/smoke/TestSucceed.java @@ -0,0 +1,18 @@ +package datadog.smoke; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class TestSucceed { + + @Test + public void test_succeed() { + assertTrue(Calculator.add(2, 2) == 4); + } + + @Test + public void test_to_skip_with_itr() { + assertTrue(true); + } +} diff --git a/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/src/testFixtures/java/datadog/smoke/TestFixtureSupport.java b/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/src/testFixtures/java/datadog/smoke/TestFixtureSupport.java new file mode 100644 index 00000000000..9eae03faebb --- /dev/null +++ b/dd-smoke-tests/gradle/src/test/resources/test-android-variant-api/src/testFixtures/java/datadog/smoke/TestFixtureSupport.java @@ -0,0 +1,9 @@ +package datadog.smoke; + +public final class TestFixtureSupport { + public static int unused() { + return 1; + } + + private TestFixtureSupport() {} +}