diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3df1052345..28db4ae7fa 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -20,6 +20,11 @@ variables: CACHE_FALLBACK_KEY: "develop" + # Unify the Gradle user home across ALL jobs so every Gradle invocation shares one + # GitLab-cacheable location ($CI_PROJECT_DIR/cache) instead of some jobs using the default, + # non-cached /root/.gradle. Jobs that pass --gradle-user-home cache/ already match this. + GRADLE_USER_HOME: "$CI_PROJECT_DIR/cache" + # Magic Mirror Depot proxy URLs. The depot proxies both Maven Central and the Gradle # Plugin Portal for the Java ecosystem from the same endpoint. Setting these routes # dependency/plugin resolution through Datadog's internal mirror to avoid HTTP 429 diff --git a/LICENSE-3rdparty.csv b/LICENSE-3rdparty.csv index fb40ca1356..fdaf79b586 100644 --- a/LICENSE-3rdparty.csv +++ b/LICENSE-3rdparty.csv @@ -53,6 +53,8 @@ import,androidx.versionedparcelable,Apache-2.0,Copyright 2018 The Android Open S import,androidx.viewpager,Apache-2.0,Copyright 2018 The Android Open Source Project import,androidx.wear,Apache-2.0,Copyright 2018 The Android Open Source Project import,androidx.work,Apache-2.0,Copyright 2018 The Android Open Source Project +import,app.cash.sql-psi,Apache-2.0,"Copyright 2016 Square, Inc" +import,app.cash.sqldelight,Apache-2.0,"Copyright 2016 Square, Inc" import,com.android.tools,Apache-2.0,Copyright 2018 The Android Open Source Project import,com.apollographql.apollo,MIT,"Copyright (c) 2016-2024 Apollo Graph, Inc. (Formerly Meteor Development Group, Inc.)" import,com.benasher44,MIT,"Copyright (c) 2019 Ben Asher" @@ -140,6 +142,7 @@ import(test),com.jakewharton.android.repackaged,Apache-2.0,"Copyright (C) 2007 T import(test),com.parse.bolts,"BSD License","Copyright (c) Facebook, Inc. and its affiliates." import(test),com.squareup,Apache-2.0,"Copyright 2015 Square, Inc." import(test),dev.drewhamilton.poko,Apache-2.0,Copyright 2020-Present Drew Hamilton +import(test),dev.zacsweers.kctfork,MPL-2.0,Copyright (C) 2022 Zac Sweers import(test),greatest,ICT,"Copyright (c) 2011-2018 Scott Vokes " import(test),io.github.davidburstrom.contester,Apache-2.0,"Copyright 2022-2024 David Burström" import(test),io.github.detekt.sarif4k,Apache-2.0,"Copyright 2021 Artur Bosch & Contributors" @@ -184,7 +187,7 @@ build,com.fasterxml.jackson.dataformat,Apache-2.,"Copyright (c) 2007- Tatu Salor build,com.fasterxml.jackson.module,Apache-2.0,"Copyright (c) 2007- Tatu Saloranta" build,com.fasterxml.woodstox,Apache-2.0,"Copyright (c) 2007- Tatu Saloranta" build,com.github.ajalt.clikt,Apache-2.0,"Copyright clikt authors" -build,com.github.tschuchortdev,MPL-2.0,"Copyright (C) 2023 Thilo Schuchort" +build,com.github.ben-manes.caffeine,Apache-2.0,"Copyright 2014 Ben Manes" build,com.google.android,Apache-2.0,Copyright (C) 2013 The Android Open Source Project build,com.google.api.grpc,Apache-2.0,Copyright 2020 Google LLC build,com.google.auto,Apache-2.0,"Copyright 2014 Google LLC" @@ -208,10 +211,11 @@ build,commons-cli,Apache-2.0,Copyright 2002-2024 The Apache Software Foundation build,commons-codec,Apache-2.0,Copyright 2002-2024 The Apache Software Foundation build,commons-io,Apache-2.0,Copyright 2002-2024 The Apache Software Foundation build,commons-logging,Apache-2.0,Copyright 2002-2024 The Apache Software Foundation +build,dev.detekt,Apache-2.0,Copyright 2016-2019 the original author or authors build,io.github.aakira,Apache-2.0,"Copyright (C) 2019 A.Akira" build,io.gitlab.arturbosch.detekt,Apache-2.0,Copyright 2016-2019 the original author or authors build,io.github.classgraph,MIT,"Copyright (c) 2019 Luke Hutchison" -build,io.github.java-diff-utils,Apache-2.0,"Copyright 2009-2017 java-diff-utils." +build,io.github.java-diff-utils,Apache-2.0,"Copyright java-diff-utils contributors" build,io.github.microutils,Apache-2.0,Copyright (c) 2016-2018 Ohad Shai build,io.github.oshai,Apache-2.0,"Copyright oshai authors" build,io.grpc,Apache-2.0,Copyright 2014 The gRPC Authors @@ -241,6 +245,7 @@ build,org.glassfish.jaxb,"Eclipse Distribution License - v 1.0","Copyright (c) 2 build,org.jcommander,Apache-2.0,"Copyright (C) 2010 the original author or authors" build,org.jetbrains.dokka,Apache-2.0,"Copyright 2014-2019 JetBrains s.r.o. and Dokka project contributors." build,org.jetbrains.intellij.deps,LGPL-2.1-only,"Copyright (c) 2001-2002, Eric D. Friedman, Jason Baldridge, Copyright (c) 1999 CERN - European Organization for Nuclear Research" +build,org.jetbrains.intellij.deps.kotlinx,Apache-2.0,Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors build,org.jlleitschuh.gradle,MIT,"Copyright (c) 2023 Jonathan Leitschuh" build,org.jvnet.staxex,"Eclipse Distribution License - v 1.0","Copyright (c) 1997-2015 Oracle and/or its affiliates" build,org.ow2.asm,BSD-3-Clause,"Copyright (c) 2000-2011 INRIA, France Telecom" diff --git a/build-logic/src/main/kotlin/com/datadog/gradle/config/AndroidConfig.kt b/build-logic/src/main/kotlin/com/datadog/gradle/config/AndroidConfig.kt index 8d7ea4f5cd..44e644d925 100644 --- a/build-logic/src/main/kotlin/com/datadog/gradle/config/AndroidConfig.kt +++ b/build-logic/src/main/kotlin/com/datadog/gradle/config/AndroidConfig.kt @@ -8,26 +8,23 @@ package com.datadog.gradle.config import com.android.build.api.dsl.CompileOptions import com.android.build.api.dsl.LibraryExtension +import com.android.build.api.variant.HostTestBuilder +import com.android.build.api.variant.LibraryAndroidComponentsExtension +import com.android.build.api.variant.LibraryVariantBuilder import com.datadog.gradle.utils.Version import org.gradle.api.JavaVersion import org.gradle.api.Project object AndroidConfig { - const val TARGET_SDK = 36 + const val TARGET_SDK = 37 const val MIN_SDK = 23 const val MIN_SDK_FOR_AUTO = 29 - const val BUILD_TOOLS_VERSION = "36.0.0" + const val BUILD_TOOLS_VERSION = "37.0.0" val VERSION = Version(3, 14, 0, Version.Type.Snapshot) } -// TODO RUM-628 Switch to Java 17 bytecode -fun CompileOptions.java11() { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 -} - fun CompileOptions.java17() { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 @@ -51,7 +48,7 @@ internal fun Project.androidLibraryConfig() { } compileOptions { - java11() + java17() } sourceSets.all { @@ -90,4 +87,9 @@ internal fun Project.androidLibraryConfig() { } } } + extensionConfig { + beforeVariants { variant: LibraryVariantBuilder -> + variant.hostTests[HostTestBuilder.UNIT_TEST_TYPE]?.enable = true + } + } } diff --git a/build-logic/src/main/kotlin/com/datadog/gradle/config/KotlinConfig.kt b/build-logic/src/main/kotlin/com/datadog/gradle/config/KotlinConfig.kt index ae0ec65c4b..33ff38b5e8 100644 --- a/build-logic/src/main/kotlin/com/datadog/gradle/config/KotlinConfig.kt +++ b/build-logic/src/main/kotlin/com/datadog/gradle/config/KotlinConfig.kt @@ -20,8 +20,8 @@ internal fun Project.kotlinConfig( jvmTarget.set(jvmBytecodeTarget) val isCI = System.getenv("CI").toBoolean() allWarningsAsErrors.set(evaluateWarningsAsErrors && isCI) - apiVersion.set(KotlinVersion.KOTLIN_2_0) - languageVersion.set(KotlinVersion.KOTLIN_2_0) + apiVersion.set(KotlinVersion.KOTLIN_2_1) + languageVersion.set(KotlinVersion.KOTLIN_2_1) } } } diff --git a/build-logic/src/main/kotlin/com/datadog/gradle/plugin/apisurface/ApiSurfacePlugin.kt b/build-logic/src/main/kotlin/com/datadog/gradle/plugin/apisurface/ApiSurfacePlugin.kt index df9aca1eb8..9017db7ba2 100644 --- a/build-logic/src/main/kotlin/com/datadog/gradle/plugin/apisurface/ApiSurfacePlugin.kt +++ b/build-logic/src/main/kotlin/com/datadog/gradle/plugin/apisurface/ApiSurfacePlugin.kt @@ -45,7 +45,7 @@ class ApiSurfacePlugin : Plugin { this.kotlinSurfaceFile.set(generateApiSurfaceTask.flatMap { it.surfaceFile }) if (target.plugins.hasPlugin(GEN_JAVA_API_LAYOUT_PLUGIN)) { this.javaSurfaceFile.set(javaSurfaceFile) - dependsOn(TASK_GEN_JAVA_API_SURFACE) + dependsOn(target.tasks.matching { it.name == TASK_GEN_JAVA_API_SURFACE }) } else { logger.info( "No Java API layout plugin found, skipping API surface" + @@ -77,15 +77,16 @@ class ApiSurfacePlugin : Plugin { // Java API generation task does a clean-up of all files in the output // folder, so let it run first if (target.plugins.hasPlugin(GEN_JAVA_API_LAYOUT_PLUGIN)) { + val apiDumpTasks = target.tasks.matching { it.name == TASK_GEN_JAVA_API_SURFACE } val isCi = target.providers.environmentVariable("CI").isPresent if (isCi) { // Java API generation wires to the release build type, so we can afford triggering compilation // of release type locally when we run debug compilation, but we would like to avoid it on CI if (name == "compileReleaseKotlin") { - finalizedBy(TASK_GEN_JAVA_API_SURFACE) + finalizedBy(apiDumpTasks) } } else { - finalizedBy(TASK_GEN_JAVA_API_SURFACE) + finalizedBy(apiDumpTasks) } } finalizedBy(generateApiSurfaceTask) diff --git a/build-logic/src/main/kotlin/com/datadog/gradle/plugin/apisurface/KotlinFileVisitor.kt b/build-logic/src/main/kotlin/com/datadog/gradle/plugin/apisurface/KotlinFileVisitor.kt index 62d163a85a..65d09b64eb 100644 --- a/build-logic/src/main/kotlin/com/datadog/gradle/plugin/apisurface/KotlinFileVisitor.kt +++ b/build-logic/src/main/kotlin/com/datadog/gradle/plugin/apisurface/KotlinFileVisitor.kt @@ -6,6 +6,7 @@ package com.datadog.gradle.plugin.apisurface +import org.jetbrains.kotlin.K1Deprecation import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.com.intellij.openapi.util.Disposer @@ -486,6 +487,10 @@ class KotlinFileVisitor { * singleton, so it is built once and kept for the life of the JVM. It holds no * per-file state: every parsed file becomes garbage as soon as we are done with it. */ + // Kotlin 2.2+ gates the K1 compiler front-end behind an opt-in. Parsing here is purely + // syntactic (PSI only, no resolution), so the K1 environment stays the right tool until + // the Analysis API offers a standalone parser. + @OptIn(K1Deprecation::class) private val PSI_FACTORY: KtPsiFactory by lazy { val environment = KotlinCoreEnvironment.createForProduction( Disposer.newDisposable("KotlinFileVisitor"), diff --git a/build-logic/src/main/kotlin/com/datadog/gradle/utils/JsonSchemaGenerationTasks.kt b/build-logic/src/main/kotlin/com/datadog/gradle/utils/JsonSchemaGenerationTasks.kt index 1ed7cba8f5..b9765d143a 100644 --- a/build-logic/src/main/kotlin/com/datadog/gradle/utils/JsonSchemaGenerationTasks.kt +++ b/build-logic/src/main/kotlin/com/datadog/gradle/utils/JsonSchemaGenerationTasks.kt @@ -84,9 +84,19 @@ fun Project.createJsonModelsGenerationTask( " the generated sources cannot be wired to any variant." ) androidComponents.onVariants { variant -> + // Registered on BOTH source sets on purpose, they are read by different consumers: + // - `java` : KSP resolves the generated models from it (Kotlin-only registration makes + // kspDebugKotlin fail with "Error type ''"). + // - `kotlin` : detekt's Android integration collects only this one, and without it every + // type touching a generated model degrades to `UNKNOWN`, which makes + // UnsafeThirdPartyFunctionCall fire across the whole call graph. variant.sources.java?.addGeneratedSourceDirectory( task, GenerateJsonSchemaTask::destinationGenDirectory ) + variant.sources.kotlin?.addGeneratedSourceDirectory( + task, + GenerateJsonSchemaTask::destinationGenDirectory + ) } } diff --git a/build-logic/src/main/kotlin/detekt-conventions.gradle.kts b/build-logic/src/main/kotlin/detekt-conventions.gradle.kts index d1dd700b93..b13fce83b3 100644 --- a/build-logic/src/main/kotlin/detekt-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/detekt-conventions.gradle.kts @@ -1,5 +1,4 @@ -import com.android.build.api.variant.LibraryAndroidComponentsExtension -import io.gitlab.arturbosch.detekt.Detekt +import dev.detekt.gradle.Detekt /* * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. @@ -8,7 +7,7 @@ import io.gitlab.arturbosch.detekt.Detekt */ plugins { - id("io.gitlab.arturbosch.detekt") + id("dev.detekt") } dependencies { @@ -24,7 +23,7 @@ val detektConfigFiles = listOf( detekt { config.setFrom(detektConfigFiles) - ignoredVariants = listOf("release") + ignoredVariants.set(listOf("release")) } tasks.withType().configureEach { @@ -32,15 +31,17 @@ tasks.withType().configureEach { reports { sarif.required = false html.required = false - xml.required = false - txt.required = false - md.required = false + checkstyle.required = false + markdown.required = false } } // Disable the default task named "detekt" since it does not run type resolution -// Use it purely as a nicely named umbrella task -tasks.detekt { +// Use it purely as a nicely named umbrella task. +// `detektMain`/`detektTest` are the per-variant aggregate tasks registered by the Detekt Android +// integration; they carry the compile classpath, so they do run type resolution. Their nested +// components (unit tests, android tests, test fixtures) are covered by `detektTest`. +tasks.named("detekt") { isEnabled = false dependsOn( @@ -48,20 +49,3 @@ tasks.detekt { tasks.named("detektTest") ) } - -// testFixtures is an Android source set and isn't picked up by the detekt/AGP on AGP 8, this should -// be able to be deleted in AGP 9 and covered by `detektTest` -extensions.getByType().onVariants { variant -> - if (variant.name == "debug") { - variant.testFixtures?.let { testFixtures -> - val detektTestFixtures = tasks.register("detektTestFixtures") { - source(layout.projectDirectory.dir("src/testFixtures/kotlin")) - config.setFrom(detektConfigFiles) - classpath.setFrom(testFixtures.compileClasspath) - } - tasks.detekt { - dependsOn(detektTestFixtures) - } - } - } -} diff --git a/build-logic/src/main/kotlin/test-pyramid-api-surface.gradle.kts b/build-logic/src/main/kotlin/test-pyramid-api-surface.gradle.kts index c90c2b561d..f5ad0921ed 100644 --- a/build-logic/src/main/kotlin/test-pyramid-api-surface.gradle.kts +++ b/build-logic/src/main/kotlin/test-pyramid-api-surface.gradle.kts @@ -5,19 +5,20 @@ */ import com.android.build.api.variant.LibraryAndroidComponentsExtension -import io.gitlab.arturbosch.detekt.Detekt +import dev.detekt.gradle.Detekt +import dev.detekt.gradle.extensions.FailOnSeverity import org.gradle.api.artifacts.VersionCatalogsExtension import org.gradle.api.attributes.Usage import org.gradle.kotlin.dsl.getByType import org.gradle.kotlin.dsl.named import org.gradle.kotlin.dsl.the -// Applying `io.gitlab.arturbosch.detekt` would also register its own detekt tasks and apply detekt.yml +// Applying `dev.detekt` would also register its own detekt tasks and apply detekt.yml // - unwanted, since this plugin isn't linting, only using Detekt as an engine. All our own task actually needs // from the plugin is the engine classpath, so provide that directly via detached configurations instead of applying it. val detektVersion = the().named("libs").findVersion("detekt").get().requiredVersion val detektEngineClasspath = configurations.detachedConfiguration( - dependencies.create("io.gitlab.arturbosch.detekt:detekt-cli:$detektVersion") + dependencies.create("dev.detekt:detekt-cli:$detektVersion") ) val detektPluginClasspath = configurations.detachedConfiguration( dependencies.create(project(":tools:detekt")) @@ -55,12 +56,32 @@ androidComponents.onVariants(androidComponents.selector().withBuildType("debug") // Only `datadog-test-pyramid` matters here - skip Detekt's bundled default rule sets. disableDefaultRuleSets = true + // This plugin deliberately does not apply `dev.detekt`, so the conventions the plugin would put + // on every Detekt task are not applied either. These mirror `setDetektTaskDefaults` in the Detekt + // plugin and must be kept in sync with it: without them Gradle fails task validation because the + // properties have no value. Only the ones this task does not set explicitly are listed. + debug = false + parallel = false + autoCorrect = false + ignoreFailures = false + failOnSeverity = FailOnSeverity.Error + allRules = false + noJdk = false + multiPlatformEnabled = false + basePath = rootProject.projectDir.absolutePath + reports { + // outputLocation has to be set even for a disabled report, otherwise Gradle task validation + // fails on it; the Detekt plugin normally supplies these conventions. Nothing is written. + val unusedReports = layout.buildDirectory.dir("reports/detekt-test-pyramid/unused") sarif.required = false + sarif.outputLocation = unusedReports.map { it.file("report.sarif") } html.required = false - xml.required = false - txt.required = false - md.required = false + html.outputLocation = unusedReports.map { it.file("report.html") } + checkstyle.required = false + checkstyle.outputLocation = unusedReports.map { it.file("report.xml") } + markdown.required = false + markdown.outputLocation = unusedReports.map { it.file("report.md") } } // Internal working state regenerated every run, not a published output - LocalState, not @OutputFile. diff --git a/build-logic/src/main/kotlin/test-pyramid-api-usage.gradle.kts b/build-logic/src/main/kotlin/test-pyramid-api-usage.gradle.kts index 25615c84c1..b46a593448 100644 --- a/build-logic/src/main/kotlin/test-pyramid-api-usage.gradle.kts +++ b/build-logic/src/main/kotlin/test-pyramid-api-usage.gradle.kts @@ -8,7 +8,8 @@ import com.android.build.api.variant.ApplicationAndroidComponentsExtension import com.android.build.api.variant.HasUnitTest import com.android.build.api.variant.LibraryAndroidComponentsExtension import com.android.build.api.variant.Variant -import io.gitlab.arturbosch.detekt.Detekt +import dev.detekt.gradle.Detekt +import dev.detekt.gradle.extensions.FailOnSeverity import org.gradle.api.artifacts.VersionCatalogsExtension import org.gradle.api.attributes.Usage import org.gradle.api.file.RegularFile @@ -17,12 +18,12 @@ import org.gradle.kotlin.dsl.findByType import org.gradle.kotlin.dsl.named import org.gradle.kotlin.dsl.the -// Applying `io.gitlab.arturbosch.detekt` would also register its own detekt tasks and apply detekt.yml +// Applying `dev.detekt` would also register its own detekt tasks and apply detekt.yml // - unwanted, since this plugin isn't linting, only using Detekt as an engine. All our own task actually needs // from the plugin is the engine classpath, so provide that directly via detached configurations instead of applying it. val detektVersion = the().named("libs").findVersion("detekt").get().requiredVersion val detektEngineClasspath = configurations.detachedConfiguration( - dependencies.create("io.gitlab.arturbosch.detekt:detekt-cli:$detektVersion") + dependencies.create("dev.detekt:detekt-cli:$detektVersion") ) val detektPluginClasspath = configurations.detachedConfiguration( dependencies.create(project(":tools:detekt")) @@ -49,12 +50,32 @@ fun configureApiUsageVariant(variant: Variant, bootClasspath: Provider @@ -111,7 +112,7 @@ internal class NoOpInternalSdkCoreTest { // Given whenever(mockFeature.name) doReturn fakeFeatureName val expectedMessage = "[${NoOpInternalSdkCore.name}]: " + - REGISTER_FEATURE_INVOKED_ON_NO_OP_CORE_ERROR.format(fakeFeatureName) + REGISTER_FEATURE_INVOKED_ON_NO_OP_CORE_ERROR.format(Locale.US, fakeFeatureName) // When + Then Mockito.mockStatic(Log::class.java).use { mockedLog -> @@ -128,7 +129,7 @@ internal class NoOpInternalSdkCoreTest { fun `M log error W getFeature()`(@StringForgery fakeFeatureName: String) { // Given val expectedMessage = "[${NoOpInternalSdkCore.name}]: " + - GET_FEATURE_INVOKED_ON_NO_OP_CORE_ERROR.format(fakeFeatureName) + GET_FEATURE_INVOKED_ON_NO_OP_CORE_ERROR.format(Locale.US, fakeFeatureName) // When + Then Mockito.mockStatic(Log::class.java).use { mockedLog -> @@ -144,7 +145,7 @@ internal class NoOpInternalSdkCoreTest { fun `M log error only once W getFeature() {called multiple times}`(@StringForgery fakeFeatureName: String) { // Given val expectedMessage = "[${NoOpInternalSdkCore.name}]: " + - GET_FEATURE_INVOKED_ON_NO_OP_CORE_ERROR.format(fakeFeatureName) + GET_FEATURE_INVOKED_ON_NO_OP_CORE_ERROR.format(Locale.US, fakeFeatureName) // When + Then Mockito.mockStatic(Log::class.java).use { mockedLog -> diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/lifecycle/ProcessLifecycleCallbackTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/lifecycle/ProcessLifecycleCallbackTest.kt index 72b3d18dab..0dd1bdf708 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/lifecycle/ProcessLifecycleCallbackTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/lifecycle/ProcessLifecycleCallbackTest.kt @@ -127,7 +127,7 @@ internal class ProcessLifecycleCallbackTest { fun `when process started cancel existing workers`() { // Given WorkManagerImpl::class.java.setStaticValue("sDefaultInstance", mockWorkManager) - whenever(mockWorkManager.cancelAllWorkByTag(any())) doReturn mock() + whenever(mockWorkManager.cancelAllWorkByTag(ArgumentMatchers.anyString())) doReturn mock() // When testedCallback.onStarted() diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/batch/BatchFileOrchestratorTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/batch/BatchFileOrchestratorTest.kt index 5196a864a8..2ddcd27900 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/batch/BatchFileOrchestratorTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/batch/BatchFileOrchestratorTest.kt @@ -592,7 +592,7 @@ internal class BatchFileOrchestratorTest { forge.anAlphabeticalString() } - previousFile?.writeText(previousData[0]) + previousFile.writeText(previousData[0]) for (i in 1 until MAX_ITEM_PER_BATCH) { val file = testedOrchestrator.getWritableFile(1L) @@ -612,7 +612,7 @@ internal class BatchFileOrchestratorTest { .doesNotExist() .hasParent(fakeRootDir) assertThat(nextFile.name.toLong()).isEqualTo(newFileTimestamp) - assertThat(previousFile?.readText()) + assertThat(previousFile.readText()) .isEqualTo(previousData.joinToString(separator = "")) argumentCaptor { diff --git a/dd-sdk-android-core/transitiveDependencies b/dd-sdk-android-core/transitiveDependencies index c49caf76c5..2b9bf72256 100644 --- a/dd-sdk-android-core/transitiveDependencies +++ b/dd-sdk-android-core/transitiveDependencies @@ -18,7 +18,7 @@ com.squareup.okhttp3:okhttp:4.12.0 : 771 Kb com.squareup.okio:okio-jvm:3.6.0 : 351 Kb org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.10 : 959 b org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10 : 965 b -org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb +org.jetbrains.kotlin:kotlin-stdlib:2.3.20 : 1762 Kb org.jetbrains:annotations:13.0 : 17 Kb Total transitive dependencies size : 5 Mb diff --git a/dd-sdk-android-internal/api/compiler-meta.txt b/dd-sdk-android-internal/api/compiler-meta.txt index a098d10949..f757d782cb 100644 --- a/dd-sdk-android-internal/api/compiler-meta.txt +++ b/dd-sdk-android-internal/api/compiler-meta.txt @@ -1,2 +1,2 @@ -kotlin_abi_version=2.0.0 -jvm_bytecode_version=11 +kotlin_abi_version=2.1.0 +jvm_bytecode_version=17 diff --git a/dd-sdk-android-internal/build.gradle.kts b/dd-sdk-android-internal/build.gradle.kts index 7376c2c54d..ef354b4ad9 100644 --- a/dd-sdk-android-internal/build.gradle.kts +++ b/dd-sdk-android-internal/build.gradle.kts @@ -4,16 +4,16 @@ * Copyright 2016-Present Datadog, Inc. */ -import com.datadog.gradle.config.java11 -import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import com.datadog.gradle.config.java17 plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("com.google.devtools.ksp") id("datadogBuildConfig") @@ -43,7 +43,7 @@ plugins { android { namespace = "com.datadog.android.internal" compileOptions { - java11() + java17() } testFixtures { @@ -80,7 +80,7 @@ dependencies { } datadogBuild { - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyAndroidLibraryConfig() applyJunitConfig() applyJavadocConfig() diff --git a/dd-sdk-android-internal/transitiveDependencies b/dd-sdk-android-internal/transitiveDependencies index cfbfb9e59c..9cb4a570e3 100644 --- a/dd-sdk-android-internal/transitiveDependencies +++ b/dd-sdk-android-internal/transitiveDependencies @@ -1,8 +1,8 @@ Dependencies List androidx.annotation:annotation-jvm:1.9.1 : 59 Kb -org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb +org.jetbrains.kotlin:kotlin-stdlib:2.3.20 : 1762 Kb org.jetbrains:annotations:13.0 : 17 Kb -Total transitive dependencies size : 1782 Kb +Total transitive dependencies size : 1838 Kb diff --git a/detekt.yml b/detekt.yml index b80ca52eac..70356d26e7 100644 --- a/detekt.yml +++ b/detekt.yml @@ -1,13 +1,9 @@ -build: - maxIssues: 0 - excludeCorrectable: false - config: validation: true warningsAsErrors: true checkExhaustiveness: false - # when writing own rules with new properties, exclude the property path e.g.: 'my_rule_set,.*>.*>[my_property]' - excludes: '' + # when writing own rules with new properties, exclude the property path e.g.: ['my_rule_set', '.*>.*>[my_property]'] + excludes: [] processors: active: true @@ -16,26 +12,27 @@ console-reports: active: true exclude: - 'ComplexityReport' - - 'FileBasedFindingsReport' - - 'BuildFailureReport' - -output-reports: - active: false + - 'FileBasedIssuesReport' comments: active: true AbsentOrWrongFileLicense: active: true - licenseTemplateFile: 'license.template' + licenseTemplate: | + /* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ licenseTemplateIsRegex: false excludes: # Gradle build outputs and generated artifacts. - '**/build/generated/**' # Vendored test files ported from the upstream dd-trace-java library. - '**/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/**' - CommentOverPrivateFunction: + DocumentationOverPrivateFunction: active: false - CommentOverPrivateProperty: + DocumentationOverPrivateProperty: active: false DeprecatedBlockTag: active: true @@ -113,20 +110,20 @@ complexity: excludes: ['**/build/**'] CognitiveComplexMethod: active: false - threshold: 15 + allowedComplexity: 14 ComplexCondition: active: true - threshold: 4 + allowedConditions: 3 ComplexInterface: active: false - threshold: 10 + allowedDefinitions: 9 includeStaticDeclarations: false includePrivateDeclarations: false ignoreOverloaded: false CyclomaticComplexMethod: active: true excludes: ['**/test/**', '**/*Test/**', '**/testFixtures/**'] - threshold: 15 + allowedComplexity: 14 ignoreSingleWhenExpression: true ignoreSimpleWhenEntries: true ignoreNestingFunctions: false @@ -146,33 +143,33 @@ complexity: LargeClass: active: true excludes: ['**/test/**', '**/*Test/**', '**/testFixtures/**'] - threshold: 600 + allowedLines: 599 LongMethod: active: true excludes: ['**/test/**', '**/*Test/**', '**/testFixtures/**'] - threshold: 60 + allowedLines: 59 LongParameterList: active: true excludes: ['**/test/**', '**/*Test/**', '**/testFixtures/**'] - functionThreshold: 12 - constructorThreshold: 12 + allowedFunctionParameters: 11 + allowedConstructorParameters: 11 ignoreDefaultParameters: true ignoreDataClasses: true ignoreAnnotatedParameter: [] ignoreAnnotated: [ 'Composable' ] MethodOverloading: active: true - threshold: 6 + allowedOverloads: 5 NamedArguments: active: false - threshold: 6 + allowedArguments: 6 ignoreArgumentsMatchingNames: true NestedBlockDepth: active: true - threshold: 4 + allowedDepth: 3 NestedScopeFunctions: active: false - threshold: 1 + allowedDepth: 1 functions: - 'kotlin.apply' - 'kotlin.run' @@ -184,18 +181,18 @@ complexity: StringLiteralDuplication: active: true excludes: ['**/test/**', '**/*Test/**', '**/testFixtures/**'] - threshold: 3 + allowedDuplications: 2 ignoreAnnotation: true - excludeStringsWithLessThan5Characters: true + allowedWithLengthLessThan: 5 ignoreStringsRegex: '$^' TooManyFunctions: active: true excludes: ['**/test/**', '**/*Test/**', '**/testFixtures/**'] - thresholdInFiles: 11 - thresholdInClasses: 11 - thresholdInInterfaces: 11 - thresholdInObjects: 11 - thresholdInEnums: 11 + allowedFunctionsPerFile: 10 + allowedFunctionsPerClass: 10 + allowedFunctionsPerInterface: 10 + allowedFunctionsPerObject: 10 + allowedFunctionsPerEnum: 10 ignoreDeprecated: true ignorePrivate: false ignoreOverridden: true @@ -248,7 +245,7 @@ empty-blocks: active: true EmptyInitBlock: active: true - EmptyKtFile: + EmptyKotlinFile: active: true EmptySecondaryConstructor: active: true @@ -356,11 +353,11 @@ naming: active: true excludes: ['**/test/**', '**/*Test/**', '**/testFixtures/**'] forbiddenName: [] - FunctionMaxLength: + FunctionNameMaxLength: active: true excludes: ['**/test/**', '**/*Test/**', '**/testFixtures/**'] maximumFunctionNameLength: 100 - FunctionMinLength: + FunctionNameMinLength: active: true excludes: ['**/test/**', '**/*Test/**', '**/testFixtures/**'] minimumFunctionNameLength: 2 @@ -428,7 +425,8 @@ performance: active: true CouldBeSequence: active: true - threshold: 3 + excludes: ['**/test/**', '**/*Test/**', '**/testFixtures/**'] + allowedOperations: 2 ForEachOnRange: active: true excludes: ['**/test/**', '**/*Test/**', '**/testFixtures/**'] @@ -629,8 +627,8 @@ style: allowedPatterns: '' ForbiddenImport: active: false - imports: [] - forbiddenPatterns: '' + forbiddenImports: [] + allowedImports: [] ForbiddenMethodCall: active: false methods: @@ -683,7 +681,7 @@ style: excludeImportStatements: true excludeCommentStatements: true excludeRawStrings: true - MayBeConst: + MayBeConstant: active: true ModifierOrder: active: true @@ -709,15 +707,13 @@ style: active: true OptionalUnit: active: false - PreferToOverPairSyntax: - active: false ProtectedMemberInFinalClass: active: true RedundantExplicitType: active: false RedundantHigherOrderMapUsage: active: true - RedundantVisibilityModifierRule: + RedundantVisibilityModifier: active: false ReturnCount: active: true @@ -731,7 +727,7 @@ style: active: true SerialVersionUIDInSerializableClass: active: true - SpacingBetweenPackageAndImports: + SpacingAfterPackageAndImports: active: false StringShouldBeRawString: active: false @@ -753,11 +749,9 @@ style: active: false acceptableLength: 4 allowNonStandardGrouping: false - UnnecessaryAbstractClass: + AbstractClassCanBeConcreteClass: active: false excludes: ['**/test/**', '**/*Test/**', '**/testFixtures/**', '**/tools/**'] - UnnecessaryAnnotationUseSiteTarget: - active: false UnnecessaryApply: active: true UnnecessaryBackticks: @@ -775,16 +769,16 @@ style: UnnecessaryParentheses: active: false allowForUnclearPrecedence: false - UntilInsteadOfRangeTo: + RangeUntilInsteadOfRangeTo: active: false - UnusedImports: + UnusedImport: active: true UnusedParameter: active: true allowedNames: 'ignored|expected' UnusedPrivateClass: active: true - UnusedPrivateMember: + UnusedPrivateFunction: active: true allowedNames: '' ignoreAnnotated: diff --git a/detekt_custom_safe_calls_android.yml b/detekt_custom_safe_calls_android.yml index 4835caa866..bfe380b9a4 100644 --- a/detekt_custom_safe_calls_android.yml +++ b/detekt_custom_safe_calls_android.yml @@ -107,13 +107,12 @@ datadog: - "android.util.TypedValue.constructor()" - "android.view.accessibility.AccessibilityManager.addTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener)" - "android.view.accessibility.AccessibilityManager.removeTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener)" - - "android.view.accessibility.TouchExplorationStateChangeListener(kotlin.Function1)" + - "android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener(kotlin.Function1)" - "android.view.Choreographer.postFrameCallback(android.view.Choreographer.FrameCallback)" - "android.view.Display.getSize(android.graphics.Point?)" - "android.view.FrameMetrics.getMetric(kotlin.Int)" - "android.view.GestureDetector.constructor(android.content.Context?, android.view.GestureDetector.OnGestureListener)" - "android.view.GestureDetector.onTouchEvent(android.view.MotionEvent)" - - "android.view.getDefaultSize(kotlin.Int, kotlin.Int)" - "android.view.LayoutInflater.inflate(kotlin.Int, android.view.ViewGroup?, kotlin.Boolean)" - "android.view.MotionEvent.PointerCoords.constructor()" - "android.view.MotionEvent.getPointerCoords(kotlin.Int, android.view.MotionEvent.PointerCoords)" @@ -121,12 +120,12 @@ datadog: - "android.view.MotionEvent.getRawX(kotlin.Int)" - "android.view.MotionEvent.getRawY(kotlin.Int)" - "android.view.MotionEvent.recycle()" - - "android.view.resolveSize(kotlin.Int, kotlin.Int)" - "android.view.View.addOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener?)" - "android.view.View.animate()" - "android.view.View.bringToFront()" - "android.view.View.findViewById(kotlin.Int)" - "android.view.View.getChildAt(kotlin.Int)" + - "android.view.View.getDefaultSize(kotlin.Int, kotlin.Int)" - "android.view.View.getTag(kotlin.Int)" - "android.view.View.hashCode()" - "android.view.View.onDraw(android.graphics.Canvas)" @@ -134,6 +133,7 @@ datadog: - "android.view.View.postInvalidateOnAnimation()" - "android.view.View.post(java.lang.Runnable?)" - "android.view.View.removeOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener?)" + - "android.view.View.resolveSize(kotlin.Int, kotlin.Int)" - "android.view.View.setOnClickListener(android.view.View.OnClickListener?)" - "android.view.View.setOnTouchListener(android.view.View.OnTouchListener?)" - "android.view.View.setTag(kotlin.Int, kotlin.Any?)" diff --git a/detekt_custom_safe_calls_third_party.yml b/detekt_custom_safe_calls_third_party.yml index 5fe259ef1a..d51cf294c2 100644 --- a/detekt_custom_safe_calls_third_party.yml +++ b/detekt_custom_safe_calls_third_party.yml @@ -75,8 +75,8 @@ datadog: - "com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader.Factory.constructor(okhttp3.Call.Factory)" - "com.bumptech.glide.load.engine.executor.GlideExecutor.Builder.build()" - "com.bumptech.glide.load.engine.executor.GlideExecutor.Builder.setUncaughtThrowableStrategy(com.bumptech.glide.load.engine.executor.GlideExecutor.UncaughtThrowableStrategy)" - - "com.bumptech.glide.load.engine.executor.newDiskCacheBuilder()" - - "com.bumptech.glide.load.engine.executor.newSourceBuilder()" + - "com.bumptech.glide.load.engine.executor.GlideExecutor.newDiskCacheBuilder()" + - "com.bumptech.glide.load.engine.executor.GlideExecutor.newSourceBuilder()" # endregion # region Gson - "com.google.gson.JsonArray.add(com.google.gson.JsonElement?)" @@ -117,12 +117,16 @@ datadog: # region Java Collections - "java.util.ArrayList.add(com.datadog.android.rum.internal.timeseries.DataPoint)" - "java.util.ArrayList.clear()" + - "java.util.ArrayList.constructor(kotlin.Int)" + - "java.util.ArrayList.constructor(kotlin.collections.MutableCollection?)" - "java.util.ArrayList.forEach(kotlin.Function1)" - "java.util.ArrayList.toList()" - "java.util.Deque.peekLast()" - "java.util.Deque.toList()" - "java.util.HashSet.add(kotlin.String)" - "java.util.HashSet.clear()" + - "java.util.HashSet.constructor()" + - "java.util.HashSet.constructor(kotlin.collections.MutableCollection?)" - "java.util.HashSet.remove(kotlin.String)" - "java.util.HashSet.toList()" - "java.util.LinkedList.add(android.view.View)" @@ -137,8 +141,7 @@ datadog: - "java.util.LinkedList.add(kotlin.Pair)" - "java.util.LinkedList.addAll(kotlin.collections.Collection)" - "java.util.LinkedList.addAll(kotlin.Int, kotlin.collections.Collection)" - - "java.util.LinkedList.addFirst(android.view.View?)" - - "java.util.LinkedList.addFirst(com.datadog.android.webview.internal.rum.domain.WebViewNativeRumViewsCache.ViewEntry?)" + - "java.util.LinkedList.addFirst()" - "java.util.LinkedList.clear()" - "java.util.LinkedList.constructor()" - "java.util.LinkedList.constructor(kotlin.collections.MutableCollection?)" @@ -152,6 +155,8 @@ datadog: - "java.util.LinkedList.poll()" - "java.util.LinkedList.remove(com.datadog.android.privacy.TrackingConsentProviderCallback)" - "java.util.LinkedList.remove(com.datadog.android.webview.internal.rum.domain.WebViewNativeRumViewsCache.ViewEntry)" + - "java.util.LinkedHashMap.constructor()" + - "java.util.LinkedHashMap.constructor(kotlin.collections.MutableMap?)" - "java.util.LinkedHashMap.remove(kotlin.String)" - "java.util.LinkedHashSet.add(kotlin.String)" - "java.util.LinkedHashSet.isNotEmpty()" @@ -243,6 +248,8 @@ datadog: - "java.util.concurrent.TimeUnit.NANOSECONDS.toMillis(kotlin.Long)" - "java.util.concurrent.TimeUnit.SECONDS.toMillis(kotlin.Long)" - "java.util.concurrent.TimeUnit.SECONDS.toNanos(kotlin.Long)" + - "java.util.concurrent.TimeUnit.toMillis(kotlin.Long)" + - "java.util.concurrent.TimeUnit.toNanos(kotlin.Long)" - "java.util.concurrent.atomic.AtomicBoolean.compareAndSet(kotlin.Boolean, kotlin.Boolean)" - "java.util.concurrent.atomic.AtomicBoolean.constructor(kotlin.Boolean)" - "java.util.concurrent.atomic.AtomicBoolean.get()" @@ -325,7 +332,6 @@ datadog: - "java.io.InputStream.reader(java.nio.charset.Charset)" - "java.io.IOException.constructor(kotlin.String?)" - "java.io.StringWriter.constructor()" - - "java.io.close()" # endregion # region Java misc - "java.lang.Character.isWhitespace(kotlin.Int)" @@ -334,6 +340,7 @@ datadog: - "java.lang.Class.getDeclaredFieldSafe(kotlin.String)" - "java.lang.Exception.addSuppressed(kotlin.Throwable)" - "java.lang.IllegalArgumentException.constructor(kotlin.String)" + - "java.lang.IllegalArgumentException.constructor(kotlin.String?)" - "java.lang.IllegalStateException.constructor(kotlin.String)" - "java.lang.IllegalStateException.constructor(kotlin.String?)" - "java.lang.Object.constructor()" @@ -345,6 +352,7 @@ datadog: - "java.lang.System.getProperty(kotlin.String?)" - "java.lang.System.identityHashCode(kotlin.Any?)" - "java.lang.System.nanoTime()" + - "java.lang.UnsupportedOperationException.constructor()" - "java.lang.ref.Reference.get()" - "java.lang.ref.WeakReference.clear()" - "java.lang.ref.WeakReference.constructor(android.app.Activity?)" @@ -361,6 +369,7 @@ datadog: - "java.lang.reflect.isStatic(kotlin.Int)" - "java.lang.reflect.Field.getSafe(kotlin.Any?)" - "java.lang.reflect.Field.accessible()" + - "java.lang.reflect.Modifier.isStatic(kotlin.Int)" - "java.lang.StringBuilder.append(kotlin.Char)" - "java.lang.StringBuilder.append(kotlin.CharArray?)" - "java.lang.StringBuilder.append(kotlin.String?)" @@ -385,6 +394,7 @@ datadog: - "java.util.Date.constructor(kotlin.Long)" - "java.util.function.Consumer(kotlin.Function1)" - "java.util.HashMap.clear()" + - "java.util.HashMap.constructor()" - "java.util.HashMap.orEmpty()" - "java.util.HashSet.addAll(kotlin.collections.Collection)" - "java.util.HashSet.find(kotlin.Function1)" @@ -427,12 +437,14 @@ datadog: - "kotlin.runCatching(kotlin.Function0)" - "kotlin.synchronized(kotlin.Any, kotlin.Function0)" - "kotlin.ReplaceWith.constructor(kotlin.String, kotlin.Array)" + - "kotlin.Result.Companion.failure(kotlin.Throwable)" - "kotlin.Result.failure(kotlin.Throwable)" - "kotlin.Result.fold(kotlin.Function1, kotlin.Function1)" - "kotlin.Result.getOrDefault(kotlin.Long)" - "kotlin.Result.getOrElse(kotlin.Function1)" - "kotlin.Result.map(kotlin.Function1)" - "kotlin.Result.onFailure(kotlin.Function1)" + - "kotlin.synchronized(kotlin.Any, kotlin.Function0)" # endregion # region Kotlin Collections - "kotlin.Array.all(kotlin.Function1)" @@ -689,10 +701,13 @@ datadog: - "kotlin.collections.MutableMap.put(kotlin.Any?, kotlin.Any?)" - "kotlin.collections.MutableMap.put(kotlin.String, kotlin.Any)" - "kotlin.collections.MutableMap.put(kotlin.String, kotlin.Any?)" + - "kotlin.collections.MutableMap.put(kotlin.String, kotlin.Comparable)" + - "kotlin.collections.MutableMap.put(kotlin.String, kotlin.Comparable?)" - "kotlin.collections.MutableMap.put(kotlin.String, kotlin.String)" - "kotlin.collections.MutableMap.put(kotlin.String, kotlin.Int)" - "kotlin.collections.MutableMap.put(kotlin.String, kotlin.Long?)" - "kotlin.collections.MutableMap.put(kotlin.String, kotlin.Long)" + - "kotlin.collections.MutableMap.put(kotlin.String, kotlin.Number)" - "kotlin.collections.MutableMap.put(kotlin.String, kotlin.collections.Map)" - "kotlin.collections.MutableMap.put(kotlin.String, UNKNOWN)" - "kotlin.collections.MutableMap.putAll(kotlin.collections.Iterable)" @@ -942,6 +957,7 @@ datadog: - "kotlin.String.all(kotlin.Function1)" - "kotlin.String.any(kotlin.Function1)" - "kotlin.String.codePoints()" + - "kotlin.String.Companion.format(java.util.Locale?, kotlin.String, kotlin.Array)" - "kotlin.String.constructor()" - "kotlin.String.contains(kotlin.Char, kotlin.Boolean)" - "kotlin.String.contains(kotlin.CharSequence, kotlin.Boolean)" @@ -1169,12 +1185,17 @@ datadog: - "io.opentelemetry.context.Scope.close()" # endregion # region RxJava + - "io.reactivex.rxjava3.core.Completable.doOnError(io.reactivex.rxjava3.functions.Consumer)" - "io.reactivex.rxjava3.core.Completable.doOnError(io.reactivex.rxjava3.functions.Consumer?)" + - "io.reactivex.rxjava3.core.Flowable.doOnError(io.reactivex.rxjava3.functions.Consumer)" - "io.reactivex.rxjava3.core.Flowable.doOnError(io.reactivex.rxjava3.functions.Consumer?)" + - "io.reactivex.rxjava3.core.Maybe.doOnError(io.reactivex.rxjava3.functions.Consumer)" - "io.reactivex.rxjava3.core.Maybe.doOnError(io.reactivex.rxjava3.functions.Consumer?)" + - "io.reactivex.rxjava3.core.Observable.doOnError(io.reactivex.rxjava3.functions.Consumer)" - "io.reactivex.rxjava3.core.Observable.doOnError(io.reactivex.rxjava3.functions.Consumer?)" + - "io.reactivex.rxjava3.core.Single.doOnError(io.reactivex.rxjava3.functions.Consumer)" - "io.reactivex.rxjava3.core.Single.doOnError(io.reactivex.rxjava3.functions.Consumer?)" # endregion # region SQLDelight - - "com.squareup.sqldelight.android.AndroidSqliteDriver.Callback.onCorruption(androidx.sqlite.db.SupportSQLiteDatabase)" + - "app.cash.sqldelight.driver.android.AndroidSqliteDriver.Callback.onCorruption(androidx.sqlite.db.SupportSQLiteDatabase)" # endregion diff --git a/detekt_custom_unsafe_calls.yml b/detekt_custom_unsafe_calls.yml index 7a507483e8..e0230d4a45 100644 --- a/detekt_custom_unsafe_calls.yml +++ b/detekt_custom_unsafe_calls.yml @@ -58,7 +58,7 @@ datadog: - "androidx.collection.LruCache.get(kotlin.String):java.lang.NullPointerException" - "androidx.collection.LruCache.put(java.io.File, kotlin.Unit):java.lang.NullPointerException" - "androidx.collection.LruCache.remove(java.io.File):java.lang.NullPointerException" - - "androidx.metrics.performance.JankStats.createAndTrack(android.view.Window, androidx.metrics.performance.JankStats.OnFrameListener):java.lang.IllegalStateException" + - "androidx.metrics.performance.JankStats.Companion.createAndTrack(android.view.Window, androidx.metrics.performance.JankStats.OnFrameListener):java.lang.IllegalStateException" - "androidx.navigation.Navigation.findNavController(android.app.Activity, kotlin.Int):java.lang.IllegalStateException" - "androidx.work.WorkManager.enqueueUniqueWork(kotlin.String, androidx.work.ExistingWorkPolicy, androidx.work.OneTimeWorkRequest):java.util.concurrent.RejectedExecutionException,java.lang.NullPointerException" - "androidx.work.Data.Builder.build():java.lang.IllegalStateException" @@ -72,6 +72,7 @@ datadog: # endregion # region Java File - "java.io.ByteArrayOutputStream.write(kotlin.ByteArray, kotlin.Int, kotlin.Int):java.lang.IndexOutOfBoundsException" + - "java.io.Closeable.close():java.io.IOException" - "java.io.File.canRead():java.lang.SecurityException" - "java.io.File.canWrite():java.lang.SecurityException" - "java.io.File.delete():java.lang.SecurityException" @@ -153,7 +154,7 @@ datadog: - "java.lang.Character.toChars(kotlin.Int):java.lang.IllegalArgumentException" - "java.lang.Class.forName(kotlin.String?):java.lang.LinkageError,java.lang.ExceptionInInitializerError,java.lang.ClassNotFoundException" - "java.lang.Class.getDeclaredField(kotlin.String?):java.lang.NoSuchFieldException,java.lang.SecurityException,java.lang.NullPointerException" - - "java.lang.Class.getDeclaredMethod(kotlin.String?, kotlin.Array?):java.lang.NoSuchMethodException,java.lang.SecurityException,java.lang.NullPointerException" + - "java.lang.Class.getDeclaredMethod(kotlin.String?, kotlin.Array):java.lang.NoSuchMethodException,java.lang.SecurityException,java.lang.NullPointerException" - "java.lang.Class.getMethod(kotlin.String?, kotlin.Array?):java.lang.NoSuchMethodException,java.lang.SecurityException,java.lang.NullPointerException" - "java.lang.Class.isAssignableFrom(java.lang.Class?):java.lang.NullPointerException" - "java.lang.Runtime.addShutdownHook(java.lang.Thread):java.lang.IllegalArgumentException,java.lang.IllegalStateException,java.lang.SecurityException" @@ -179,6 +180,7 @@ datadog: - "java.util.Collections.newSetFromMap(kotlin.collections.MutableMap?):java.lang.IllegalArgumentException" - "java.util.LinkedList.add(kotlin.Int, android.view.View):java.lang.IndexOutOfBoundsException" - "java.util.LinkedList.add(kotlin.Int, com.datadog.android.webview.internal.rum.domain.WebViewNativeRumViewsCache.ViewEntry):java.lang.IndexOutOfBoundsException" + - "java.util.LinkedList.first():java.util.NoSuchElementException" - "java.util.LinkedList.offer(com.datadog.android.core.internal.data.upload.UploadWorker.UploadNextBatchTask):java.lang.ClassCastException,java.lang.NullPointerException,java.lang.IllegalArgumentException" - "java.util.LinkedList.removeFirst():java.util.NoSuchElementException" - "java.util.LinkedList.removeLast():java.util.NoSuchElementException" diff --git a/features/dd-sdk-android-flags-openfeature/api/compiler-meta.txt b/features/dd-sdk-android-flags-openfeature/api/compiler-meta.txt index a098d10949..f757d782cb 100644 --- a/features/dd-sdk-android-flags-openfeature/api/compiler-meta.txt +++ b/features/dd-sdk-android-flags-openfeature/api/compiler-meta.txt @@ -1,2 +1,2 @@ -kotlin_abi_version=2.0.0 -jvm_bytecode_version=11 +kotlin_abi_version=2.1.0 +jvm_bytecode_version=17 diff --git a/features/dd-sdk-android-flags-openfeature/build.gradle.kts b/features/dd-sdk-android-flags-openfeature/build.gradle.kts index 790545be70..e6992f1b3e 100644 --- a/features/dd-sdk-android-flags-openfeature/build.gradle.kts +++ b/features/dd-sdk-android-flags-openfeature/build.gradle.kts @@ -4,15 +4,14 @@ * Copyright 2016-Present Datadog, Inc. */ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("datadogBuildConfig") // Publish @@ -73,7 +72,7 @@ unMock { } datadogBuild { - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyAndroidLibraryConfig() applyJunitConfig() applyJavadocConfig() diff --git a/features/dd-sdk-android-flags-openfeature/src/main/kotlin/com/datadog/android/flags/openfeature/DatadogFlagsProvider.kt b/features/dd-sdk-android-flags-openfeature/src/main/kotlin/com/datadog/android/flags/openfeature/DatadogFlagsProvider.kt index 469c37bd9f..68d43a99dc 100644 --- a/features/dd-sdk-android-flags-openfeature/src/main/kotlin/com/datadog/android/flags/openfeature/DatadogFlagsProvider.kt +++ b/features/dd-sdk-android-flags-openfeature/src/main/kotlin/com/datadog/android/flags/openfeature/DatadogFlagsProvider.kt @@ -212,6 +212,7 @@ class DatadogFlagsProvider private constructor(private val flagsClient: FlagsCli // Safe: We properly call awaitClose to cleanup listener, satisfying callbackFlow requirements @Suppress("UnsafeThirdPartyFunctionCall") override fun observe(): Flow = callbackFlow { + val scope = this val listener = object : FlagsStateListener { override fun onStateChanged(newState: FlagsClientState) { val providerEvent: OpenFeatureProviderEvents? = when (newState) { @@ -223,7 +224,7 @@ class DatadogFlagsProvider private constructor(private val flagsClient: FlagsCli error = OpenFeatureError.ProviderFatalError() ) } - providerEvent?.let { trySend(it) } + if (providerEvent != null) scope.trySend(providerEvent) } } diff --git a/features/dd-sdk-android-flags-openfeature/src/test/kotlin/com/datadog/android/flags/openfeature/DatadogFlagsProviderTest.kt b/features/dd-sdk-android-flags-openfeature/src/test/kotlin/com/datadog/android/flags/openfeature/DatadogFlagsProviderTest.kt index 727e0e3839..b294320918 100644 --- a/features/dd-sdk-android-flags-openfeature/src/test/kotlin/com/datadog/android/flags/openfeature/DatadogFlagsProviderTest.kt +++ b/features/dd-sdk-android-flags-openfeature/src/test/kotlin/com/datadog/android/flags/openfeature/DatadogFlagsProviderTest.kt @@ -44,7 +44,6 @@ import org.mockito.junit.jupiter.MockitoSettings import org.mockito.kotlin.any import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.doAnswer -import org.mockito.kotlin.doNothing import org.mockito.kotlin.eq import org.mockito.kotlin.verify import org.mockito.kotlin.whenever @@ -83,7 +82,6 @@ internal class DatadogFlagsProviderTest { lenient().whenever(mockStateObservable.addListener(any())).doAnswer { capturedStateListener = it.getArgument(0) - Unit } lenient().apply { doNothing().whenever(mockStateObservable).removeListener(any()) } diff --git a/features/dd-sdk-android-flags-openfeature/transitiveDependencies b/features/dd-sdk-android-flags-openfeature/transitiveDependencies index c8d0ba5c20..e8c8293a3a 100644 --- a/features/dd-sdk-android-flags-openfeature/transitiveDependencies +++ b/features/dd-sdk-android-flags-openfeature/transitiveDependencies @@ -2,10 +2,8 @@ Dependencies List androidx.annotation:annotation-jvm:1.9.1 : 59 Kb dev.openfeature:kotlin-sdk-android:0.6.2 : 118 Kb -org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.20 : 963 b -org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.20 : 969 b -org.jetbrains.kotlin:kotlin-stdlib:2.1.21 : 1683 Kb -org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.3 : 1514 Kb +org.jetbrains.kotlin:kotlin-stdlib:2.3.20 : 1762 Kb +org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.10.2 : 1444 Kb org.jetbrains:annotations:23.0.0 : 28 Kb Total transitive dependencies size : 3 Mb diff --git a/features/dd-sdk-android-flags/api/compiler-meta.txt b/features/dd-sdk-android-flags/api/compiler-meta.txt index a098d10949..f757d782cb 100644 --- a/features/dd-sdk-android-flags/api/compiler-meta.txt +++ b/features/dd-sdk-android-flags/api/compiler-meta.txt @@ -1,2 +1,2 @@ -kotlin_abi_version=2.0.0 -jvm_bytecode_version=11 +kotlin_abi_version=2.1.0 +jvm_bytecode_version=17 diff --git a/features/dd-sdk-android-flags/build.gradle.kts b/features/dd-sdk-android-flags/build.gradle.kts index 3a52437f0c..c4895eb46d 100644 --- a/features/dd-sdk-android-flags/build.gradle.kts +++ b/features/dd-sdk-android-flags/build.gradle.kts @@ -5,15 +5,15 @@ */ import com.datadog.gradle.utils.createJsonModelsGenerationTask -import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("com.google.devtools.ksp") id("datadogBuildConfig") @@ -83,7 +83,7 @@ unMock { } datadogBuild { - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyAndroidLibraryConfig() applyJunitConfig() applyJavadocConfig() diff --git a/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/_FlagsInternalProxy.kt b/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/_FlagsInternalProxy.kt index 5b232f5298..4946641958 100644 --- a/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/_FlagsInternalProxy.kt +++ b/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/_FlagsInternalProxy.kt @@ -12,17 +12,17 @@ import com.datadog.android.flags.model.UnparsedFlag import com.datadog.android.lint.InternalApi /** - * This class exposes internal methods that are used by other Datadog modules and cross platform + * This class exposes internal methods that are used by other Datadog modules and cross-platform * frameworks. It is not meant for public use. * * DO NOT USE this class or its methods if you are not working on the internals of the Datadog SDK - * or one of the cross platform frameworks. + * or one of the cross-platform frameworks. * * Methods, members, and functionality of this class are subject to change without notice, as they * are not considered part of the public interface of the Datadog SDK. */ @InternalApi -@Suppress("ClassName", "UndocumentedPublicFunction") +@Suppress("ClassName", "ClassNaming", "UndocumentedPublicFunction") class _FlagsInternalProxy(private val client: FlagsClient) { fun getFlagAssignmentsSnapshot(): Map = if (client is DatadogFlagsClient) { client.getFlagAssignmentsSnapshot() diff --git a/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/internal/DatadogFlagsClient.kt b/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/internal/DatadogFlagsClient.kt index a394131d39..f4a546720d 100644 --- a/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/internal/DatadogFlagsClient.kt +++ b/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/internal/DatadogFlagsClient.kt @@ -50,7 +50,7 @@ internal class DatadogFlagsClient( private val rumEvaluationLogger: RumEvaluationLogger, private val exposureProcessor: EventsProcessor, private val evaluationsFeature: EvaluationsFeature?, - private val flagStateManager: FlagsStateManager + flagStateManager: FlagsStateManager ) : FlagsClient { override val state: StateObservable = flagStateManager diff --git a/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/internal/repository/DefaultFlagsRepository.kt b/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/internal/repository/DefaultFlagsRepository.kt index 48e4798e5b..24e93d0f56 100644 --- a/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/internal/repository/DefaultFlagsRepository.kt +++ b/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/internal/repository/DefaultFlagsRepository.kt @@ -19,8 +19,8 @@ import java.util.concurrent.atomic.AtomicReference internal class DefaultFlagsRepository( private val featureSdkCore: FeatureSdkCore, - private val instanceName: String, - private val dataStore: DataStoreHandler, + instanceName: String, + dataStore: DataStoreHandler, private val internalLogger: InternalLogger = featureSdkCore.internalLogger, private val persistenceLoadTimeoutMs: Long = PERSISTENCE_LOAD_TIMEOUT_MS ) : FlagsRepository { diff --git a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/NoOpFlagsClientTest.kt b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/NoOpFlagsClientTest.kt index 4fde3f0c4d..bf30f2018c 100644 --- a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/NoOpFlagsClientTest.kt +++ b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/NoOpFlagsClientTest.kt @@ -219,7 +219,7 @@ internal class NoOpFlagsClientTest { val fakeDefaultValue = forge.aDouble() // When - val result = testedClient.resolveDoubleValue(fakeFlagKey, fakeDefaultValue.toDouble()) + val result = testedClient.resolveDoubleValue(fakeFlagKey, fakeDefaultValue) // Then assertThat(result).isEqualTo(fakeDefaultValue) diff --git a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/EvaluationEventsProcessorTest.kt b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/EvaluationEventsProcessorTest.kt index dd8cf16ad5..86af33e101 100644 --- a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/EvaluationEventsProcessorTest.kt +++ b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/EvaluationEventsProcessorTest.kt @@ -829,7 +829,7 @@ internal class EvaluationEventsProcessorTest { val writeCount = AtomicInteger(0) val trackingWriter = object : EvaluationEventWriter { override fun writeAll(events: List) { - writeCount.addAndGet(events.sumOf { it.count }.toInt()) + writeCount.addAndGet(events.sumOf { it.count }) } } diff --git a/features/dd-sdk-android-flags/transitiveDependencies b/features/dd-sdk-android-flags/transitiveDependencies index c4394247cc..c493d28c9c 100644 --- a/features/dd-sdk-android-flags/transitiveDependencies +++ b/features/dd-sdk-android-flags/transitiveDependencies @@ -7,7 +7,7 @@ com.squareup.okhttp3:okhttp:4.12.0 : 771 Kb com.squareup.okio:okio-jvm:3.6.0 : 351 Kb org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.10 : 959 b org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10 : 965 b -org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb +org.jetbrains.kotlin:kotlin-stdlib:2.3.20 : 1762 Kb org.jetbrains:annotations:13.0 : 17 Kb Total transitive dependencies size : 3 Mb diff --git a/features/dd-sdk-android-logs/api/compiler-meta.txt b/features/dd-sdk-android-logs/api/compiler-meta.txt index a098d10949..f757d782cb 100644 --- a/features/dd-sdk-android-logs/api/compiler-meta.txt +++ b/features/dd-sdk-android-logs/api/compiler-meta.txt @@ -1,2 +1,2 @@ -kotlin_abi_version=2.0.0 -jvm_bytecode_version=11 +kotlin_abi_version=2.1.0 +jvm_bytecode_version=17 diff --git a/features/dd-sdk-android-logs/build.gradle.kts b/features/dd-sdk-android-logs/build.gradle.kts index 2afc62f73d..21075667d3 100644 --- a/features/dd-sdk-android-logs/build.gradle.kts +++ b/features/dd-sdk-android-logs/build.gradle.kts @@ -6,16 +6,16 @@ @file:Suppress("StringLiteralDuplication") import com.datadog.gradle.utils.createJsonModelsGenerationTask -import org.jetbrains.kotlin.gradle.dsl.JvmTarget import java.nio.file.Paths plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("com.google.devtools.ksp") id("datadogBuildConfig") @@ -90,7 +90,7 @@ createJsonModelsGenerationTask("generateLogModelsFromJson") { } datadogBuild { - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyAndroidLibraryConfig() applyJunitConfig() applyJavadocConfig() diff --git a/features/dd-sdk-android-logs/src/main/kotlin/com/datadog/android/log/Logger.kt b/features/dd-sdk-android-logs/src/main/kotlin/com/datadog/android/log/Logger.kt index f254df5a00..bfaee361e8 100644 --- a/features/dd-sdk-android-logs/src/main/kotlin/com/datadog/android/log/Logger.kt +++ b/features/dd-sdk-android-logs/src/main/kotlin/com/datadog/android/log/Logger.kt @@ -4,6 +4,8 @@ * Copyright 2016-Present Datadog, Inc. */ +@file:Suppress("FunctionNameMinLength") + package com.datadog.android.log import androidx.annotation.FloatRange diff --git a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/LoggerTest.kt b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/LoggerTest.kt index d45627787f..506da20aa0 100644 --- a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/LoggerTest.kt +++ b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/LoggerTest.kt @@ -389,6 +389,9 @@ internal class LoggerTest { } @Test + // NULL_MAP_VALUE is typed java.lang.Object (RUM-373); mapOf(...) infers Map<_, Object> here. + // Suppress at the use site until RUM-373 removes the constant. + @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") fun `add null String attribute to logger`(forge: Forge) { val key = forge.anAlphabeticalString() val value: String? = null diff --git a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/domain/event/LogEventSerializerTest.kt b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/domain/event/LogEventSerializerTest.kt index b7b8a9c402..1673b3a510 100644 --- a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/domain/event/LogEventSerializerTest.kt +++ b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/domain/event/LogEventSerializerTest.kt @@ -169,11 +169,9 @@ internal class LogEventSerializerTest { } val faultyLogEvent = fakeLog.copy( usr = fakeLog.usr?.copy( - additionalProperties = fakeLog.usr?.additionalProperties - ?.toMutableMap() - ?.apply { put(faultyKey, faultyObject) } - .orEmpty() + additionalProperties = fakeLog.usr.additionalProperties .toMutableMap() + .apply { put(faultyKey, faultyObject) } ) ) @@ -198,11 +196,9 @@ internal class LogEventSerializerTest { } val faultyLogEvent = fakeLog.copy( account = fakeLog.account?.copy( - additionalProperties = fakeLog.account?.additionalProperties - ?.toMutableMap() - ?.apply { put(faultyKey, faultyObject) } - .orEmpty() + additionalProperties = fakeLog.account.additionalProperties .toMutableMap() + .apply { put(faultyKey, faultyObject) } ) ) @@ -371,7 +367,7 @@ internal class LogEventSerializerTest { doesNotHaveField(KEY_USR_EMAIL) } containsExtraAttributes( - userInfo.additionalProperties.minus(LogEvent.Usr.RESERVED_PROPERTIES) + userInfo.additionalProperties.minus(LogEvent.Usr.RESERVED_PROPERTIES.toSet()) ) } @@ -391,7 +387,7 @@ internal class LogEventSerializerTest { doesNotHaveField(KEY_ACCOUNT_NAME) } containsExtraAttributes( - accountInfo.additionalProperties.minus(LogEvent.Account.RESERVED_PROPERTIES) + accountInfo.additionalProperties.minus(LogEvent.Account.RESERVED_PROPERTIES.toSet()) ) } diff --git a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/net/LogsRequestFactoryTest.kt b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/net/LogsRequestFactoryTest.kt index b9660fb137..d8e4a611f3 100644 --- a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/net/LogsRequestFactoryTest.kt +++ b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/net/LogsRequestFactoryTest.kt @@ -48,16 +48,15 @@ internal class LogsRequestFactoryTest { ) } - @Suppress("NAME_SHADOWING") @Test fun `M create a proper request W create()`( @Forgery batchData: List, @Forgery executionContext: RequestExecutionContext, - @StringForgery batchMetadata: String, + @StringForgery fakeBatchMetadata: String, forge: Forge ) { // Given - val batchMetadata = forge.aNullable { batchMetadata.toByteArray() } + val batchMetadata = forge.aNullable { fakeBatchMetadata.toByteArray() } // When val request = testedFactory.create(fakeDatadogContext, executionContext, batchData, batchMetadata) @@ -90,12 +89,11 @@ internal class LogsRequestFactoryTest { ) } - @Suppress("NAME_SHADOWING") @Test fun `M create a proper request W create() { custom endpoint }`( @StringForgery(regex = "https://[a-z]+\\.com(/[a-z]+)+") fakeEndpoint: String, @Forgery batchData: List, - @StringForgery batchMetadata: String, + @StringForgery fakeBatchMetadata: String, @Forgery executionContext: RequestExecutionContext, forge: Forge ) { @@ -104,7 +102,7 @@ internal class LogsRequestFactoryTest { customEndpointUrl = fakeEndpoint, internalLogger = InternalLogger.UNBOUND ) - val batchMetadata = forge.aNullable { batchMetadata.toByteArray() } + val batchMetadata = forge.aNullable { fakeBatchMetadata.toByteArray() } // When val request = testedFactory.create(fakeDatadogContext, executionContext, batchData, batchMetadata) diff --git a/features/dd-sdk-android-logs/src/testDebug/java/com/datadog/android/log/internal/logger/LogcatLogHandlerJavaTest.java b/features/dd-sdk-android-logs/src/testDebug/java/com/datadog/android/log/internal/logger/LogcatLogHandlerJavaTest.java index 486117388e..53f807def2 100644 --- a/features/dd-sdk-android-logs/src/testDebug/java/com/datadog/android/log/internal/logger/LogcatLogHandlerJavaTest.java +++ b/features/dd-sdk-android-logs/src/testDebug/java/com/datadog/android/log/internal/logger/LogcatLogHandlerJavaTest.java @@ -29,7 +29,7 @@ public class LogcatLogHandlerJavaTest { void resolves_stack_trace_element_null_if_in_release_mode() { testedHandler = new LogcatLogHandler(fakeServiceName, true, false); - StackTraceElement element = testedHandler.getCallerStackElement$dd_sdk_android_logs_debug(); + StackTraceElement element = testedHandler.getCallerStackElement$dd_sdk_android_logs(); assertThat(element) .isNull(); @@ -39,7 +39,7 @@ void resolves_stack_trace_element_null_if_in_release_mode() { void resolves_stack_trace_element_null_if_useClassnameAsTag_is_false() { testedHandler = new LogcatLogHandler(fakeServiceName, false, true); - StackTraceElement element = testedHandler.getCallerStackElement$dd_sdk_android_logs_debug(); + StackTraceElement element = testedHandler.getCallerStackElement$dd_sdk_android_logs(); assertThat(element) .isNull(); @@ -49,7 +49,7 @@ void resolves_stack_trace_element_null_if_useClassnameAsTag_is_false() { void resolves_stack_trace_element_from_caller() { testedHandler = new LogcatLogHandler(fakeServiceName, true, true); - StackTraceElement element = testedHandler.getCallerStackElement$dd_sdk_android_logs_debug(); + StackTraceElement element = testedHandler.getCallerStackElement$dd_sdk_android_logs(); assertThat(element).isNotNull(); assertThat(element.getClassName()) @@ -65,7 +65,7 @@ void resolves_nested_stack_trace_element_from_caller() { Runnable runnable = new Runnable() { @Override public void run() { - elementRef.set(testedHandler.getCallerStackElement$dd_sdk_android_logs_debug()); + elementRef.set(testedHandler.getCallerStackElement$dd_sdk_android_logs()); } }; runnable.run(); diff --git a/features/dd-sdk-android-logs/transitiveDependencies b/features/dd-sdk-android-logs/transitiveDependencies index b099002d83..74216ceb2c 100644 --- a/features/dd-sdk-android-logs/transitiveDependencies +++ b/features/dd-sdk-android-logs/transitiveDependencies @@ -2,7 +2,7 @@ Dependencies List androidx.annotation:annotation-jvm:1.9.1 : 59 Kb com.google.code.gson:gson:2.10.1 : 276 Kb -org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb +org.jetbrains.kotlin:kotlin-stdlib:2.3.20 : 1762 Kb org.jetbrains:annotations:13.0 : 17 Kb Total transitive dependencies size : 2 Mb diff --git a/features/dd-sdk-android-ndk/api/compiler-meta.txt b/features/dd-sdk-android-ndk/api/compiler-meta.txt index a098d10949..f757d782cb 100644 --- a/features/dd-sdk-android-ndk/api/compiler-meta.txt +++ b/features/dd-sdk-android-ndk/api/compiler-meta.txt @@ -1,2 +1,2 @@ -kotlin_abi_version=2.0.0 -jvm_bytecode_version=11 +kotlin_abi_version=2.1.0 +jvm_bytecode_version=17 diff --git a/features/dd-sdk-android-ndk/build.gradle.kts b/features/dd-sdk-android-ndk/build.gradle.kts index 17ad9c5097..fb4f35c448 100644 --- a/features/dd-sdk-android-ndk/build.gradle.kts +++ b/features/dd-sdk-android-ndk/build.gradle.kts @@ -6,15 +6,15 @@ import com.datadog.gradle.Dependencies import com.datadog.gradle.config.AndroidConfig -import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("datadogBuildConfig") // Publish @@ -101,7 +101,7 @@ dependencies { } datadogBuild { - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyAndroidLibraryConfig() applyJunitConfig() applyJavadocConfig() diff --git a/features/dd-sdk-android-ndk/transitiveDependencies b/features/dd-sdk-android-ndk/transitiveDependencies index ab5e1f156f..e578534395 100644 --- a/features/dd-sdk-android-ndk/transitiveDependencies +++ b/features/dd-sdk-android-ndk/transitiveDependencies @@ -4,7 +4,7 @@ com.squareup.okhttp3:okhttp:4.12.0 : 771 Kb com.squareup.okio:okio-jvm:3.6.0 : 351 Kb org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.10 : 959 b org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10 : 965 b -org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb +org.jetbrains.kotlin:kotlin-stdlib:2.3.20 : 1762 Kb org.jetbrains:annotations:13.0 : 17 Kb Total transitive dependencies size : 2 Mb diff --git a/features/dd-sdk-android-profiling/api/compiler-meta.txt b/features/dd-sdk-android-profiling/api/compiler-meta.txt index a098d10949..f757d782cb 100644 --- a/features/dd-sdk-android-profiling/api/compiler-meta.txt +++ b/features/dd-sdk-android-profiling/api/compiler-meta.txt @@ -1,2 +1,2 @@ -kotlin_abi_version=2.0.0 -jvm_bytecode_version=11 +kotlin_abi_version=2.1.0 +jvm_bytecode_version=17 diff --git a/features/dd-sdk-android-profiling/build.gradle.kts b/features/dd-sdk-android-profiling/build.gradle.kts index 93da54b5f6..bfa2e1c34f 100644 --- a/features/dd-sdk-android-profiling/build.gradle.kts +++ b/features/dd-sdk-android-profiling/build.gradle.kts @@ -8,15 +8,15 @@ import com.datadog.gradle.utils.cloneRumEventsFormat import com.datadog.gradle.utils.createJsonModelsGenerationTask import com.datadog.gradle.utils.createRumSchemaCloneTask -import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("com.google.devtools.ksp") id("datadogBuildConfig") @@ -125,7 +125,7 @@ createJsonModelsGenerationTask("generateProfilingModelsFromJson") { } datadogBuild { - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyAndroidLibraryConfig() applyJunitConfig() applyJavadocConfig() diff --git a/features/dd-sdk-android-profiling/src/main/java/com/datadog/android/profiling/internal/ProfilingDataWriter.kt b/features/dd-sdk-android-profiling/src/main/java/com/datadog/android/profiling/internal/ProfilingDataWriter.kt index 295ab1f491..dbbc8ffaec 100644 --- a/features/dd-sdk-android-profiling/src/main/java/com/datadog/android/profiling/internal/ProfilingDataWriter.kt +++ b/features/dd-sdk-android-profiling/src/main/java/com/datadog/android/profiling/internal/ProfilingDataWriter.kt @@ -24,6 +24,7 @@ import com.datadog.android.profiling.model.ProfileEvent import com.datadog.android.profiling.model.RumMetadataEvent import com.google.gson.JsonArray import java.io.File +import java.util.Locale import java.util.concurrent.TimeUnit import kotlin.math.abs @@ -323,14 +324,14 @@ internal class ProfilingDataWriter( sdkCore.internalLogger.log( InternalLogger.Level.WARN, InternalLogger.Target.MAINTAINER, - { LOG_FILE_DELETE_FAILED.format(path) } + { LOG_FILE_DELETE_FAILED.format(Locale.US, path) } ) } } catch (@Suppress("TooGenericExceptionCaught") t: Throwable) { sdkCore.internalLogger.log( InternalLogger.Level.WARN, InternalLogger.Target.MAINTAINER, - { LOG_FILE_DELETE_FAILED.format(path) }, + { LOG_FILE_DELETE_FAILED.format(Locale.US, path) }, t ) } diff --git a/features/dd-sdk-android-profiling/src/test/kotlin/com/datadog/android/profiling/internal/ProfilingRequestFactoryTest.kt b/features/dd-sdk-android-profiling/src/test/kotlin/com/datadog/android/profiling/internal/ProfilingRequestFactoryTest.kt index 2c9a4c7ead..e813203bd8 100644 --- a/features/dd-sdk-android-profiling/src/test/kotlin/com/datadog/android/profiling/internal/ProfilingRequestFactoryTest.kt +++ b/features/dd-sdk-android-profiling/src/test/kotlin/com/datadog/android/profiling/internal/ProfilingRequestFactoryTest.kt @@ -32,6 +32,7 @@ import org.mockito.kotlin.eq import org.mockito.kotlin.isNull import org.mockito.kotlin.verify import org.mockito.quality.Strictness +import java.util.Locale @Extensions( ExtendWith(MockitoExtension::class), @@ -212,7 +213,7 @@ class ProfilingRequestFactoryTest { eq(null) ) assertThat(firstValue()).isEqualTo( - ProfilingRequestFactory.MULTIPLE_BATCH_EVENTS_WARNING_MESSAGE.format(batchData.size) + ProfilingRequestFactory.MULTIPLE_BATCH_EVENTS_WARNING_MESSAGE.format(Locale.US, batchData.size) ) } assertThat(request.body.containsSubsequence(fakeEvent.data)).isTrue() diff --git a/features/dd-sdk-android-profiling/src/test/kotlin/com/datadog/android/profiling/internal/quota/QuotaCheckerTest.kt b/features/dd-sdk-android-profiling/src/test/kotlin/com/datadog/android/profiling/internal/quota/QuotaCheckerTest.kt index 54bde1fbd6..cc3a63343e 100644 --- a/features/dd-sdk-android-profiling/src/test/kotlin/com/datadog/android/profiling/internal/quota/QuotaCheckerTest.kt +++ b/features/dd-sdk-android-profiling/src/test/kotlin/com/datadog/android/profiling/internal/quota/QuotaCheckerTest.kt @@ -38,6 +38,7 @@ import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import org.mockito.quality.Strictness import java.io.IOException +import java.util.Locale @Extensions( ExtendWith(MockitoExtension::class), @@ -213,6 +214,7 @@ internal class QuotaCheckerTest { // Then val capturedRequest = requestCaptor.firstValue val expectedUrl = ProfilingQuotaChecker.QUOTA_URL_TEMPLATE.format( + Locale.US, fakeDatadogContext.site.intakeEndpoint.removePrefix("https://"), fakeSessionId ) @@ -430,6 +432,7 @@ internal class QuotaCheckerTest { Request.Builder() .url( ProfilingQuotaChecker.QUOTA_URL_TEMPLATE.format( + Locale.US, fakeDatadogContext.site.intakeEndpoint.removePrefix("https://"), fakeSessionId ) diff --git a/features/dd-sdk-android-profiling/src/test/kotlin/com/datadog/android/profiling/internal/telemetry/ProfilingTelemetryTest.kt b/features/dd-sdk-android-profiling/src/test/kotlin/com/datadog/android/profiling/internal/telemetry/ProfilingTelemetryTest.kt index 28fc5e614e..e4d09dceeb 100644 --- a/features/dd-sdk-android-profiling/src/test/kotlin/com/datadog/android/profiling/internal/telemetry/ProfilingTelemetryTest.kt +++ b/features/dd-sdk-android-profiling/src/test/kotlin/com/datadog/android/profiling/internal/telemetry/ProfilingTelemetryTest.kt @@ -54,6 +54,7 @@ internal class ProfilingTelemetryTest { } @Test + @Suppress fun `M dispatch SessionEnd through logMetric W report() {logger set}`( @StringForgery fakeErrorMessage: String, @LongForgery(min = 0L) fakeDuration: Long, @@ -94,11 +95,12 @@ internal class ProfilingTelemetryTest { ProfilingTelemetry.KEY_STOPPED_REASON to ProfilingTelemetry.STOPPED_REASON_ERROR, ProfilingTelemetry.KEY_APP_START_INFO to null ), - ProfilingTelemetry.KEY_PROFILING_CONFIG to mapOf( - // toInt here is needed because otherwise compiler may treat them as Long, in this case verify -> eq - // below may fail since 5120 != 5120L - ProfilingTelemetry.KEY_BUFFER_SIZE to 5120.toInt(), - ProfilingTelemetry.KEY_SAMPLING_FREQUENCY to 201.toInt(), + // the value type is pinned to Any? so that the literals below stay Int: letting it be + // inferred widens them to Long to match the version code, and the verify -> eq further down + // then fails since 5120L != 5120 + ProfilingTelemetry.KEY_PROFILING_CONFIG to mapOf( + ProfilingTelemetry.KEY_BUFFER_SIZE to 5120, + ProfilingTelemetry.KEY_SAMPLING_FREQUENCY to 201, ProfilingTelemetry.KEY_PROFILING_PACKAGE_VERSION_CODE to fakeProfilingPackageVersionCode ) ) diff --git a/features/dd-sdk-android-profiling/transitiveDependencies b/features/dd-sdk-android-profiling/transitiveDependencies index da1cd9057e..0a59f4773e 100644 --- a/features/dd-sdk-android-profiling/transitiveDependencies +++ b/features/dd-sdk-android-profiling/transitiveDependencies @@ -14,7 +14,7 @@ com.squareup.okhttp3:okhttp:4.12.0 : 771 Kb com.squareup.okio:okio-jvm:3.6.0 : 351 Kb org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.10 : 959 b org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10 : 965 b -org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb +org.jetbrains.kotlin:kotlin-stdlib:2.3.20 : 1762 Kb org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4 : 19 Kb org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.6.4 : 1442 Kb org.jetbrains:annotations:13.0 : 17 Kb diff --git a/features/dd-sdk-android-rum-debug-widget/api/compiler-meta.txt b/features/dd-sdk-android-rum-debug-widget/api/compiler-meta.txt index a098d10949..f757d782cb 100644 --- a/features/dd-sdk-android-rum-debug-widget/api/compiler-meta.txt +++ b/features/dd-sdk-android-rum-debug-widget/api/compiler-meta.txt @@ -1,2 +1,2 @@ -kotlin_abi_version=2.0.0 -jvm_bytecode_version=11 +kotlin_abi_version=2.1.0 +jvm_bytecode_version=17 diff --git a/features/dd-sdk-android-rum-debug-widget/build.gradle.kts b/features/dd-sdk-android-rum-debug-widget/build.gradle.kts index b9d017d0c8..0f670c3c4f 100644 --- a/features/dd-sdk-android-rum-debug-widget/build.gradle.kts +++ b/features/dd-sdk-android-rum-debug-widget/build.gradle.kts @@ -4,15 +4,14 @@ * Copyright 2016-Present Datadog, Inc. */ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("datadogBuildConfig") // Publish @@ -69,7 +68,7 @@ unMock { } datadogBuild { - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyAndroidLibraryConfig() applyJunitConfig() applyJavadocConfig() diff --git a/features/dd-sdk-android-rum-debug-widget/transitiveDependencies b/features/dd-sdk-android-rum-debug-widget/transitiveDependencies index 61131d6303..8d99fef08b 100644 --- a/features/dd-sdk-android-rum-debug-widget/transitiveDependencies +++ b/features/dd-sdk-android-rum-debug-widget/transitiveDependencies @@ -22,7 +22,7 @@ androidx.versionedparcelable:versionedparcelable:1.1.1 : 30 Kb androidx.viewpager:viewpager:1.0.0 : 52 Kb org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.0 : 963 b org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.0 : 968 b -org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb +org.jetbrains.kotlin:kotlin-stdlib:2.3.20 : 1762 Kb org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4 : 19 Kb org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.6.4 : 1442 Kb org.jetbrains:annotations:13.0 : 17 Kb diff --git a/features/dd-sdk-android-rum/api/compiler-meta.txt b/features/dd-sdk-android-rum/api/compiler-meta.txt index a098d10949..f757d782cb 100644 --- a/features/dd-sdk-android-rum/api/compiler-meta.txt +++ b/features/dd-sdk-android-rum/api/compiler-meta.txt @@ -1,2 +1,2 @@ -kotlin_abi_version=2.0.0 -jvm_bytecode_version=11 +kotlin_abi_version=2.1.0 +jvm_bytecode_version=17 diff --git a/features/dd-sdk-android-rum/build.gradle.kts b/features/dd-sdk-android-rum/build.gradle.kts index 7e3ece0a6e..4f8ebaddb4 100644 --- a/features/dd-sdk-android-rum/build.gradle.kts +++ b/features/dd-sdk-android-rum/build.gradle.kts @@ -8,16 +8,16 @@ import com.datadog.gradle.utils.cloneRumEventsFormat import com.datadog.gradle.utils.createJsonModelsGenerationTask import com.datadog.gradle.utils.createRumSchemaCloneTask -import org.jetbrains.kotlin.gradle.dsl.JvmTarget import java.nio.file.Paths plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("com.google.devtools.ksp") id("datadogBuildConfig") @@ -93,7 +93,12 @@ dependencies { testImplementation(testFixtures(project(":features:dd-sdk-android-trace"))) unmock(libs.robolectric) - // Test Fixtures + // Test Fixtures — declared explicitly because the testFixtures source set is its own variant + // and does not inherit the main `implementation` classpath (nor the auto-added kotlin-stdlib). + testFixturesImplementation(libs.kotlin) + testFixturesImplementation(libs.bundles.jUnit5) + testFixturesImplementation(libs.okHttp) + testFixturesImplementation(libs.bundles.testTools) testFixturesImplementation(testFixtures(project(":dd-sdk-android-core"))) testFixturesImplementation(testFixtures(project(":dd-sdk-android-internal"))) testFixturesImplementation(project(":tools:unit")) { @@ -104,10 +109,6 @@ dependencies { ) } } - testFixturesImplementation(libs.kotlin) - testFixturesImplementation(libs.bundles.jUnit5) - testFixturesImplementation(libs.okHttp) - testFixturesImplementation(libs.bundles.testTools) } unMock { @@ -188,7 +189,7 @@ createJsonModelsGenerationTask("generateTelemetryModelsFromJson") { } datadogBuild { - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyAndroidLibraryConfig() applyJunitConfig() applyJavadocConfig() diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/GlobalRumMonitor.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/GlobalRumMonitor.kt index 226c2e1c91..1fba8bc0af 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/GlobalRumMonitor.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/GlobalRumMonitor.kt @@ -118,7 +118,7 @@ object GlobalRumMonitor { } // This method is mainly for test purposes. - @Suppress("unused") + @Suppress("unused", "UnusedPrivateFunction") @JvmStatic private fun reset() { clear() diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/RumMonitor.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/RumMonitor.kt index bf08174e62..175bd12dfa 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/RumMonitor.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/RumMonitor.kt @@ -456,7 +456,7 @@ interface RumMonitor { * * @see _RumInternalProxy */ - @Suppress("FunctionName") + @Suppress("FunctionName", "FunctionNaming") @JvmSynthetic fun _getInternal(): _RumInternalProxy? } diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/accessibility/DefaultAccessibilityReader.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/accessibility/DefaultAccessibilityReader.kt index 4f7e85cfc2..71fa5c0d1c 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/accessibility/DefaultAccessibilityReader.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/accessibility/DefaultAccessibilityReader.kt @@ -38,7 +38,7 @@ internal class DefaultAccessibilityReader( applicationContext.getSystemServiceAs(Context.ACCESSIBILITY_SERVICE), private val secureWrapper: SecureWrapper = SecureWrapper(), private val globalWrapper: GlobalWrapper = GlobalWrapper(), - private val handler: Handler = Handler(Looper.getMainLooper()) + handler: Handler = Handler(Looper.getMainLooper()) ) : InfoProvider, ComponentCallbacks { private val displayInversionListener = object : ContentObserver(handler) { @@ -86,6 +86,7 @@ internal class DefaultAccessibilityReader( } @Deprecated("Deprecated in Java") + @Suppress("OVERRIDE_DEPRECATION") override fun onLowMemory() { // do nothing - there's nothing we're holding onto that takes up any significant memory } diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/display/DefaultDisplayInfoProvider.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/display/DefaultDisplayInfoProvider.kt index 76c5c31b7f..64c23abbb6 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/display/DefaultDisplayInfoProvider.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/display/DefaultDisplayInfoProvider.kt @@ -25,7 +25,7 @@ internal class DefaultDisplayInfoProvider( internalLogger = internalLogger ), private val contentResolver: ContentResolver = applicationContext.contentResolver, - private val handler: Handler = Handler(Looper.getMainLooper()) + handler: Handler = Handler(Looper.getMainLooper()) ) : InfoProvider { @Volatile diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumActionScope.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumActionScope.kt index d71d183db6..c2442f638b 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumActionScope.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumActionScope.kt @@ -273,7 +273,7 @@ internal class RumActionScope( frustrations.add(ActionEvent.Type.ERROR_TAP) } - sdkCore.newRumEventWriteOperation(datadogContext, writeScope, writer) { + sdkCore.newRumEventWriteOperation(writeScope, writer) { val user = datadogContext.userInfo val hasReplay = featuresContextResolver.resolveViewHasReplay( datadogContext, diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumResourceScope.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumResourceScope.kt index d7e968f25a..beac1f1e93 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumResourceScope.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumResourceScope.kt @@ -287,7 +287,7 @@ internal class RumResourceScope( ResourceEvent.Response(headers = ResourceEvent.RequestHeaders(it.toMutableMap())) } - sdkCore.newRumEventWriteOperation(datadogContext, writeScope, writer) { + sdkCore.newRumEventWriteOperation(writeScope, writer) { val user = datadogContext.userInfo val hasReplay = featuresContextResolver.resolveViewHasReplay( datadogContext, @@ -458,7 +458,7 @@ internal class RumResourceScope( else -> ErrorEvent.ErrorEventSessionType.SYNTHETICS } - sdkCore.newRumEventWriteOperation(datadogContext, writeScope, writer) { + sdkCore.newRumEventWriteOperation(writeScope, writer) { val user = datadogContext.userInfo val hasReplay = featuresContextResolver.resolveViewHasReplay( datadogContext, diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScope.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScope.kt index 611dc3a859..f9871d6df6 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScope.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScope.kt @@ -261,7 +261,7 @@ internal class RumSessionScope( return !isActive && childScope == null } - @Suppress("ComplexMethod") + @Suppress("ComplexMethod", "CyclomaticComplexMethod") private fun updateSession(event: RumRawEvent) { val nanoTime = sdkCore.timeProvider.getDeviceElapsedTimeNanos() val isNewSession = sessionId == RumContext.NULL_UUID diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScope.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScope.kt index fd2501af33..43d17525cc 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScope.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScope.kt @@ -273,7 +273,7 @@ internal open class RumViewScope( val vitalId = UUID.randomUUID().toString() val capturedRumContext = getRumContext() - sdkCore.newRumEventWriteOperation(datadogContext, writeScope, writer) { + sdkCore.newRumEventWriteOperation(writeScope, writer) { newVitalEvent( vitalId, event, @@ -306,7 +306,7 @@ internal open class RumViewScope( val vitalId = UUID.randomUUID().toString() val capturedRumContext = getRumContext() - sdkCore.newRumEventWriteOperation(datadogContext, writeScope, writer) { + sdkCore.newRumEventWriteOperation(writeScope, writer) { newVitalEvent( vitalId, event, @@ -728,7 +728,7 @@ internal open class RumViewScope( pendingResourceCount++ } - @Suppress("ComplexMethod", "LongMethod") + @Suppress("ComplexMethod", "CyclomaticComplexMethod", "LongMethod") @WorkerThread private fun onAddError( event: RumRawEvent.AddError, @@ -777,7 +777,7 @@ internal open class RumViewScope( } // end region - sdkCore.newRumEventWriteOperation(datadogContext, writeScope, writer, eventType) { + sdkCore.newRumEventWriteOperation(writeScope, writer, eventType) { val user = datadogContext.userInfo val syntheticsAttribute = if ( rumContext.syntheticsTestId.isNullOrBlank() || @@ -1216,7 +1216,7 @@ internal open class RumViewScope( } } - @Suppress("LongMethod", "ComplexMethod") + @Suppress("LongMethod", "ComplexMethod", "CyclomaticComplexMethod") internal fun sendViewUpdate( event: RumRawEvent, datadogContext: DatadogContext, @@ -1326,7 +1326,7 @@ internal open class RumViewScope( currentViewId ) - sdkCore.newRumEventWriteOperation(datadogContext, writeScope, writer, eventType) { + sdkCore.newRumEventWriteOperation(writeScope, writer, eventType) { val user = datadogContext.userInfo val replayStats = ViewEvent.ReplayStats(recordsCount = sessionReplayRecordsCount) val syntheticsAttribute = if ( @@ -1547,7 +1547,7 @@ internal open class RumViewScope( ) val longTaskId = UUID.randomUUID().toString() - sdkCore.newRumEventWriteOperation(datadogContext, writeScope, writer) { + sdkCore.newRumEventWriteOperation(writeScope, writer) { val user = datadogContext.userInfo val syntheticsAttribute = if ( rumContext.syntheticsTestId.isNullOrBlank() || diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/monitor/DatadogRumMonitor.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/monitor/DatadogRumMonitor.kt index 0406ce5173..9fe1d25ed4 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/monitor/DatadogRumMonitor.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/monitor/DatadogRumMonitor.kt @@ -108,7 +108,7 @@ internal class DatadogRumMonitor( accessibilitySnapshotManager: AccessibilitySnapshotManager, batteryInfoProvider: InfoProvider, displayInfoProvider: InfoProvider, - private val rumSessionScopeStartupManagerFactory: () -> RumSessionScopeStartupManager, + rumSessionScopeStartupManagerFactory: () -> RumSessionScopeStartupManager, insightsCollector: InsightsCollector, heatmapIdentifierRegistry: HeatmapIdentifierRegistry?, timeseriesCollectorFactory: TimeseriesCollector.Factory @@ -1023,7 +1023,7 @@ internal class DatadogRumMonitor( * Wait for any pending events. This is mostly for integration tests to ensure that the * RUM context is in the correct state before proceeding. */ - @Suppress("unused") + @Suppress("unused", "UnusedPrivateFunction") private fun waitForPendingEvents() { if (!executorService.isShutdown) { @Suppress("UnsafeThirdPartyFunctionCall") // 1 cannot be negative diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/startup/RumSessionScopeStartupManager.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/startup/RumSessionScopeStartupManager.kt index 83fe325c73..7e3c293625 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/startup/RumSessionScopeStartupManager.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/startup/RumSessionScopeStartupManager.kt @@ -155,7 +155,6 @@ internal class RumSessionScopeStartupManagerImpl( ) sendTTIDEvent( - datadogContext = datadogContext, writeScope = writeScope, writer = writer, ttidEvent = ttidEvent, @@ -297,13 +296,12 @@ internal class RumSessionScopeStartupManagerImpl( ) ) - sdkCore.newRumEventWriteOperation(datadogContext, writeScope, writer) { + sdkCore.newRumEventWriteOperation(writeScope, writer) { ttfdEvent }.submit() } private fun sendTTIDEvent( - datadogContext: DatadogContext, writeScope: EventWriteScope, writer: DataWriter, ttidEvent: VitalAppLaunchEvent, @@ -325,7 +323,7 @@ internal class RumSessionScopeStartupManagerImpl( return } - sdkCore.newRumEventWriteOperation(datadogContext, writeScope, writer) { + sdkCore.newRumEventWriteOperation(writeScope, writer) { ttidEvent }.submit() } diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/utils/SdkCoreExt.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/utils/SdkCoreExt.kt index ee301b0097..471a9c5f36 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/utils/SdkCoreExt.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/utils/SdkCoreExt.kt @@ -7,7 +7,6 @@ package com.datadog.android.rum.internal.utils import com.datadog.android.api.InternalLogger -import com.datadog.android.api.context.DatadogContext import com.datadog.android.api.feature.EventWriteScope import com.datadog.android.api.feature.FeatureSdkCore import com.datadog.android.api.storage.DataWriter @@ -20,7 +19,6 @@ internal typealias EventOutcomeAction = (rumMonitor: AdvancedRumMonitor) -> Unit internal class WriteOperation( private val sdkCore: FeatureSdkCore, - private val datadogContext: DatadogContext, private val writeScope: EventWriteScope, private val rumDataWriter: DataWriter, private val eventType: EventType, @@ -108,11 +106,10 @@ internal class WriteOperation( } internal fun FeatureSdkCore.newRumEventWriteOperation( - datadogContext: DatadogContext, writeScope: EventWriteScope, rumDataWriter: DataWriter, eventType: EventType = EventType.DEFAULT, eventSource: () -> Any ): WriteOperation { - return WriteOperation(this, datadogContext, writeScope, rumDataWriter, eventType, eventSource) + return WriteOperation(this, writeScope, rumDataWriter, eventType, eventSource) } diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/event/RumEventSerializerTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/event/RumEventSerializerTest.kt index ccfce1a4ec..ac7a2bc2ca 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/event/RumEventSerializerTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/event/RumEventSerializerTest.kt @@ -867,25 +867,25 @@ internal class RumEventSerializerTest { hasField("configuration") { val configuration = event.telemetry.configuration if (configuration.sessionSampleRate != null) { - hasField("session_sample_rate", configuration.sessionSampleRate!!) + hasField("session_sample_rate", configuration.sessionSampleRate) } if (configuration.telemetrySampleRate != null) { - hasField("telemetry_sample_rate", configuration.telemetrySampleRate!!) + hasField("telemetry_sample_rate", configuration.telemetrySampleRate) } if (configuration.telemetryConfigurationSampleRate != null) { hasField( "telemetry_configuration_sample_rate", - configuration.telemetryConfigurationSampleRate!! + configuration.telemetryConfigurationSampleRate ) } if (configuration.traceSampleRate != null) { - hasField("trace_sample_rate", configuration.traceSampleRate!!) + hasField("trace_sample_rate", configuration.traceSampleRate) } if (configuration.premiumSampleRate != null) { - hasField("premium_sample_rate", configuration.premiumSampleRate!!) + hasField("premium_sample_rate", configuration.premiumSampleRate) } if (configuration.replaySampleRate != null) { - hasField("replay_sample_rate", configuration.replaySampleRate!!) + hasField("replay_sample_rate", configuration.replaySampleRate) } if (configuration.sessionReplaySampleRate != null) { hasField( @@ -897,36 +897,36 @@ internal class RumEventSerializerTest { hasField("use_proxy", configuration.useProxy!!) } if (configuration.useBeforeSend != null) { - hasField("use_before_send", configuration.useBeforeSend!!) + hasField("use_before_send", configuration.useBeforeSend) } if (configuration.silentMultipleInit != null) { - hasField("silent_multiple_init", configuration.silentMultipleInit!!) + hasField("silent_multiple_init", configuration.silentMultipleInit) } if (configuration.trackSessionAcrossSubdomains != null) { hasField( "track_session_across_subdomains", - configuration.trackSessionAcrossSubdomains!! + configuration.trackSessionAcrossSubdomains ) } if (configuration.useCrossSiteSessionCookie != null) { hasField( "use_cross_site_session_cookie", - configuration.useCrossSiteSessionCookie!! + configuration.useCrossSiteSessionCookie ) } if (configuration.useSecureSessionCookie != null) { hasField( "use_secure_session_cookie", - configuration.useSecureSessionCookie!! + configuration.useSecureSessionCookie ) } if (configuration.actionNameAttribute != null) { - hasField("action_name_attribute", configuration.actionNameAttribute!!) + hasField("action_name_attribute", configuration.actionNameAttribute) } if (configuration.useAllowedTracingOrigins != null) { hasField( "use_allowed_tracing_origins", - configuration.useAllowedTracingOrigins!! + configuration.useAllowedTracingOrigins ) } if (configuration.defaultPrivacyLevel != null) { @@ -935,7 +935,7 @@ internal class RumEventSerializerTest { if (configuration.useExcludedActivityUrls != null) { hasField( "use_excluded_activity_urls", - configuration.useExcludedActivityUrls!! + configuration.useExcludedActivityUrls ) } if (configuration.trackFrustrations != null) { @@ -948,21 +948,21 @@ internal class RumEventSerializerTest { hasField("track_interactions", configuration.trackInteractions!!) } if (configuration.forwardErrorsToLogs != null) { - hasField("forward_errors_to_logs", configuration.forwardErrorsToLogs!!) + hasField("forward_errors_to_logs", configuration.forwardErrorsToLogs) } if (configuration.forwardConsoleLogs != null) { - hasField("forward_console_logs", configuration.forwardConsoleLogs!!) + hasField("forward_console_logs", configuration.forwardConsoleLogs) } if (configuration.forwardReports != null) { - hasField("forward_reports", configuration.forwardReports!!) + hasField("forward_reports", configuration.forwardReports) } if (configuration.useLocalEncryption != null) { - hasField("use_local_encryption", configuration.useLocalEncryption!!) + hasField("use_local_encryption", configuration.useLocalEncryption) } if (configuration.viewTrackingStrategy != null) { hasField( "view_tracking_strategy", - configuration.viewTrackingStrategy!!.toJson() + configuration.viewTrackingStrategy.toJson() ) } if (configuration.trackBackgroundEvents != null) { @@ -984,7 +984,7 @@ internal class RumEventSerializerTest { hasField("track_network_requests", configuration.trackNetworkRequests!!) } if (configuration.useTracing != null) { - hasField("use_tracing", configuration.useTracing!!) + hasField("use_tracing", configuration.useTracing) } if (configuration.trackNativeViews != null) { hasField("track_native_views", configuration.trackNativeViews!!) @@ -1014,13 +1014,13 @@ internal class RumEventSerializerTest { ) } if (configuration.batchSize != null) { - hasField("batch_size", configuration.batchSize!!) + hasField("batch_size", configuration.batchSize) } if (configuration.batchUploadFrequency != null) { - hasField("batch_upload_frequency", configuration.batchUploadFrequency!!) + hasField("batch_upload_frequency", configuration.batchUploadFrequency) } if (configuration.isMainProcess != null) { - hasField("is_main_process", configuration.isMainProcess!!) + hasField("is_main_process", configuration.isMainProcess) } } } @@ -1085,7 +1085,7 @@ internal class RumEventSerializerTest { hasField("usage") { when (event.telemetry.usage) { is TelemetryUsageEvent.Usage.AddViewLoadingTime -> { - val usage = event.telemetry.usage as TelemetryUsageEvent.Usage.AddViewLoadingTime + val usage = event.telemetry.usage hasField("no_view", usage.noView) hasField("no_active_view", usage.noActiveView) hasField("overwritten", usage.overwritten) @@ -1099,30 +1099,28 @@ internal class RumEventSerializerTest { if (event.telemetry.device != null) { hasField("device") { val device = event.telemetry.device - checkNotNull(device) if (device.architecture != null) { - hasField("architecture", device.architecture!!) + hasField("architecture", device.architecture) } if (device.brand != null) { - hasField("brand", device.brand!!) + hasField("brand", device.brand) } if (device.model != null) { - hasField("model", device.model!!) + hasField("model", device.model) } } } if (event.telemetry.os != null) { hasField("os") { val os = event.telemetry.os - checkNotNull(os) if (os.build != null) { - hasField("build", os.build!!) + hasField("build", os.build) } if (os.name != null) { - hasField("name", os.name!!) + hasField("name", os.name) } if (os.version != null) { - hasField("version", os.version!!) + hasField("version", os.version) } } } @@ -1597,10 +1595,9 @@ internal class RumEventSerializerTest { } val faultyEvent = event.copy( usr = event.usr?.copy( - additionalProperties = event.usr?.additionalProperties - ?.toMutableMap() - ?.apply { put(faultyKey, faultyObject) } - .orEmpty() + additionalProperties = event.usr.additionalProperties + .toMutableMap() + .apply { put(faultyKey, faultyObject) } .toMutableMap() ) ) @@ -1634,10 +1631,9 @@ internal class RumEventSerializerTest { } val faultyEvent = event.copy( account = event.account?.copy( - additionalProperties = event.account?.additionalProperties - ?.toMutableMap() - ?.apply { put(faultyKey, faultyObject) } - .orEmpty() + additionalProperties = event.account.additionalProperties + .toMutableMap() + .apply { put(faultyKey, faultyObject) } .toMutableMap() ) ) @@ -1670,10 +1666,9 @@ internal class RumEventSerializerTest { } val faultyEvent = event.copy( context = event.context?.copy( - additionalProperties = event.context?.additionalProperties - ?.toMutableMap() - ?.apply { put(faultyKey, faultyObject) } - .orEmpty() + additionalProperties = event.context.additionalProperties + .toMutableMap() + .apply { put(faultyKey, faultyObject) } .toMutableMap() ) ) @@ -1704,10 +1699,9 @@ internal class RumEventSerializerTest { } val faultyEvent = event.copy( usr = event.usr?.copy( - additionalProperties = event.usr?.additionalProperties - ?.toMutableMap() - ?.apply { put(faultyKey, faultyObject) } - .orEmpty() + additionalProperties = event.usr.additionalProperties + .toMutableMap() + .apply { put(faultyKey, faultyObject) } .toMutableMap() ) ) @@ -1741,10 +1735,9 @@ internal class RumEventSerializerTest { } val faultyEvent = event.copy( account = event.account?.copy( - additionalProperties = event.account?.additionalProperties - ?.toMutableMap() - ?.apply { put(faultyKey, faultyObject) } - .orEmpty() + additionalProperties = event.account.additionalProperties + .toMutableMap() + .apply { put(faultyKey, faultyObject) } .toMutableMap() ) ) @@ -1777,10 +1770,9 @@ internal class RumEventSerializerTest { } val faultyEvent = event.copy( context = event.context?.copy( - additionalProperties = event.context?.additionalProperties - ?.toMutableMap() - ?.apply { put(faultyKey, faultyObject) } - .orEmpty() + additionalProperties = event.context.additionalProperties + .toMutableMap() + .apply { put(faultyKey, faultyObject) } .toMutableMap() ) ) @@ -1811,10 +1803,9 @@ internal class RumEventSerializerTest { } val faultyEvent = event.copy( usr = event.usr?.copy( - additionalProperties = event.usr?.additionalProperties - ?.toMutableMap() - ?.apply { put(faultyKey, faultyObject) } - .orEmpty() + additionalProperties = event.usr.additionalProperties + .toMutableMap() + .apply { put(faultyKey, faultyObject) } .toMutableMap() ) ) @@ -1848,10 +1839,9 @@ internal class RumEventSerializerTest { } val faultyEvent = event.copy( account = event.account?.copy( - additionalProperties = event.account?.additionalProperties - ?.toMutableMap() - ?.apply { put(faultyKey, faultyObject) } - .orEmpty() + additionalProperties = event.account.additionalProperties + .toMutableMap() + .apply { put(faultyKey, faultyObject) } .toMutableMap() ) ) @@ -1884,10 +1874,9 @@ internal class RumEventSerializerTest { } val faultyEvent = event.copy( context = event.context?.copy( - additionalProperties = event.context?.additionalProperties - ?.toMutableMap() - ?.apply { put(faultyKey, faultyObject) } - .orEmpty() + additionalProperties = event.context.additionalProperties + .toMutableMap() + .apply { put(faultyKey, faultyObject) } .toMutableMap() ) ) @@ -1918,10 +1907,9 @@ internal class RumEventSerializerTest { } val faultyEvent = event.copy( usr = event.usr?.copy( - additionalProperties = event.usr?.additionalProperties - ?.toMutableMap() - ?.apply { put(faultyKey, faultyObject) } - .orEmpty() + additionalProperties = event.usr.additionalProperties + .toMutableMap() + .apply { put(faultyKey, faultyObject) } .toMutableMap() ) ) @@ -1955,10 +1943,9 @@ internal class RumEventSerializerTest { } val faultyEvent = event.copy( account = event.account?.copy( - additionalProperties = event.account?.additionalProperties - ?.toMutableMap() - ?.apply { put(faultyKey, faultyObject) } - .orEmpty() + additionalProperties = event.account.additionalProperties + .toMutableMap() + .apply { put(faultyKey, faultyObject) } .toMutableMap() ) ) @@ -1991,10 +1978,9 @@ internal class RumEventSerializerTest { } val faultyEvent = event.copy( context = event.context?.copy( - additionalProperties = event.context?.additionalProperties - ?.toMutableMap() - ?.apply { put(faultyKey, faultyObject) } - .orEmpty() + additionalProperties = event.context.additionalProperties + .toMutableMap() + .apply { put(faultyKey, faultyObject) } .toMutableMap() ) ) @@ -2025,10 +2011,9 @@ internal class RumEventSerializerTest { } val faultyEvent = event.copy( usr = event.usr?.copy( - additionalProperties = event.usr?.additionalProperties - ?.toMutableMap() - ?.apply { put(faultyKey, faultyObject) } - .orEmpty() + additionalProperties = event.usr.additionalProperties + .toMutableMap() + .apply { put(faultyKey, faultyObject) } .toMutableMap() ) ) @@ -2062,10 +2047,9 @@ internal class RumEventSerializerTest { } val faultyEvent = event.copy( account = event.account?.copy( - additionalProperties = event.account?.additionalProperties - ?.toMutableMap() - ?.apply { put(faultyKey, faultyObject) } - .orEmpty() + additionalProperties = event.account.additionalProperties + .toMutableMap() + .apply { put(faultyKey, faultyObject) } .toMutableMap() ) ) @@ -2098,10 +2082,9 @@ internal class RumEventSerializerTest { } val faultyEvent = event.copy( context = event.context?.copy( - additionalProperties = event.context?.additionalProperties - ?.toMutableMap() - ?.apply { put(faultyKey, faultyObject) } - .orEmpty() + additionalProperties = event.context.additionalProperties + .toMutableMap() + .apply { put(faultyKey, faultyObject) } .toMutableMap() ) ) @@ -2132,10 +2115,9 @@ internal class RumEventSerializerTest { } val faultyEvent = event.copy( usr = event.usr?.copy( - additionalProperties = event.usr?.additionalProperties - ?.toMutableMap() - ?.apply { put(faultyKey, faultyObject) } - .orEmpty() + additionalProperties = event.usr.additionalProperties + .toMutableMap() + .apply { put(faultyKey, faultyObject) } .toMutableMap() ) ) @@ -2169,10 +2151,9 @@ internal class RumEventSerializerTest { } val faultyEvent = event.copy( context = event.context?.copy( - additionalProperties = event.context?.additionalProperties - ?.toMutableMap() - ?.apply { put(faultyKey, faultyObject) } - .orEmpty() + additionalProperties = event.context.additionalProperties + .toMutableMap() + .apply { put(faultyKey, faultyObject) } .toMutableMap() ) ) diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumActionScopeAttributePropagationTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumActionScopeAttributePropagationTest.kt index b514c28054..1bd334ae23 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumActionScopeAttributePropagationTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumActionScopeAttributePropagationTest.kt @@ -228,9 +228,7 @@ internal class RumActionScopeAttributePropagationTest { // region Internal private fun mockEvent(timeOffset: Long = 0L): RumRawEvent { - val event: RumRawEvent = mock() - whenever(event.eventTime) doReturn timeWithOffset(timeOffset) - return event + return RumRawEvent.WebViewEvent(eventTime = timeWithOffset(timeOffset)) } private fun timeWithOffset(offsetMs: Long): Time { diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumActionScopeTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumActionScopeTest.kt index 3d5a59f2a3..db11332f80 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumActionScopeTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumActionScopeTest.kt @@ -3615,9 +3615,7 @@ internal class RumActionScopeTest { // region Internal private fun mockEvent(timeOffset: Long = 0L): RumRawEvent { - val event: RumRawEvent = mock() - whenever(event.eventTime) doReturn timeWithOffset(timeOffset) - return event + return RumRawEvent.WebViewEvent(eventTime = timeWithOffset(timeOffset)) } private fun timeWithOffset(offsetMs: Long): Time { diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScopeTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScopeTest.kt index b5a0faae3d..1b58b55bda 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScopeTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScopeTest.kt @@ -79,7 +79,6 @@ internal class RumApplicationScopeTest { @Mock lateinit var mockChildScope: RumSessionScope - @Mock lateinit var mockEvent: RumRawEvent @Mock @@ -173,6 +172,7 @@ internal class RumApplicationScopeTest { @BeforeEach fun `set up`(forge: Forge) { + mockEvent = mock() whenever(mockSdkCore.getFeature(Feature.RUM_FEATURE_NAME)) doReturn mockRumFeatureScope whenever(mockSdkCore.time) doReturn fakeTimeInfoAtScopeStart whenever(mockSdkCore.internalLogger) doReturn mockInternalLogger diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumContinuousActionScopeTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumContinuousActionScopeTest.kt index 39e11a0c76..432159abb8 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumContinuousActionScopeTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumContinuousActionScopeTest.kt @@ -55,7 +55,6 @@ import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.doAnswer import org.mockito.kotlin.doReturn import org.mockito.kotlin.eq -import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoInteractions @@ -3083,9 +3082,7 @@ internal class RumContinuousActionScopeTest { } private fun mockEvent(timeOffset: Long = 0L): RumRawEvent { - val event: RumRawEvent = mock() - whenever(event.eventTime) doReturn timeWithOffset(timeOffset) - return event + return RumRawEvent.WebViewEvent(eventTime = timeWithOffset(timeOffset)) } // endregion diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumResourceScopeTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumResourceScopeTest.kt index 1db01ccbe4..be170274f6 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumResourceScopeTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumResourceScopeTest.kt @@ -99,7 +99,6 @@ internal class RumResourceScopeTest { @Mock lateinit var mockParentScope: RumScope - @Mock lateinit var mockEvent: RumRawEvent @Mock @@ -3888,7 +3887,7 @@ internal class RumResourceScopeTest { // region Internal private fun mockEvent(): RumRawEvent { - val event: RumRawEvent = mock() + val event: RumRawEvent = mock() whenever(event.eventTime) doReturn fakeEventTime return event } diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScopeTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScopeTest.kt index f376b60801..1ffa2ea0d4 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScopeTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScopeTest.kt @@ -292,7 +292,7 @@ internal class RumSessionScopeTest { fun `M delegate events to child scope W handleViewEvent() {NOT TRACKED}`() { // Given testedScope.sessionState = RumSessionScope.State.NOT_TRACKED - val mockEvent: RumRawEvent = mock() + val mockEvent: RumRawEvent = mock() // When val result = testedScope.handleEvent(mockEvent, fakeDatadogContext, mockEventWriteScope, mockWriter) @@ -311,7 +311,7 @@ internal class RumSessionScopeTest { fun `M delegate events to child scope W handleViewEvent() {EXPIRED}`() { // Given testedScope.sessionState = RumSessionScope.State.EXPIRED - val mockEvent = mock() + val mockEvent = RumRawEvent.WebViewEvent(eventTime = currentFakeTime()) // When val result = testedScope.handleEvent(mockEvent, fakeDatadogContext, mockEventWriteScope, mockWriter) @@ -409,7 +409,7 @@ internal class RumSessionScopeTest { fun `M return null from handleEvent W stopped { completed child scopes }`() { // Given val stopEvent = RumRawEvent.StopSession(eventTime = currentFakeTime()) - val fakeEvent: RumRawEvent = mock() + val fakeEvent: RumRawEvent = RumRawEvent.WebViewEvent(eventTime = currentFakeTime()) whenever( mockChildScope.handleEvent( eq(stopEvent), @@ -649,7 +649,12 @@ internal class RumSessionScopeTest { val initialContext = testedScope.getRumContext() // When - val result = testedScope.handleEvent(mock(), fakeDatadogContext, mockEventWriteScope, mockWriter) + val result = testedScope.handleEvent( + mock(), + fakeDatadogContext, + mockEventWriteScope, + mockWriter + ) val context = testedScope.getRumContext() // Then @@ -710,7 +715,12 @@ internal class RumSessionScopeTest { // When advanceTimeByMs(TEST_INACTIVITY_MS) - val result = testedScope.handleEvent(mock(), fakeDatadogContext, mockEventWriteScope, mockWriter) + val result = testedScope.handleEvent( + mock(), + fakeDatadogContext, + mockEventWriteScope, + mockWriter + ) val context = testedScope.getRumContext() // Then @@ -731,7 +741,12 @@ internal class RumSessionScopeTest { // When advanceTimeByMs(TEST_INACTIVITY_MS) - val result = testedScope.handleEvent(mock(), fakeDatadogContext, mockEventWriteScope, mockWriter) + val result = testedScope.handleEvent( + mock(), + fakeDatadogContext, + mockEventWriteScope, + mockWriter + ) val context = testedScope.getRumContext() // Then @@ -902,7 +917,12 @@ internal class RumSessionScopeTest { // When advanceTimeByMs(TEST_SLEEP_MS) - val result = testedScope.handleEvent(mock(), fakeDatadogContext, mockEventWriteScope, mockWriter) + val result = testedScope.handleEvent( + mock(), + fakeDatadogContext, + mockEventWriteScope, + mockWriter + ) val context = testedScope.getRumContext() // Then @@ -1979,7 +1999,15 @@ internal class RumSessionScopeTest { // When advanceTimeByMs(TEST_INACTIVITY_MS) - testedScope.handleEvent(mock(), fakeDatadogContext, mockEventWriteScope, mockWriter) + // A concrete subclass, not the sealed base: at jvmTarget 17 Kotlin emits RumRawEvent as a + // real JVM sealed class, which Mockito cannot mock. WebViewEvent does not renew the + // session, so the scope still expires, and eventTime is only read on renewal paths. + testedScope.handleEvent( + mock(), + fakeDatadogContext, + mockEventWriteScope, + mockWriter + ) // Then assertThat(testedScope.sessionState).isEqualTo(RumSessionScope.State.EXPIRED) diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewManagerScopeTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewManagerScopeTest.kt index 5b4797da66..8bc12572dc 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewManagerScopeTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewManagerScopeTest.kt @@ -211,7 +211,7 @@ internal class RumViewManagerScopeTest { @Test fun `M delegate to child scope W handleEvent()`() { // Given - val fakeEvent: RumRawEvent = mock() + val fakeEvent: RumRawEvent = RumRawEvent.WebViewEvent(eventTime = fakeEventTime) testedScope.childrenScopes.add(mockChildScope) // When @@ -226,7 +226,7 @@ internal class RumViewManagerScopeTest { @Test fun `M keep children scope W handleEvent child returns non null`() { // Given - val fakeEvent: RumRawEvent = mock() + val fakeEvent: RumRawEvent = RumRawEvent.WebViewEvent(eventTime = fakeEventTime) testedScope.childrenScopes.add(mockChildScope) whenever( mockChildScope.handleEvent( @@ -249,7 +249,7 @@ internal class RumViewManagerScopeTest { @Test fun `M remove children scope W handleEvent child returns null`() { // Given - val fakeEvent: RumRawEvent = mock() + val fakeEvent: RumRawEvent = RumRawEvent.WebViewEvent(eventTime = fakeEventTime) testedScope.childrenScopes.add(mockChildScope) whenever( mockChildScope.handleEvent( @@ -876,7 +876,7 @@ internal class RumViewManagerScopeTest { testedScope.applicationDisplayed = true testedScope.childrenScopes.add(mockChildScope) val stopEvent = RumRawEvent.StopSession(eventTime = fakeEventTime) - val fakeEvent: RumRawEvent = mock() + val fakeEvent: RumRawEvent = RumRawEvent.WebViewEvent(eventTime = fakeEventTime) whenever( mockChildScope.handleEvent( stopEvent, diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeTest.kt index 3e4d890538..efdaf9fb26 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeTest.kt @@ -2719,7 +2719,7 @@ internal class RumViewScopeTest { fun `M returns null W handleEvent(any) on stopped view {no pending event}`() { // Given testedScope.stopped = true - fakeEvent = mock() + fakeEvent = mock() // When val result = testedScope.handleEvent(fakeEvent, fakeDatadogContext, mockEventWriteScope, mockWriter) @@ -2738,7 +2738,7 @@ internal class RumViewScopeTest { val fakeSessionReplayContext = forge.exhaustiveAttributes() .apply { put(testedScope.viewId, forge.aBool()) } testedScope.stopped = true - fakeEvent = mock() + fakeEvent = mock() // When testedScope.handleEvent(fakeEvent, fakeDatadogContext, mockEventWriteScope, mockWriter) @@ -2760,7 +2760,7 @@ internal class RumViewScopeTest { // Given testedScope.stopped = true testedScope.pendingActionCount = pendingEvents - fakeEvent = mock() + fakeEvent = mock() // When val result = testedScope.handleEvent(fakeEvent, fakeDatadogContext, mockEventWriteScope, mockWriter) @@ -2782,7 +2782,7 @@ internal class RumViewScopeTest { // Given testedScope.stopped = true testedScope.pendingResourceCount = pendingEvents - fakeEvent = mock() + fakeEvent = mock() // When val result = testedScope.handleEvent(fakeEvent, fakeDatadogContext, mockEventWriteScope, mockWriter) @@ -2804,7 +2804,7 @@ internal class RumViewScopeTest { // Given testedScope.stopped = true testedScope.pendingErrorCount = pendingEvents - fakeEvent = mock() + fakeEvent = mock() // When val result = testedScope.handleEvent(fakeEvent, fakeDatadogContext, mockEventWriteScope, mockWriter) @@ -2826,7 +2826,7 @@ internal class RumViewScopeTest { // Given testedScope.stopped = true testedScope.pendingLongTaskCount = pendingEvents - fakeEvent = mock() + fakeEvent = mock() // When val result = testedScope.handleEvent(fakeEvent, fakeDatadogContext, mockEventWriteScope, mockWriter) @@ -7042,6 +7042,7 @@ internal class RumViewScopeTest { InternalLogger.Level.DEBUG, InternalLogger.Target.USER, RumViewScope.ADDING_VIEW_LOADING_TIME_DEBUG_MESSAGE_FORMAT.format( + Locale.US, expectedViewLoadingTime, testedScope.key.name ) @@ -10810,9 +10811,7 @@ internal class RumViewScopeTest { } private fun mockEvent(): RumRawEvent { - val event: RumRawEvent = mock() - whenever(event.eventTime) doReturn fakeEventTime - return event + return RumRawEvent.WebViewEvent(eventTime = fakeEventTime) } // Builds an event time that is strictly after the scope start (fakeEventTime), so that the diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/metric/RumSessionEndedMetricTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/metric/RumSessionEndedMetricTest.kt index f4086108c0..901173e799 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/metric/RumSessionEndedMetricTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/metric/RumSessionEndedMetricTest.kt @@ -530,28 +530,28 @@ class RumSessionEndedMetricTest { assertThat(rseObject.get(SessionEndedMetric.PROCESS_TYPE_KEY)).isInstanceOf(String::class.java) assertThat(rseObject.get(SessionEndedMetric.PRECONDITION_KEY)).isInstanceOf(String::class.java) - assertThat(rseObject.get(SessionEndedMetric.DURATION_KEY)).isInstanceOf(java.lang.Long::class.java) - assertThat(rseObject.get(SessionEndedMetric.WAS_STOPPED_KEY)).isInstanceOf(java.lang.Boolean::class.java) + assertThat(rseObject.get(SessionEndedMetric.DURATION_KEY)).isInstanceOf(Long::class.javaObjectType) + assertThat(rseObject.get(SessionEndedMetric.WAS_STOPPED_KEY)).isInstanceOf(Boolean::class.javaObjectType) val viewCountsObject = rseObject.get(SessionEndedMetric.VIEW_COUNTS_KEY) as JSONObject assertThat( viewCountsObject.get(SessionEndedMetric.VIEW_COUNTS_TOTAL_KEY) - ).isInstanceOf(java.lang.Integer::class.java) + ).isInstanceOf(Int::class.javaObjectType) assertThat( viewCountsObject.get(SessionEndedMetric.VIEW_COUNTS_BG_KEY) - ).isInstanceOf(java.lang.Integer::class.java) + ).isInstanceOf(Int::class.javaObjectType) assertThat( viewCountsObject.get(SessionEndedMetric.VIEW_COUNTS_APP_LAUNCH_KEY) - ).isInstanceOf(java.lang.Integer::class.java) + ).isInstanceOf(Int::class.javaObjectType) assertThat( viewCountsObject.get(SessionEndedMetric.VIEW_COUNT_WITH_HAS_REPLAY) - ).isInstanceOf(java.lang.Integer::class.java) + ).isInstanceOf(Int::class.javaObjectType) val sdkErrorsCount = rseObject.get(SessionEndedMetric.SDK_ERRORS_COUNT_KEY) as JSONObject assertThat( sdkErrorsCount.get(SessionEndedMetric.SDK_ERRORS_COUNT_TOTAL_KEY) - ).isInstanceOf(java.lang.Integer::class.java) + ).isInstanceOf(Int::class.javaObjectType) assertThat( sdkErrorsCount.get(SessionEndedMetric.SDK_ERRORS_COUNT_BY_KIND_KEY) ).isInstanceOf(JSONObject::class.java) @@ -559,23 +559,23 @@ class RumSessionEndedMetricTest { val noViewCounts = rseObject.get(SessionEndedMetric.NO_VIEW_EVENTS_COUNT_KEY) as JSONObject assertThat( noViewCounts.get(SessionEndedMetric.NO_VIEW_EVENTS_COUNT_ACTIONS_KEY) - ).isInstanceOf(java.lang.Integer::class.java) + ).isInstanceOf(Int::class.javaObjectType) assertThat( noViewCounts.get(SessionEndedMetric.NO_VIEW_EVENTS_COUNT_RESOURCES_KEY) - ).isInstanceOf(java.lang.Integer::class.java) + ).isInstanceOf(Int::class.javaObjectType) assertThat( noViewCounts.get(SessionEndedMetric.NO_VIEW_EVENTS_COUNT_ERRORS_KEY) - ).isInstanceOf(java.lang.Integer::class.java) + ).isInstanceOf(Int::class.javaObjectType) assertThat( noViewCounts.get(SessionEndedMetric.NO_VIEW_EVENTS_COUNT_LONG_TASKS_KEY) - ).isInstanceOf(java.lang.Integer::class.java) + ).isInstanceOf(Int::class.javaObjectType) assertThat(rseObject.get(SessionEndedMetric.HAS_BACKGROUND_EVENTS_TRACKING_ENABLED_KEY)) - .isInstanceOf(java.lang.Boolean::class.java) + .isInstanceOf(Boolean::class.javaObjectType) val nptOffset = rseObject.get(SessionEndedMetric.NTP_OFFSET_KEY) as JSONObject - assertThat(nptOffset.get(SessionEndedMetric.NTP_OFFSET_AT_START_KEY)).isInstanceOf(java.lang.Long::class.java) - assertThat(nptOffset.get(SessionEndedMetric.NTP_OFFSET_AT_END_KEY)).isInstanceOf(java.lang.Long::class.java) + assertThat(nptOffset.get(SessionEndedMetric.NTP_OFFSET_AT_START_KEY)).isInstanceOf(Long::class.javaObjectType) + assertThat(nptOffset.get(SessionEndedMetric.NTP_OFFSET_AT_END_KEY)).isInstanceOf(Long::class.javaObjectType) } private fun stubSessionEndedMetric( diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/monitor/DatadogRumMonitorTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/monitor/DatadogRumMonitorTest.kt index 7909bcda4b..3a3ad71937 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/monitor/DatadogRumMonitorTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/monitor/DatadogRumMonitorTest.kt @@ -2387,7 +2387,7 @@ internal class DatadogRumMonitorTest { whenever(mockExecutorService.isShutdown).thenReturn(true) // When - testedMonitor.handleEvent(mock()) + testedMonitor.handleEvent(mock()) // Then verify(mockExecutorService, never()).execute(any()) diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/net/RumRequestFactoryTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/net/RumRequestFactoryTest.kt index f5528232ca..eafad898ef 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/net/RumRequestFactoryTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/net/RumRequestFactoryTest.kt @@ -64,15 +64,14 @@ internal class RumRequestFactoryTest { ) } - @Suppress("NAME_SHADOWING") @Test fun `M create a proper request W create()`( @Forgery batchData: List, - @StringForgery batchMetadata: String, + @StringForgery fakeBatchMetadata: String, forge: Forge ) { // Given - val batchMetadata = forge.aNullable { batchMetadata.toByteArray() } + val batchMetadata = forge.aNullable { fakeBatchMetadata.toByteArray() } // When val request = testedFactory.create(fakeDatadogContext, fakeExecutionContext, batchData, batchMetadata) @@ -108,12 +107,11 @@ internal class RumRequestFactoryTest { ) } - @Suppress("NAME_SHADOWING") @Test fun `M create a proper request W create() { custom endpoint }`( @StringForgery(regex = "https://[a-z]+\\.com(/[a-z]+)+") fakeEndpoint: String, @Forgery batchData: List, - @StringForgery batchMetadata: String, + @StringForgery fakeBatchMetadata: String, forge: Forge ) { // Given @@ -122,7 +120,7 @@ internal class RumRequestFactoryTest { viewEventFilter = mockViewEventFilter, internalLogger = InternalLogger.UNBOUND ) - val batchMetadata = forge.aNullable { batchMetadata.toByteArray() } + val batchMetadata = forge.aNullable { fakeBatchMetadata.toByteArray() } // When val request = testedFactory.create(fakeDatadogContext, fakeExecutionContext, batchData, batchMetadata) diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/timeseries/provider/CpuDatapointReaderTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/timeseries/provider/CpuDatapointReaderTest.kt index ad7b954c7f..7f93a2b116 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/timeseries/provider/CpuDatapointReaderTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/timeseries/provider/CpuDatapointReaderTest.kt @@ -38,7 +38,11 @@ import java.util.concurrent.TimeUnit @ForgeConfiguration(Configurator::class) internal class CpuDatapointReaderTest { - @LongForgery(min = 100L, max = 60_000L) + // Min is 2_500 so that intervalMs * MAX_GAP_FACTOR always covers the largest fakeElapsedMs the + // tests below forge (5_000 ms). Below that, CpuDatapointReader's "gap too large" guard + // (elapsedMs > intervalMs * MAX_GAP_FACTOR) discards the sample and read() returns null, + // which made every checkNotNull(result) test fail on unlucky seeds. + @LongForgery(min = 2_500L, max = 60_000L) var fakeIntervalMs: Long = 0L @LongForgery(min = 1_000L, max = 1_000_000L) diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/utils/SdkCoreExtTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/utils/SdkCoreExtTest.kt index 4d812b49ce..915518fdca 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/utils/SdkCoreExtTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/utils/SdkCoreExtTest.kt @@ -7,7 +7,6 @@ package com.datadog.android.rum.internal.utils import com.datadog.android.api.InternalLogger -import com.datadog.android.api.context.DatadogContext import com.datadog.android.api.feature.EventWriteScope import com.datadog.android.api.storage.DataWriter import com.datadog.android.api.storage.EventBatchWriter @@ -67,9 +66,6 @@ internal class SdkCoreExtTest { private val mockSdkCore get() = rumMonitor.mockSdkCore - @Forgery - lateinit var fakeDatadogContext: DatadogContext - @Forgery lateinit var fakeEventType: EventType @@ -89,7 +85,7 @@ internal class SdkCoreExtTest { val fakeEvent = Any() // When - mockSdkCore.newRumEventWriteOperation(fakeDatadogContext, mockEventWriteScope, mockWriter) { fakeEvent } + mockSdkCore.newRumEventWriteOperation(mockEventWriteScope, mockWriter) { fakeEvent } .submit() // Then @@ -104,7 +100,6 @@ internal class SdkCoreExtTest { // When mockSdkCore.newRumEventWriteOperation( - fakeDatadogContext, mockEventWriteScope, mockWriter, fakeEventType @@ -123,7 +118,6 @@ internal class SdkCoreExtTest { // When mockSdkCore.newRumEventWriteOperation( - fakeDatadogContext, mockEventWriteScope, mockWriter, fakeEventType @@ -147,7 +141,6 @@ internal class SdkCoreExtTest { // When mockSdkCore.newRumEventWriteOperation( - fakeDatadogContext, mockEventWriteScope, mockWriter, fakeEventType @@ -178,7 +171,6 @@ internal class SdkCoreExtTest { // When mockSdkCore.newRumEventWriteOperation( - fakeDatadogContext, mockEventWriteScope, mockWriter, fakeEventType @@ -208,7 +200,6 @@ internal class SdkCoreExtTest { // When mockSdkCore.newRumEventWriteOperation( - fakeDatadogContext, mockEventWriteScope, mockWriter, fakeEventType @@ -237,7 +228,6 @@ internal class SdkCoreExtTest { // When mockSdkCore.newRumEventWriteOperation( - fakeDatadogContext, mockEventWriteScope, mockWriter, fakeEventType @@ -262,7 +252,6 @@ internal class SdkCoreExtTest { // When mockSdkCore.newRumEventWriteOperation( - fakeDatadogContext, mockEventWriteScope, mockWriter, fakeEventType diff --git a/features/dd-sdk-android-rum/transitiveDependencies b/features/dd-sdk-android-rum/transitiveDependencies index 5d0faec409..f7c463dd47 100644 --- a/features/dd-sdk-android-rum/transitiveDependencies +++ b/features/dd-sdk-android-rum/transitiveDependencies @@ -38,7 +38,7 @@ com.squareup.okhttp3:okhttp:4.12.0 : 771 Kb com.squareup.okio:okio-jvm:3.6.0 : 351 Kb org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.10 : 959 b org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10 : 965 b -org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb +org.jetbrains.kotlin:kotlin-stdlib:2.3.20 : 1762 Kb org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4 : 19 Kb org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.6.4 : 1442 Kb org.jetbrains:annotations:13.0 : 17 Kb diff --git a/features/dd-sdk-android-session-replay-compose/api/compiler-meta.txt b/features/dd-sdk-android-session-replay-compose/api/compiler-meta.txt index a098d10949..f757d782cb 100644 --- a/features/dd-sdk-android-session-replay-compose/api/compiler-meta.txt +++ b/features/dd-sdk-android-session-replay-compose/api/compiler-meta.txt @@ -1,2 +1,2 @@ -kotlin_abi_version=2.0.0 -jvm_bytecode_version=11 +kotlin_abi_version=2.1.0 +jvm_bytecode_version=17 diff --git a/features/dd-sdk-android-session-replay-compose/build.gradle.kts b/features/dd-sdk-android-session-replay-compose/build.gradle.kts index ceb42ab2a9..f7ecff280d 100644 --- a/features/dd-sdk-android-session-replay-compose/build.gradle.kts +++ b/features/dd-sdk-android-session-replay-compose/build.gradle.kts @@ -5,16 +5,16 @@ */ import com.datadog.gradle.config.taskConfig -import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") alias(libs.plugins.composeCompilerPlugin) id("datadogBuildConfig") @@ -80,7 +80,7 @@ unMock { } datadogBuild { - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyAndroidLibraryConfig() applyJunitConfig() applyJavadocConfig() diff --git a/features/dd-sdk-android-session-replay-compose/src/test/kotlin/com/datadog/android/sessionreplay/compose/internal/mappers/semantics/ImageSemanticsNodeMapperTest.kt b/features/dd-sdk-android-session-replay-compose/src/test/kotlin/com/datadog/android/sessionreplay/compose/internal/mappers/semantics/ImageSemanticsNodeMapperTest.kt index 7a4858c6bb..7d476fffca 100644 --- a/features/dd-sdk-android-session-replay-compose/src/test/kotlin/com/datadog/android/sessionreplay/compose/internal/mappers/semantics/ImageSemanticsNodeMapperTest.kt +++ b/features/dd-sdk-android-session-replay-compose/src/test/kotlin/com/datadog/android/sessionreplay/compose/internal/mappers/semantics/ImageSemanticsNodeMapperTest.kt @@ -9,7 +9,6 @@ package com.datadog.android.sessionreplay.compose.internal.mappers.semantics import android.graphics.Bitmap import androidx.compose.ui.Alignment import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.semantics.SemanticsNode import com.datadog.android.api.InternalLogger import com.datadog.android.sessionreplay.ImagePrivacy import com.datadog.android.sessionreplay.compose.internal.data.BitmapInfo @@ -54,9 +53,6 @@ internal class ImageSemanticsNodeMapperTest : AbstractSemanticsNodeMapperTest() @Mock private lateinit var mockInternalLogger: InternalLogger - @Mock - private lateinit var mockSemanticsNode: SemanticsNode - @Mock private lateinit var mockAsyncJobStatusCallback: AsyncJobStatusCallback diff --git a/features/dd-sdk-android-session-replay-compose/transitiveDependencies b/features/dd-sdk-android-session-replay-compose/transitiveDependencies index cad117ceb5..72c63654fb 100644 --- a/features/dd-sdk-android-session-replay-compose/transitiveDependencies +++ b/features/dd-sdk-android-session-replay-compose/transitiveDependencies @@ -46,7 +46,7 @@ androidx.versionedparcelable:versionedparcelable:1.1.1 : 30 Kb com.google.code.gson:gson:2.10.1 : 276 Kb org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.0 : 963 b org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.0 : 968 b -org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb +org.jetbrains.kotlin:kotlin-stdlib:2.3.20 : 1762 Kb org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4 : 19 Kb org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.6.4 : 1442 Kb org.jetbrains:annotations:13.0 : 17 Kb diff --git a/features/dd-sdk-android-session-replay-material/api/compiler-meta.txt b/features/dd-sdk-android-session-replay-material/api/compiler-meta.txt index a098d10949..f757d782cb 100644 --- a/features/dd-sdk-android-session-replay-material/api/compiler-meta.txt +++ b/features/dd-sdk-android-session-replay-material/api/compiler-meta.txt @@ -1,2 +1,2 @@ -kotlin_abi_version=2.0.0 -jvm_bytecode_version=11 +kotlin_abi_version=2.1.0 +jvm_bytecode_version=17 diff --git a/features/dd-sdk-android-session-replay-material/build.gradle.kts b/features/dd-sdk-android-session-replay-material/build.gradle.kts index 79734bd89a..9a21d9afc2 100644 --- a/features/dd-sdk-android-session-replay-material/build.gradle.kts +++ b/features/dd-sdk-android-session-replay-material/build.gradle.kts @@ -4,15 +4,14 @@ * Copyright 2016-Present Datadog, Inc. */ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("datadogBuildConfig") // Publish @@ -63,7 +62,7 @@ dependencies { } datadogBuild { - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyAndroidLibraryConfig() applyJunitConfig() applyJavadocConfig() diff --git a/features/dd-sdk-android-session-replay-material/transitiveDependencies b/features/dd-sdk-android-session-replay-material/transitiveDependencies index 156acdc0d2..d1970e467f 100644 --- a/features/dd-sdk-android-session-replay-material/transitiveDependencies +++ b/features/dd-sdk-android-session-replay-material/transitiveDependencies @@ -39,7 +39,7 @@ androidx.viewpager:viewpager:1.0.0 : 52 Kb androidx.viewpager2:viewpager2:1.0.0 : 60 Kb com.google.android.material:material:1.3.0 : 1535 Kb com.google.code.gson:gson:2.10.1 : 276 Kb -org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb +org.jetbrains.kotlin:kotlin-stdlib:2.3.20 : 1762 Kb org.jetbrains:annotations:13.0 : 17 Kb Total transitive dependencies size : 7 Mb diff --git a/features/dd-sdk-android-session-replay/api/compiler-meta.txt b/features/dd-sdk-android-session-replay/api/compiler-meta.txt index a098d10949..f757d782cb 100644 --- a/features/dd-sdk-android-session-replay/api/compiler-meta.txt +++ b/features/dd-sdk-android-session-replay/api/compiler-meta.txt @@ -1,2 +1,2 @@ -kotlin_abi_version=2.0.0 -jvm_bytecode_version=11 +kotlin_abi_version=2.1.0 +jvm_bytecode_version=17 diff --git a/features/dd-sdk-android-session-replay/build.gradle.kts b/features/dd-sdk-android-session-replay/build.gradle.kts index 99f3b6bc68..6c0e9d3f8c 100644 --- a/features/dd-sdk-android-session-replay/build.gradle.kts +++ b/features/dd-sdk-android-session-replay/build.gradle.kts @@ -8,15 +8,15 @@ import com.datadog.gradle.utils.cloneRumEventsFormat import com.datadog.gradle.utils.createJsonModelsGenerationTask import com.datadog.gradle.utils.createRumSchemaCloneTask -import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("com.google.devtools.ksp") id("datadogBuildConfig") @@ -125,7 +125,7 @@ createJsonModelsGenerationTask("generateSessionReplayModels") { } datadogBuild { - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyAndroidLibraryConfig() applyJunitConfig() applyJavadocConfig() diff --git a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/embedded/EmbeddedContentReceiver.kt b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/embedded/EmbeddedContentReceiver.kt index f332290c5e..48ba32d48a 100644 --- a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/embedded/EmbeddedContentReceiver.kt +++ b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/embedded/EmbeddedContentReceiver.kt @@ -134,6 +134,10 @@ internal class EmbeddedContentReceiver( * also no longer registered has been torn down, and nothing will draw it again; holding its * batches until the bounds happen to displace them only delays the same unshifted write. */ + // Not a sequence on purpose: the chain must stay eager so that the `remove` calls happen + // inside the `synchronized` block. A sequence would be consumed by `writeAll` after the + // lock is released, mutating `pendingBatches` unguarded. + @Suppress("CouldBeSequence") private fun flushAbandonedSlots(drawnSlotIds: Set) { val activeSlotIds = embeddedContentSlotRegistry.activeSlotIds() val abandoned = synchronized(pendingBatches) { diff --git a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/processor/MutationResolver.kt b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/processor/MutationResolver.kt index 78ff0bdb24..5021f7ea48 100644 --- a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/processor/MutationResolver.kt +++ b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/processor/MutationResolver.kt @@ -39,7 +39,7 @@ internal class MutationResolver(private val internalLogger: InternalLogger) { * @return the mutations as [MobileSegment.MobileIncrementalData.MobileMutationData] * describing changes from `oldArray` to `newArray` or null if no difference was found. */ - @Suppress("LongMethod", "ComplexMethod", "NestedBlockDepth", "ReturnCount") + @Suppress("LongMethod", "ComplexMethod", "CyclomaticComplexMethod", "NestedBlockDepth", "ReturnCount") internal fun resolveMutations( oldSnapshot: List, newSnapshot: List diff --git a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/SessionReplayRecorder.kt b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/SessionReplayRecorder.kt index 926e67c1e3..738aa35776 100644 --- a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/SessionReplayRecorder.kt +++ b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/SessionReplayRecorder.kt @@ -437,6 +437,7 @@ internal class SessionReplayRecorder : OnWindowRefreshedCallback, Recorder { } // a window already open (e.g. a dialog) isn't reported through the Activity lifecycle + @Suppress("CouldBeSequence") private fun resolveUntrackedWindows(decorViews: List, knownWindows: List): List { return decorViews .filterNot { it.width == 0 || it.height == 0 } diff --git a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/mapper/AndroidMDrawableToColorMapper.kt b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/mapper/AndroidMDrawableToColorMapper.kt index e387a69904..b79e195fd6 100644 --- a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/mapper/AndroidMDrawableToColorMapper.kt +++ b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/mapper/AndroidMDrawableToColorMapper.kt @@ -256,6 +256,9 @@ internal open class AndroidMDrawableToColorMapper( // PorterDuffColorFilter.mColor is blocked (max-target-o) on API 28+ and unused on API 29+ // (AndroidQDrawableToColorMapper overrides resolveGradientDrawable). Skip reflection on P+. + // Reflection is guarded by the SDK_INT < P check below, so the blocked field is never + // accessed on API 37+; SuppressLint silences the static-only SoonBlockedPrivateApi check. + @SuppressLint("SoonBlockedPrivateApi") @Suppress("PrivateAPI", "SwallowedException", "TooGenericExceptionCaught") internal val mColorField = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { try { diff --git a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/resources/Alpha8ResourceCache.kt b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/resources/Alpha8ResourceCache.kt index 5f100b8c0c..e873ece8b2 100644 --- a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/resources/Alpha8ResourceCache.kt +++ b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/resources/Alpha8ResourceCache.kt @@ -84,6 +84,7 @@ internal class DefaultAlpha8ResourceCache( override fun onConfigurationChanged(newConfig: Configuration) {} @Deprecated("Deprecated in Java") + @Suppress("OVERRIDE_DEPRECATION") override fun onLowMemory() { cache.evictAll() } diff --git a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/resources/BitmapPool.kt b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/resources/BitmapPool.kt index 370af1e7bf..5a5b687f58 100644 --- a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/resources/BitmapPool.kt +++ b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/resources/BitmapPool.kt @@ -108,6 +108,7 @@ internal class BitmapPool( @Deprecated("Deprecated in Java") @Synchronized + @Suppress("OVERRIDE_DEPRECATION") override fun onLowMemory() { bitmapPoolHelper.safeCall { @Suppress("UnsafeThirdPartyFunctionCall") // Called within a try/catch block diff --git a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/SessionReplayConfigurationBuilderTest.kt b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/SessionReplayConfigurationBuilderTest.kt index 528b01fd3b..4c10399adf 100644 --- a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/SessionReplayConfigurationBuilderTest.kt +++ b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/SessionReplayConfigurationBuilderTest.kt @@ -399,7 +399,7 @@ internal class SessionReplayConfigurationBuilderTest { testedBuilder.build() - val expected = String.format(DUPLICATE_MAPPER_DETECTED, ImageView::class.java) + val expected = String.format(Locale.US, DUPLICATE_MAPPER_DETECTED, ImageView::class.java) // Then mockLogger.verifyLog( diff --git a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/forge/ForgeConfigurator.kt b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/forge/ForgeConfigurator.kt index edfdf1b354..d3fbdceab9 100644 --- a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/forge/ForgeConfigurator.kt +++ b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/forge/ForgeConfigurator.kt @@ -35,6 +35,7 @@ internal class ForgeConfigurator : BaseConfigurator() { forge.addFactory(IncrementalSnapshotRecordForgeryFactory()) forge.addFactory(MetaRecordForgeryFactory()) forge.addFactory(ViewEndRecordForgeryFactory()) + forge.addFactory(MobileRecordForgeryFactory()) forge.addFactory(EnrichedRecordForgeryFactory()) forge.addFactory(ResourceEventForgeryFactory()) forge.addFactory(EnrichedResourceForgeryFactory()) diff --git a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/forge/MobileRecordForgeryFactory.kt b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/forge/MobileRecordForgeryFactory.kt new file mode 100644 index 0000000000..7141279878 --- /dev/null +++ b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/forge/MobileRecordForgeryFactory.kt @@ -0,0 +1,23 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + +package com.datadog.android.sessionreplay.forge + +import com.datadog.android.sessionreplay.model.MobileSegment +import fr.xgouchet.elmyr.Forge +import fr.xgouchet.elmyr.ForgeryFactory + +internal class MobileRecordForgeryFactory : ForgeryFactory { + override fun getForgery(forge: Forge): MobileSegment.MobileRecord { + return forge.anElementFrom( + MetaRecordForgeryFactory().getForgery(forge), + FocusRecordForgeryFactory().getForgery(forge), + ViewEndRecordForgeryFactory().getForgery(forge), + FullSnapshotRecordForgeryFactory().getForgery(forge), + IncrementalSnapshotRecordForgeryFactory().getForgery(forge) + ) + } +} diff --git a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueHandlerTest.kt b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueHandlerTest.kt index 50babe6443..bd8677465e 100644 --- a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueHandlerTest.kt +++ b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueHandlerTest.kt @@ -130,7 +130,7 @@ internal class RecordedDataQueueHandlerTest { ) ) - fakeTouchData = forge.aList { mock() } + fakeTouchData = forge.aList { getForgery() } fakeNodeData = forge.aList { mock() } diff --git a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/gson/GsonExtTest.kt b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/gson/GsonExtTest.kt index 425dec8d68..59e110d792 100644 --- a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/gson/GsonExtTest.kt +++ b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/gson/GsonExtTest.kt @@ -24,6 +24,7 @@ import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension import org.mockito.junit.jupiter.MockitoSettings import org.mockito.quality.Strictness +import java.util.Locale @Extensions( ExtendWith(MockitoExtension::class), @@ -48,6 +49,7 @@ internal class GsonExtTest { fun `M return null W safeGetAsJsonObject{a JsonArray}`(@Forgery fakeJsonArray: JsonArray) { // Given val expectedLogMessage = BROKEN_JSON_ERROR_MESSAGE_FORMAT.format( + Locale.US, fakeJsonArray.toString(), JSON_OBJECT_TYPE ) @@ -67,6 +69,7 @@ internal class GsonExtTest { ) { // Given val expectedLogMessage = BROKEN_JSON_ERROR_MESSAGE_FORMAT.format( + Locale.US, fakeJsonPrimitive.toString(), JSON_OBJECT_TYPE ) @@ -95,6 +98,7 @@ internal class GsonExtTest { ) { // Given val expectedLogMessage = BROKEN_JSON_ERROR_MESSAGE_FORMAT.format( + Locale.US, fakeJsonPrimitive.toString(), JSON_ARRAY_TYPE ) @@ -125,6 +129,7 @@ internal class GsonExtTest { // Given val fakeNonLongJsonPrimitive = JsonPrimitive(forge.anAlphabeticalString()) val expectedLogMessage = BROKEN_JSON_ERROR_MESSAGE_FORMAT.format( + Locale.US, fakeNonLongJsonPrimitive.toString(), JSON_PRIMITIVE_TYPE ) diff --git a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/recorder/WindowReflectionUtilsTest.kt b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/recorder/WindowReflectionUtilsTest.kt index 569843026b..abae98379b 100644 --- a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/recorder/WindowReflectionUtilsTest.kt +++ b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/recorder/WindowReflectionUtilsTest.kt @@ -52,7 +52,7 @@ internal class WindowReflectionUtilsTest { // Given val fakeWindow = mock() val fakeDecorLike = object : View(mock()) { - @Suppress("unused") + @Suppress("unused", "UnusedPrivateProperty") private val mWindow: Window = fakeWindow } diff --git a/features/dd-sdk-android-session-replay/transitiveDependencies b/features/dd-sdk-android-session-replay/transitiveDependencies index 40e5eaa785..504329480d 100644 --- a/features/dd-sdk-android-session-replay/transitiveDependencies +++ b/features/dd-sdk-android-session-replay/transitiveDependencies @@ -31,7 +31,7 @@ com.squareup.okhttp3:okhttp:4.12.0 : 771 Kb com.squareup.okio:okio-jvm:3.6.0 : 351 Kb org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.10 : 959 b org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10 : 965 b -org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb +org.jetbrains.kotlin:kotlin-stdlib:2.3.20 : 1762 Kb org.jetbrains:annotations:13.0 : 17 Kb Total transitive dependencies size : 5 Mb diff --git a/features/dd-sdk-android-trace-api/api/compiler-meta.txt b/features/dd-sdk-android-trace-api/api/compiler-meta.txt index a098d10949..f757d782cb 100644 --- a/features/dd-sdk-android-trace-api/api/compiler-meta.txt +++ b/features/dd-sdk-android-trace-api/api/compiler-meta.txt @@ -1,2 +1,2 @@ -kotlin_abi_version=2.0.0 -jvm_bytecode_version=11 +kotlin_abi_version=2.1.0 +jvm_bytecode_version=17 diff --git a/features/dd-sdk-android-trace-api/build.gradle.kts b/features/dd-sdk-android-trace-api/build.gradle.kts index f673240bd3..bd0f2c387f 100644 --- a/features/dd-sdk-android-trace-api/build.gradle.kts +++ b/features/dd-sdk-android-trace-api/build.gradle.kts @@ -6,15 +6,15 @@ @file:Suppress("StringLiteralDuplication") import com.datadog.gradle.config.taskConfig -import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("com.google.devtools.ksp") id("datadogBuildConfig") @@ -61,7 +61,7 @@ unMock { } datadogBuild { - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyAndroidLibraryConfig() applyJunitConfig() applyJavadocConfig() diff --git a/features/dd-sdk-android-trace-api/transitiveDependencies b/features/dd-sdk-android-trace-api/transitiveDependencies index 998404520a..22a5fc6e8a 100644 --- a/features/dd-sdk-android-trace-api/transitiveDependencies +++ b/features/dd-sdk-android-trace-api/transitiveDependencies @@ -4,7 +4,7 @@ androidx.annotation:annotation-jvm:1.9.1 : 59 Kb com.google.code.gson:gson:2.10.1 : 276 Kb com.google.re2j:re2j:1.7 : 111 Kb org.jctools:jctools-core:3.3.0 : 328 Kb -org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb +org.jetbrains.kotlin:kotlin-stdlib:2.3.20 : 1762 Kb org.jetbrains:annotations:13.0 : 17 Kb Total transitive dependencies size : 2 Mb diff --git a/features/dd-sdk-android-trace-internal/api/compiler-meta.txt b/features/dd-sdk-android-trace-internal/api/compiler-meta.txt index a098d10949..f757d782cb 100644 --- a/features/dd-sdk-android-trace-internal/api/compiler-meta.txt +++ b/features/dd-sdk-android-trace-internal/api/compiler-meta.txt @@ -1,2 +1,2 @@ -kotlin_abi_version=2.0.0 -jvm_bytecode_version=11 +kotlin_abi_version=2.1.0 +jvm_bytecode_version=17 diff --git a/features/dd-sdk-android-trace-internal/build.gradle.kts b/features/dd-sdk-android-trace-internal/build.gradle.kts index d00ac2da57..9c5a868e32 100644 --- a/features/dd-sdk-android-trace-internal/build.gradle.kts +++ b/features/dd-sdk-android-trace-internal/build.gradle.kts @@ -6,15 +6,15 @@ @file:Suppress("StringLiteralDuplication") import com.datadog.gradle.plugin.gitclone.GitCloneDependenciesTask -import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("datadogBuildConfig") // Publish @@ -122,7 +122,7 @@ tasks.register("cloneDdTrace") { } datadogBuild { - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyAndroidLibraryConfig() applyJunitConfig() applyJavadocConfig() diff --git a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/DDSpanContextTest.kt b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/DDSpanContextTest.kt index 967d6e387a..e905832234 100644 --- a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/DDSpanContextTest.kt +++ b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/DDSpanContextTest.kt @@ -215,7 +215,7 @@ internal class DDSpanContextTest : DDCoreSpecification() { "789", tracer.propagationTagsFactory.empty(), TracePropagationStyle.DATADOG - ).withRequestContextDataAppSec("palceholder") + ).withRequestContextDataAppSec("placeholder") val top = tracer .buildSpan(instrumentationName, "top") @@ -334,20 +334,20 @@ internal class DDSpanContextTest : DDCoreSpecification() { @JvmStatic fun provideMetricsForTest(): Stream = Stream.of( - Arguments.of(java.lang.Integer::class.java, 0), - Arguments.of(java.lang.Integer::class.java, Int.MAX_VALUE), - Arguments.of(java.lang.Integer::class.java, Int.MIN_VALUE), - Arguments.of(java.lang.Short::class.java, Short.MAX_VALUE), - Arguments.of(java.lang.Short::class.java, Short.MIN_VALUE), - Arguments.of(java.lang.Float::class.java, Float.MAX_VALUE), - Arguments.of(java.lang.Float::class.java, Float.MIN_VALUE), - Arguments.of(java.lang.Double::class.java, Double.MAX_VALUE), - Arguments.of(java.lang.Double::class.java, Double.MIN_VALUE), - Arguments.of(java.lang.Float::class.java, 1f), - Arguments.of(java.lang.Double::class.java, 1.0), - Arguments.of(java.lang.Float::class.java, 0.5f), - Arguments.of(java.lang.Double::class.java, 0.5), - Arguments.of(java.lang.Integer::class.java, 0x55) + Arguments.of(Int::class.javaObjectType, 0), + Arguments.of(Int::class.javaObjectType, Int.MAX_VALUE), + Arguments.of(Int::class.javaObjectType, Int.MIN_VALUE), + Arguments.of(Short::class.javaObjectType, Short.MAX_VALUE), + Arguments.of(Short::class.javaObjectType, Short.MIN_VALUE), + Arguments.of(Float::class.javaObjectType, Float.MAX_VALUE), + Arguments.of(Float::class.javaObjectType, Float.MIN_VALUE), + Arguments.of(Double::class.javaObjectType, Double.MAX_VALUE), + Arguments.of(Double::class.javaObjectType, Double.MIN_VALUE), + Arguments.of(Float::class.javaObjectType, 1f), + Arguments.of(Double::class.javaObjectType, 1.0), + Arguments.of(Float::class.javaObjectType, 0.5f), + Arguments.of(Double::class.javaObjectType, 0.5), + Arguments.of(Int::class.javaObjectType, 0x55) ) @JvmStatic diff --git a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/logger/DatadogCoreTracerLoggerTest.kt b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/logger/DatadogCoreTracerLoggerTest.kt index b10c4805d0..3dd89e2be5 100644 --- a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/logger/DatadogCoreTracerLoggerTest.kt +++ b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/logger/DatadogCoreTracerLoggerTest.kt @@ -45,15 +45,15 @@ internal class DatadogCoreTracerLoggerTest { @Mock lateinit var mockInternalLogger: InternalLogger - private val fakeArg1 = Object() - private val fakeArg2 = Object() + private val fakeArg1 = Any() + private val fakeArg2 = Any() lateinit var fakeArgs: Array private lateinit var fakeThrowable: Throwable @BeforeEach fun `set up`(forge: Forge) { - fakeArgs = forge.aList(size = forge.aSmallInt()) { Object() }.toTypedArray() + fakeArgs = forge.aList(size = forge.aSmallInt()) { Any() }.toTypedArray() fakeThrowable = forge.getForgery() expectedMessage = String.format(Locale.US, "%s: %s", fakeLoggerName, fakeMessage) testedLogger = diff --git a/features/dd-sdk-android-trace-internal/transitiveDependencies b/features/dd-sdk-android-trace-internal/transitiveDependencies index 998404520a..22a5fc6e8a 100644 --- a/features/dd-sdk-android-trace-internal/transitiveDependencies +++ b/features/dd-sdk-android-trace-internal/transitiveDependencies @@ -4,7 +4,7 @@ androidx.annotation:annotation-jvm:1.9.1 : 59 Kb com.google.code.gson:gson:2.10.1 : 276 Kb com.google.re2j:re2j:1.7 : 111 Kb org.jctools:jctools-core:3.3.0 : 328 Kb -org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb +org.jetbrains.kotlin:kotlin-stdlib:2.3.20 : 1762 Kb org.jetbrains:annotations:13.0 : 17 Kb Total transitive dependencies size : 2 Mb diff --git a/features/dd-sdk-android-trace-otel/api/compiler-meta.txt b/features/dd-sdk-android-trace-otel/api/compiler-meta.txt index a098d10949..f757d782cb 100644 --- a/features/dd-sdk-android-trace-otel/api/compiler-meta.txt +++ b/features/dd-sdk-android-trace-otel/api/compiler-meta.txt @@ -1,2 +1,2 @@ -kotlin_abi_version=2.0.0 -jvm_bytecode_version=11 +kotlin_abi_version=2.1.0 +jvm_bytecode_version=17 diff --git a/features/dd-sdk-android-trace-otel/build.gradle.kts b/features/dd-sdk-android-trace-otel/build.gradle.kts index fd3daf1ecf..6ce11eafbb 100644 --- a/features/dd-sdk-android-trace-otel/build.gradle.kts +++ b/features/dd-sdk-android-trace-otel/build.gradle.kts @@ -5,15 +5,14 @@ */ @file:Suppress("StringLiteralDuplication") -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("datadogBuildConfig") // Publish @@ -84,7 +83,7 @@ unMock { } datadogBuild { - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyAndroidLibraryConfig() applyJunitConfig() applyJavadocConfig() diff --git a/features/dd-sdk-android-trace-otel/transitiveDependencies b/features/dd-sdk-android-trace-otel/transitiveDependencies index 8372b1583e..80d588a8b0 100644 --- a/features/dd-sdk-android-trace-otel/transitiveDependencies +++ b/features/dd-sdk-android-trace-otel/transitiveDependencies @@ -3,8 +3,8 @@ Dependencies List androidx.annotation:annotation-jvm:1.9.1 : 59 Kb io.opentelemetry:opentelemetry-api:1.40.0 : 138 Kb io.opentelemetry:opentelemetry-context:1.40.0 : 46 Kb -org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb +org.jetbrains.kotlin:kotlin-stdlib:2.3.20 : 1762 Kb org.jetbrains:annotations:13.0 : 17 Kb -Total transitive dependencies size : 1967 Kb +Total transitive dependencies size : 2023 Kb diff --git a/features/dd-sdk-android-trace/api/compiler-meta.txt b/features/dd-sdk-android-trace/api/compiler-meta.txt index a098d10949..f757d782cb 100644 --- a/features/dd-sdk-android-trace/api/compiler-meta.txt +++ b/features/dd-sdk-android-trace/api/compiler-meta.txt @@ -1,2 +1,2 @@ -kotlin_abi_version=2.0.0 -jvm_bytecode_version=11 +kotlin_abi_version=2.1.0 +jvm_bytecode_version=17 diff --git a/features/dd-sdk-android-trace/build.gradle.kts b/features/dd-sdk-android-trace/build.gradle.kts index 768c74f01e..be5cb00e9f 100644 --- a/features/dd-sdk-android-trace/build.gradle.kts +++ b/features/dd-sdk-android-trace/build.gradle.kts @@ -6,16 +6,16 @@ @file:Suppress("StringLiteralDuplication") import com.datadog.gradle.utils.createJsonModelsGenerationTask -import org.jetbrains.kotlin.gradle.dsl.JvmTarget import java.nio.file.Paths plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("com.google.devtools.ksp") id("datadogBuildConfig") @@ -62,7 +62,6 @@ dependencies { implementation(libs.kotlin) implementation(libs.gson) implementation(libs.androidXAnnotation) - implementation(libs.bundles.traceCore) // Generate NoOp implementations ksp(project(":tools:noopfactory")) @@ -81,7 +80,8 @@ dependencies { unmock(libs.robolectric) - // Test Fixtures + // Test Fixtures — declared explicitly because the testFixtures source set is its own variant + // and does not inherit the main `implementation` classpath (nor the auto-added kotlin-stdlib). testFixturesImplementation(libs.gson) testFixturesImplementation(libs.kotlin) testFixturesImplementation(libs.okHttp) @@ -106,7 +106,7 @@ createJsonModelsGenerationTask("generateTraceModelsFromJson") { } datadogBuild { - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyAndroidLibraryConfig() applyJunitConfig() applyJavadocConfig() diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogPropagationHelper.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogPropagationHelper.kt index e31e7d57a1..404de4b065 100644 --- a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogPropagationHelper.kt +++ b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogPropagationHelper.kt @@ -23,6 +23,7 @@ import com.datadog.trace.core.propagation.B3HttpCodec import com.datadog.trace.core.propagation.DatadogHttpCodec import com.datadog.trace.core.propagation.ExtractedContext import com.datadog.trace.core.propagation.W3CHttpCodec +import java.util.Locale /** * For internal usage only. @@ -439,6 +440,7 @@ class DatadogPropagationHelper internal constructor() { W3CHttpCodec.TRACE_PARENT_KEY, @Suppress("UnsafeThirdPartyFunctionCall") // Format string is static W3C_TRACE_PARENT_DROP_SAMPLING_DECISION.format( + Locale.US, traceId.padStart(length = W3C_TRACE_ID_LENGTH, padChar = '0'), spanId.padStart(length = W3C_PARENT_ID_LENGTH, padChar = '0') ) @@ -446,7 +448,7 @@ class DatadogPropagationHelper internal constructor() { // TODO RUM-2121 3rd party vendor information will be erased @Suppress("UnsafeThirdPartyFunctionCall") // Format string is static var traceStateHeader = W3C_TRACE_STATE_DROP_SAMPLING_DECISION - .format(spanId.padStart(length = W3C_PARENT_ID_LENGTH, padChar = '0')) + .format(Locale.US, spanId.padStart(length = W3C_PARENT_ID_LENGTH, padChar = '0')) if (traceOrigin != null) { traceStateHeader += ";o:$traceOrigin" } diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/_TraceInternalProxy.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/_TraceInternalProxy.kt index 8dbe7c073f..1ffc209dad 100644 --- a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/_TraceInternalProxy.kt +++ b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/_TraceInternalProxy.kt @@ -20,7 +20,7 @@ import com.datadog.trace.core.propagation.Baggage * Provides implementation for specific interfaces to dependent modules */ @InternalApi -@Suppress("ClassName") +@Suppress("ClassName", "ClassNaming") object _TraceInternalProxy { /** * Provides a mechanism for converting Datadog span IDs between decimal and hexadecimal representations. diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/domain/event/SpanEventSerializerTest.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/domain/event/SpanEventSerializerTest.kt index 533e18b3c8..442fa98f5f 100644 --- a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/domain/event/SpanEventSerializerTest.kt +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/domain/event/SpanEventSerializerTest.kt @@ -229,8 +229,11 @@ internal class SpanEventSerializerTest { meta = fakeSpanEvent.meta.copy( account = fakeSpanEvent.meta.account?.copy( additionalProperties = fakeSpanEvent.meta.account - ?.additionalProperties - .orEmpty() + .additionalProperties + // Copy before mutating: `fakeSpanEvent` is the expected value in + // assertJsonMatchesInputSpan below, so injecting the faulty entry in place + // would poison the assertion (only visible when Forge yields a non-null + // account, which is why it failed on the release seed and not on debug). .toMutableMap() .apply { put(faultyKey, faultyObject) } ) @@ -318,8 +321,7 @@ internal class SpanEventSerializerTest { ) { attributes.filter { it.key.isNotBlank() } .forEach { - val value = it.value - when (value) { + when (val value = it.value) { NULL_MAP_VALUE -> doesNotHaveField(it.key) null -> doesNotHaveField(it.key) is Date -> hasField(it.key, value.time.toString()) diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/net/TracesRequestFactoryTest.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/net/TracesRequestFactoryTest.kt index 0db94910fe..ac36f47ac3 100644 --- a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/net/TracesRequestFactoryTest.kt +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/net/TracesRequestFactoryTest.kt @@ -48,16 +48,15 @@ internal class TracesRequestFactoryTest { ) } - @Suppress("NAME_SHADOWING") @Test fun `M create a proper request W create()`( @Forgery batchData: List, - @StringForgery batchMetadata: String, + @StringForgery fakeBatchMetadata: String, @Forgery executionContext: RequestExecutionContext, forge: Forge ) { // Given - val batchMetadata = forge.aNullable { batchMetadata.toByteArray() } + val batchMetadata = forge.aNullable { fakeBatchMetadata.toByteArray() } // When val request = testedFactory.create(fakeDatadogContext, executionContext, batchData, batchMetadata) @@ -84,18 +83,17 @@ internal class TracesRequestFactoryTest { ) } - @Suppress("NAME_SHADOWING") @Test fun `M create a proper request W create() { custom endpoint }`( @StringForgery(regex = "https://[a-z]+\\.com(/[a-z]+)+") fakeEndpoint: String, @Forgery batchData: List, - @StringForgery batchMetadata: String, + @StringForgery fakeBatchMetadata: String, @Forgery executionContext: RequestExecutionContext, forge: Forge ) { // Given testedFactory = TracesRequestFactory(customEndpointUrl = fakeEndpoint, internalLogger = InternalLogger.UNBOUND) - val batchMetadata = forge.aNullable { batchMetadata.toByteArray() } + val batchMetadata = forge.aNullable { fakeBatchMetadata.toByteArray() } // When val request = testedFactory.create(fakeDatadogContext, executionContext, batchData, batchMetadata) diff --git a/features/dd-sdk-android-trace/src/testFixtures/kotlin/com/datadog/android/trace/api/DatadogTracingTestExt.kt b/features/dd-sdk-android-trace/src/testFixtures/kotlin/com/datadog/android/trace/api/DatadogTracingTestExt.kt index e83c8e72ef..9e518f84e8 100644 --- a/features/dd-sdk-android-trace/src/testFixtures/kotlin/com/datadog/android/trace/api/DatadogTracingTestExt.kt +++ b/features/dd-sdk-android-trace/src/testFixtures/kotlin/com/datadog/android/trace/api/DatadogTracingTestExt.kt @@ -30,7 +30,7 @@ val DatadogSpanContext.resourceName: String? get() = ddSpanContext?.resourceName?.toString() val DatadogSpanContext.serviceName: String? - get() = ddSpanContext?.serviceName?.toString() + get() = ddSpanContext?.serviceName val DatadogTraceId.Companion.ZERO: DatadogTraceId get() = DatadogTraceIdAdapter(DDTraceId.ZERO) diff --git a/features/dd-sdk-android-trace/transitiveDependencies b/features/dd-sdk-android-trace/transitiveDependencies index 998404520a..74216ceb2c 100644 --- a/features/dd-sdk-android-trace/transitiveDependencies +++ b/features/dd-sdk-android-trace/transitiveDependencies @@ -2,9 +2,7 @@ Dependencies List androidx.annotation:annotation-jvm:1.9.1 : 59 Kb com.google.code.gson:gson:2.10.1 : 276 Kb -com.google.re2j:re2j:1.7 : 111 Kb -org.jctools:jctools-core:3.3.0 : 328 Kb -org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb +org.jetbrains.kotlin:kotlin-stdlib:2.3.20 : 1762 Kb org.jetbrains:annotations:13.0 : 17 Kb Total transitive dependencies size : 2 Mb diff --git a/features/dd-sdk-android-webview/api/compiler-meta.txt b/features/dd-sdk-android-webview/api/compiler-meta.txt index a098d10949..f757d782cb 100644 --- a/features/dd-sdk-android-webview/api/compiler-meta.txt +++ b/features/dd-sdk-android-webview/api/compiler-meta.txt @@ -1,2 +1,2 @@ -kotlin_abi_version=2.0.0 -jvm_bytecode_version=11 +kotlin_abi_version=2.1.0 +jvm_bytecode_version=17 diff --git a/features/dd-sdk-android-webview/build.gradle.kts b/features/dd-sdk-android-webview/build.gradle.kts index 9cf56bac9d..3e4bf56478 100644 --- a/features/dd-sdk-android-webview/build.gradle.kts +++ b/features/dd-sdk-android-webview/build.gradle.kts @@ -4,15 +4,14 @@ * Copyright 2016-Present Datadog, Inc. */ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("com.google.devtools.ksp") id("datadogBuildConfig") @@ -73,7 +72,7 @@ unMock { } datadogBuild { - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyAndroidLibraryConfig() applyJunitConfig() applyJavadocConfig() diff --git a/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/replay/WebViewReplayEventMapper.kt b/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/replay/WebViewReplayEventMapper.kt index ef2b079aac..b598d09c3e 100644 --- a/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/replay/WebViewReplayEventMapper.kt +++ b/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/replay/WebViewReplayEventMapper.kt @@ -34,16 +34,15 @@ internal class WebViewReplayEventMapper( val viewDataObject = event.get(VIEW_OBJECT_KEY)?.asJsonObject val viewId = viewDataObject?.get(VIEW_ID_KEY)?.asString ?: error(BROWSER_EVENT_MISSING_VIEW_DATA_ERROR_MESSAGE) - event.get(EVENT_KEY)?.asJsonObject?.let { record -> - val timeOffset = offsetProvider.getOffset(viewId, datadogContext) - record.get(TIMESTAMP_KEY)?.let { timestamp -> - val asLong = timestamp.asLong - val correctedTimestamp = asLong + timeOffset - record.addProperty(TIMESTAMP_KEY, correctedTimestamp) - } - record.addProperty(SLOT_ID_KEY, webViewId) - return bundleIntoEnrichedRecord(record, viewId, rumContext) - } ?: error(BROWSER_EVENT_MISSING_RECORD_ERROR_MESSAGE) + val record = event.get(EVENT_KEY)?.asJsonObject + ?: error(BROWSER_EVENT_MISSING_RECORD_ERROR_MESSAGE) + + val timeOffset = offsetProvider.getOffset(viewId, datadogContext) + record.get(TIMESTAMP_KEY)?.let { timestamp -> + record.addProperty(TIMESTAMP_KEY, timestamp.asLong + timeOffset) + } + record.addProperty(SLOT_ID_KEY, webViewId) + return bundleIntoEnrichedRecord(record, viewId, rumContext) } private fun bundleIntoEnrichedRecord( diff --git a/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/rum/domain/WebViewNativeRumViewsCache.kt b/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/rum/domain/WebViewNativeRumViewsCache.kt index dec5646387..2f7fb26b61 100644 --- a/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/rum/domain/WebViewNativeRumViewsCache.kt +++ b/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/rum/domain/WebViewNativeRumViewsCache.kt @@ -25,7 +25,7 @@ internal class WebViewNativeRumViewsCache( val iterator = parentViewsHistoryQueue.iterator() var backupCandidate: String? = null while (iterator.hasNext()) { - // the function is synchronized and we are checking hasNext() before calling next() + // the function is synchronized, and we are checking hasNext() before calling next() @Suppress("UnsafeThirdPartyFunctionCall") val entry = iterator.next() if (entry.timestamp <= browserEventTimestampInMs) { @@ -96,7 +96,7 @@ internal class WebViewNativeRumViewsCache( while (parentViewsHistoryQueue.size > DATA_CACHE_ENTRIES_LIMIT) { // removeAt(index) instead of removeLast here is on purpose, to prevent issues // with old AGP being used when compiling with Android API 35. - // the function is synchronized and we are checking the size before + // the function is synchronized, and we are checking the size before @Suppress("UnsafeThirdPartyFunctionCall") parentViewsHistoryQueue.removeAt(parentViewsHistoryQueue.size - 1) } diff --git a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/replay/WebViewReplayEventMapperTest.kt b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/replay/WebViewReplayEventMapperTest.kt index c1defe8f66..ff159a82f8 100644 --- a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/replay/WebViewReplayEventMapperTest.kt +++ b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/replay/WebViewReplayEventMapperTest.kt @@ -159,7 +159,7 @@ internal class WebViewReplayEventMapperTest { assertThat(mappedWrappedEvent.get(WebViewReplayEventMapper.TIMESTAMP_KEY).asLong) .isEqualTo(fakeTimestamp + fakeServerTimeOffset) assertThat(mappedWrappedEvent.get(WebViewReplayEventMapper.SLOT_ID_KEY).asString) - .isEqualTo(fakeWebViewId.toString()) + .isEqualTo(fakeWebViewId) assertThat(result.get(WebViewReplayEventMapper.ENRICHED_RECORD_APPLICATION_ID_KEY).asString) .isEqualTo(fakeRumContext.applicationId) assertThat(result.get(WebViewReplayEventMapper.ENRICHED_RECORD_SESSION_ID_KEY).asString) diff --git a/features/dd-sdk-android-webview/transitiveDependencies b/features/dd-sdk-android-webview/transitiveDependencies index b099002d83..74216ceb2c 100644 --- a/features/dd-sdk-android-webview/transitiveDependencies +++ b/features/dd-sdk-android-webview/transitiveDependencies @@ -2,7 +2,7 @@ Dependencies List androidx.annotation:annotation-jvm:1.9.1 : 59 Kb com.google.code.gson:gson:2.10.1 : 276 Kb -org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb +org.jetbrains.kotlin:kotlin-stdlib:2.3.20 : 1762 Kb org.jetbrains:annotations:13.0 : 17 Kb Total transitive dependencies size : 2 Mb diff --git a/gradle.properties b/gradle.properties index e13eca421b..bcdd0339be 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,13 +18,4 @@ org.jetbrains.dokka.experimental.gradle.pluginMode.noWarn=true # Allows Kotlin with target JVM compat 17 to be built on JDK 21 kotlin.jvm.target.validation.mode = IGNORE -# TODO RUM-18189 -# Kotlin 2.0 is not compatible with new DSL APIs enforced in AGP 9 by default. This option will -# be removed in AGP 10 (2027-2028?) -android.newDsl=false -android.builtInKotlin=false - -# Changed from false to true by default on AGP 8 -> AGP 9, putting the old behavior -android.onlyEnableUnitTestForTheTestedBuildType=false - # Leave the next line blank for CI diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ddb13110e1..4633712a5d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,20 +1,11 @@ [versions] # Commons -apollo = "4.3.3" +apollo = "4.4.3" cronetApi = "143.7445.0" cronetPlayServices = "18.1.1" gson = "2.10.1" -kotlin = "2.0.21" -# Deliberately not `version.ref = "kotlin"`: this must match the Kotlin Gradle Plugin that is actually -# loaded, which is AGP's. `androidToolsPlugin` (below) depends on KGP 2.2.10, and that outranks the -# `kotlin` version above in conflict resolution (`kotlin-gradle-plugin:2.0.21 -> 2.2.10`). -# The Compose *compiler* is always resolved at the loaded KGP's version and never at this one, so the -# two drift apart silently. An older plugin against a newer compiler passes options the compiler has -# since deprecated (RUM-18191, `generateFunctionKeyMetaClasses`), and the version also decides which -# options are sent at all — 2.2.10 turns on `sourceInformation`, which changes generated Compose code. -# Re-check this whenever `androidToolsPlugin` moves. -composeCompilerPlugin = "2.2.10" -kotlinSP = "2.0.21-1.0.28" +kotlin = "2.3.20" +kotlinSP = "2.3.9" kronosNTP = "0.0.1-alpha11" kotlinxSerialization = "1.6.3" okHttp = "4.12.0" @@ -22,7 +13,7 @@ okHttp = "4.12.0" # Android adapterDelegates = "4.3.2" androidDesugaringSdk = "2.0.4" -androidToolsPlugin = "9.1.1" +androidToolsPlugin = "9.2.1" androidXAnnotations = "1.9.1" androidXAppCompat = "1.4.2" # Next version will bring coroutines androidXCar = "1.4.0" @@ -47,7 +38,7 @@ dagger = "2.56.2" # JUnit jUnitJupiter = "5.9.3" jUnitPlatform = "1.9.3" -jUnitMockitoExt = "5.4.0" +jUnitMockitoExt = "5.12.0" systemStubsJupiter = "2.1.6" # Android JUnit @@ -64,16 +55,16 @@ leakCanary = "2.14" mockitoKotlin = "5.1.0" mockitoAndroid = "5.12.0" kover = "0.9.8" -kspTesting = "1.5.0" +kspTesting = "0.12.1" # Tools -detekt = "1.23.8" +detekt = "2.0.0-alpha.6" dokka = "2.2.0" ktlint = "1.5.0" ktlintGradlePlugin = "14.2.0" -unmock = "0.9.0" +unmock = "0.11.0" robolectric = "4.4_r1-robolectric-r2" -androidLint = "31.0.2" +androidLint = "32.2.1" nexusPublishGradlePlugin = "2.0.0" datadogPlugin = "1.27.0" @@ -85,7 +76,7 @@ dependencyLicense = "0.7.0" kotlinXmlBuilder = "1.9.3" # Integrations -sqlDelight = "1.5.5" +sqlDelight = "2.3.2" coil = "1.0.0" coilCompose = "2.1.0" coil3 = "3.0.4" @@ -95,7 +86,7 @@ picasso = "2.8" room = "2.5.1" rxJava3 = "3.0.0" timber = "5.0.1" -coroutines = "1.7.3" +coroutines = "1.10.2" media3 = "1.1.1" newPipeExtractor = "v0.24.6" navigation3 = "1.0.0-alpha01" @@ -121,7 +112,7 @@ androidToolsGradlePluginApi = { module = "com.android.tools.build:gradle-api", v apolloRuntime = { module = "com.apollographql.apollo:apollo-runtime", version.ref = "apollo" } kotlinGradlePluginApi = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin-api", version.ref = "kotlin" } dokkaGradlePlugin = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref = "dokka" } -detektGradlePlugin = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version.ref = "detekt" } +detektGradlePlugin = { module = "dev.detekt:detekt-gradle-plugin", version.ref = "detekt" } unmockGradlePlugin = { module = "com.github.bjoernq:unmockplugin", version.ref = "unmock" } # Annotation processors @@ -135,6 +126,7 @@ kotlinxSerializationJson = { module = "org.jetbrains.kotlinx:kotlinx-serializati okHttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okHttp" } cronetApi = { module = "org.chromium.net:cronet-api", version.ref = "cronetApi" } +cronetShared = { module = "org.chromium.net:cronet-shared", version.ref = "cronetApi" } cronetPlayServices = { module = "com.google.android.gms:play-services-cronet", version.ref = "cronetPlayServices" } gson = { module = "com.google.code.gson:gson", version.ref = "gson" } kronosNTP = { module = "com.lyft.kronos:kronos-android", version.ref = "kronosNTP" } @@ -210,13 +202,13 @@ leakCanaryInstrumentation = { module = "com.squareup.leakcanary:leakcanary-andro mockitoAndroid = { module = "org.mockito:mockito-android", version.ref = "mockitoAndroid" } mockitoKotlin = { module = "org.mockito.kotlin:mockito-kotlin", version.ref = "mockitoKotlin" } kotlinReflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" } -kspTesting = { module = "com.github.tschuchortdev:kotlin-compile-testing-ksp", version.ref = "kspTesting" } +kspTesting = { module = "dev.zacsweers.kctfork:ksp", version.ref = "kspTesting" } androidLintTests = { module = "com.android.tools.lint:lint-tests", version.ref = "androidLint" } systemStubsJupiter = { module = "uk.org.webcompere:system-stubs-jupiter", version.ref = "systemStubsJupiter" } # Tools -detektApi = { module = "io.gitlab.arturbosch.detekt:detekt-api", version.ref = "detekt" } -detektTest = { module = "io.gitlab.arturbosch.detekt:detekt-test", version.ref = "detekt" } +detektApi = { module = "dev.detekt:detekt-api", version.ref = "detekt" } +detektTest = { module = "dev.detekt:detekt-test", version.ref = "detekt" } ktlintGradlePlugin = { module = "org.jlleitschuh.gradle.ktlint:org.jlleitschuh.gradle.ktlint.gradle.plugin", version.ref = "ktlintGradlePlugin" } okHttpMock = { module = "com.squareup.okhttp3:mockwebserver", version.ref = "okHttp" } robolectric = { module = "org.robolectric:android-all", version.ref = "robolectric" } @@ -231,7 +223,8 @@ jsonSchemaValidator = { module = "com.github.everit-org.json-schema:org.everit.j kotlinXmlBuilder = { module = "org.redundent:kotlin-xml-builder", version.ref = "kotlinXmlBuilder" } # Integrations -sqlDelight = { module = "com.squareup.sqldelight:android-driver", version.ref = "sqlDelight" } +sqlDelight = { module = "app.cash.sqldelight:android-driver", version.ref = "sqlDelight" } +sqlDelightSqlite324Dialect = { module = "app.cash.sqldelight:sqlite-3-24-dialect", version.ref = "sqlDelight" } coil = { module = "io.coil-kt:coil", version.ref = "coil" } coilCompose = { module = "io.coil-kt:coil-compose", version.ref = "coilCompose" } coil3 = { module = "io.coil-kt.coil3:coil", version.ref = "coil3" } @@ -307,8 +300,7 @@ testTools = [ ] androidXNavigation = [ - "androidXNavigationFragment", - "androidXNavigationRuntime" + "androidXNavigationFragment" ] androidXSupportBase = [ @@ -319,8 +311,6 @@ androidXSupportBase = [ androidXCompose = [ "androidXComposeNavigation", - "androidXComposeRuntime", - "androidXComposeUi", "androidXComposeUiTooling", "androidXComposeMaterial", ] @@ -345,7 +335,6 @@ fresco = [ ] glide = [ - "glideAnnotations", "glideCore", "glideOkHttp3", ] @@ -366,7 +355,6 @@ ktorServer = [ "ktorServerSSE" ] ktorClient = [ - "ktorClientCore", "ktorClientOkHttp", "ktorContentNegotiation", "ktorSerializationKotlinxJson" @@ -380,16 +368,15 @@ traceCore = [ [plugins] nexusPublishGradlePlugin = { id = "io.github.gradle-nexus.publish-plugin", version.ref = "nexusPublishGradlePlugin" } datadogGradlePlugin = { id = "com.datadoghq.dd-sdk-android-gradle-plugin", version.ref = "datadogPlugin" } -composeCompilerPlugin = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "composeCompilerPlugin" } +composeCompilerPlugin = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } apolloPlugin = { id = "com.apollographql.apollo", version.ref = "apollo" } dependencyLicenseGradlePlugin = { id = "com.datadoghq.dependency-license", version.ref = "dependencyLicense" } -kotlinAndroidPlugin = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } androidLibraryPlugin = { id = "com.android.library", version.ref = "androidToolsPlugin" } kotlinSPGradlePlugin = { id = "com.google.devtools.ksp", version.ref = "kotlinSP" } dokkaGradlePlugin = { id = "org.jetbrains.dokka", version.ref = "dokka" } -sqlDelightGradlePlugin = { id = "com.squareup.sqldelight", version.ref = "sqlDelight" } +sqlDelightGradlePlugin = { id = "app.cash.sqldelight", version.ref = "sqlDelight" } binaryCompatibilityGradlePlugin = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binaryCompatibility" } kotlinxSerializationPlugin = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } koverPlugin = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" } ktlintGradlePlugin = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlintGradlePlugin" } -detektGradlePlugin = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } +detektGradlePlugin = { id = "dev.detekt", version.ref = "detekt" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 5be30bbeb2..a9cef91aa3 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-all.zip -networkTimeout=10000 -validateDistributionUrl=true +distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.1-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/instrumented/integration/build.gradle.kts b/instrumented/integration/build.gradle.kts index dad57c8b5e..333cfd4180 100644 --- a/instrumented/integration/build.gradle.kts +++ b/instrumented/integration/build.gradle.kts @@ -11,12 +11,13 @@ import com.datadog.gradle.config.taskConfig import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.application") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") alias(libs.plugins.composeCompilerPlugin) id("datadogBuildConfig") } @@ -51,16 +52,6 @@ android { unitTests.isReturnDefaultValues = true } - sourceSets.named("main") { - java.srcDir("src/main/kotlin") - } - sourceSets.named("test") { - java.srcDir("src/test/kotlin") - } - sourceSets.named("androidTest") { - java.srcDir("src/androidTest/kotlin") - } - compileOptions { java17() if (project.hasProperty(com.datadog.gradle.Properties.USE_DESUGARING)) { @@ -116,6 +107,10 @@ dependencies { implementation(libs.gson) implementation(libs.kotlin) + // androidTest network wrappers use kotlinx.coroutines; declared on main so consistent + // resolution aligns the whole module to the SDK's coroutines version (vs the old 1.6.4 + // pulled transitively via androidx.test), and it lands on the androidTest compile classpath. + implementation(libs.coroutinesCore) implementation(libs.bundles.androidXSupportBase) implementation(libs.elmyr) implementation(libs.leakCanaryAndroid) diff --git a/instrumented/integration/src/androidTest/kotlin/com/datadog/android/sdk/integration/network/tests/NetworkInstrumentationComparisonTest.kt b/instrumented/integration/src/androidTest/kotlin/com/datadog/android/sdk/integration/network/tests/NetworkInstrumentationComparisonTest.kt index 3bce8e460e..35de5d7196 100644 --- a/instrumented/integration/src/androidTest/kotlin/com/datadog/android/sdk/integration/network/tests/NetworkInstrumentationComparisonTest.kt +++ b/instrumented/integration/src/androidTest/kotlin/com/datadog/android/sdk/integration/network/tests/NetworkInstrumentationComparisonTest.kt @@ -105,7 +105,7 @@ internal class NetworkInstrumentationComparisonTest { // Given val method = networkRule.forge.anElementFrom(ALLOWED_METHODS) // https://github.com/RunningTheSnail/Okhttp/blob/84cbd2dae1708967c3b1bd71ccecdecefd6354f5/okhttp/src/main/java/okhttp3/internal/http/RetryAndFollowUpInterceptor.java#L318 - // OkHttp will retry on 408 status code by default, se we excluding this code here + // OkHttp will retry on 408 status code by default, se we exclude this code here val error = networkRule.forge.anElementFrom( HttpSpec.StatusCode.clientErrors(HttpSpec.StatusCode.REQUEST_TIMEOUT) ) diff --git a/integrations/dd-sdk-android-apollo/api/compiler-meta.txt b/integrations/dd-sdk-android-apollo/api/compiler-meta.txt index a098d10949..f757d782cb 100644 --- a/integrations/dd-sdk-android-apollo/api/compiler-meta.txt +++ b/integrations/dd-sdk-android-apollo/api/compiler-meta.txt @@ -1,2 +1,2 @@ -kotlin_abi_version=2.0.0 -jvm_bytecode_version=11 +kotlin_abi_version=2.1.0 +jvm_bytecode_version=17 diff --git a/integrations/dd-sdk-android-apollo/build.gradle.kts b/integrations/dd-sdk-android-apollo/build.gradle.kts index 62aeaad12b..d7edd06c61 100644 --- a/integrations/dd-sdk-android-apollo/build.gradle.kts +++ b/integrations/dd-sdk-android-apollo/build.gradle.kts @@ -4,15 +4,14 @@ * Copyright 2016-Present Datadog, Inc. */ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("datadogBuildConfig") // Publish @@ -65,7 +64,7 @@ unMock { } datadogBuild { - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyJunitConfig() applyAndroidLibraryConfig() applyPublishingConfig( diff --git a/integrations/dd-sdk-android-apollo/transitiveDependencies b/integrations/dd-sdk-android-apollo/transitiveDependencies index f0e2dd3307..34cd1d4b5c 100644 --- a/integrations/dd-sdk-android-apollo/transitiveDependencies +++ b/integrations/dd-sdk-android-apollo/transitiveDependencies @@ -1,15 +1,15 @@ Dependencies List -com.apollographql.apollo:apollo-annotations-jvm:4.3.3 : 6 Kb -com.apollographql.apollo:apollo-api-jvm:4.3.3 : 330 Kb -com.apollographql.apollo:apollo-mpp-utils-jvm:4.3.3 : 1477 b -com.apollographql.apollo:apollo-runtime-android:4.3.3 : 379 Kb +com.apollographql.apollo:apollo-annotations-jvm:4.4.3 : 6 Kb +com.apollographql.apollo:apollo-api-jvm:4.4.3 : 330 Kb +com.apollographql.apollo:apollo-mpp-utils-jvm:4.4.3 : 1477 b +com.apollographql.apollo:apollo-runtime-android:4.4.3 : 383 Kb com.benasher44:uuid-jvm:0.8.2 : 8 Kb com.squareup.okhttp3:okhttp:4.12.0 : 771 Kb com.squareup.okio:okio-jvm:3.9.0 : 363 Kb org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.21 : 963 b org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.21 : 968 b -org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb +org.jetbrains.kotlin:kotlin-stdlib:2.3.20 : 1762 Kb org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.9.0 : 1429 Kb org.jetbrains:annotations:23.0.0 : 28 Kb diff --git a/integrations/dd-sdk-android-coil/api/compiler-meta.txt b/integrations/dd-sdk-android-coil/api/compiler-meta.txt index a098d10949..f757d782cb 100644 --- a/integrations/dd-sdk-android-coil/api/compiler-meta.txt +++ b/integrations/dd-sdk-android-coil/api/compiler-meta.txt @@ -1,2 +1,2 @@ -kotlin_abi_version=2.0.0 -jvm_bytecode_version=11 +kotlin_abi_version=2.1.0 +jvm_bytecode_version=17 diff --git a/integrations/dd-sdk-android-coil/build.gradle.kts b/integrations/dd-sdk-android-coil/build.gradle.kts index fd55fff33b..65ac18cb88 100644 --- a/integrations/dd-sdk-android-coil/build.gradle.kts +++ b/integrations/dd-sdk-android-coil/build.gradle.kts @@ -4,15 +4,14 @@ * Copyright 2016-Present Datadog, Inc. */ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("datadogBuildConfig") // Publish @@ -60,7 +59,7 @@ dependencies { } datadogBuild { - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyAndroidLibraryConfig() applyJunitConfig() applyJavadocConfig() diff --git a/integrations/dd-sdk-android-coil/transitiveDependencies b/integrations/dd-sdk-android-coil/transitiveDependencies index ca3c200f71..2448bfbab6 100644 --- a/integrations/dd-sdk-android-coil/transitiveDependencies +++ b/integrations/dd-sdk-android-coil/transitiveDependencies @@ -9,7 +9,7 @@ io.coil-kt:coil-base:1.0.0 : 395 Kb io.coil-kt:coil:1.0.0 : 15 Kb org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.10 : 959 b org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10 : 965 b -org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb +org.jetbrains.kotlin:kotlin-stdlib:2.3.20 : 1762 Kb org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9 : 19 Kb org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.3.9 : 1629 Kb org.jetbrains:annotations:13.0 : 17 Kb diff --git a/integrations/dd-sdk-android-coil3/api/compiler-meta.txt b/integrations/dd-sdk-android-coil3/api/compiler-meta.txt index a098d10949..f757d782cb 100644 --- a/integrations/dd-sdk-android-coil3/api/compiler-meta.txt +++ b/integrations/dd-sdk-android-coil3/api/compiler-meta.txt @@ -1,2 +1,2 @@ -kotlin_abi_version=2.0.0 -jvm_bytecode_version=11 +kotlin_abi_version=2.1.0 +jvm_bytecode_version=17 diff --git a/integrations/dd-sdk-android-coil3/build.gradle.kts b/integrations/dd-sdk-android-coil3/build.gradle.kts index 4980e54251..b2569fb288 100644 --- a/integrations/dd-sdk-android-coil3/build.gradle.kts +++ b/integrations/dd-sdk-android-coil3/build.gradle.kts @@ -4,15 +4,14 @@ * Copyright 2016-Present Datadog, Inc. */ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("datadogBuildConfig") // Publish @@ -61,7 +60,7 @@ dependencies { } datadogBuild { - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyAndroidLibraryConfig() applyJunitConfig() applyJavadocConfig() diff --git a/integrations/dd-sdk-android-coil3/transitiveDependencies b/integrations/dd-sdk-android-coil3/transitiveDependencies index f4f75f8edd..bb3235468c 100644 --- a/integrations/dd-sdk-android-coil3/transitiveDependencies +++ b/integrations/dd-sdk-android-coil3/transitiveDependencies @@ -12,7 +12,7 @@ io.coil-kt.coil3:coil-network-core-android:3.0.4 : 58 Kb io.coil-kt.coil3:coil-network-okhttp-jvm:3.0.4 : 19 Kb org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.21 : 963 b org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.21 : 968 b -org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb +org.jetbrains.kotlin:kotlin-stdlib:2.3.20 : 1762 Kb org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0 : 18 Kb org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.9.0 : 1429 Kb org.jetbrains:annotations:23.0.0 : 28 Kb diff --git a/integrations/dd-sdk-android-compose/api/compiler-meta.txt b/integrations/dd-sdk-android-compose/api/compiler-meta.txt index a098d10949..f757d782cb 100644 --- a/integrations/dd-sdk-android-compose/api/compiler-meta.txt +++ b/integrations/dd-sdk-android-compose/api/compiler-meta.txt @@ -1,2 +1,2 @@ -kotlin_abi_version=2.0.0 -jvm_bytecode_version=11 +kotlin_abi_version=2.1.0 +jvm_bytecode_version=17 diff --git a/integrations/dd-sdk-android-compose/build.gradle.kts b/integrations/dd-sdk-android-compose/build.gradle.kts index 3fdaa27643..464b6e4c05 100644 --- a/integrations/dd-sdk-android-compose/build.gradle.kts +++ b/integrations/dd-sdk-android-compose/build.gradle.kts @@ -5,16 +5,16 @@ */ import com.datadog.gradle.config.taskConfig -import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") alias(libs.plugins.composeCompilerPlugin) id("datadogBuildConfig") @@ -80,7 +80,7 @@ unMock { } datadogBuild { - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyAndroidLibraryConfig() applyJunitConfig() applyJavadocConfig() diff --git a/integrations/dd-sdk-android-compose/src/main/kotlin/com/datadog/android/compose/internal/InternalInteractionTracking.kt b/integrations/dd-sdk-android-compose/src/main/kotlin/com/datadog/android/compose/internal/InternalInteractionTracking.kt index 0ea33ec073..da81f4ad03 100644 --- a/integrations/dd-sdk-android-compose/src/main/kotlin/com/datadog/android/compose/internal/InternalInteractionTracking.kt +++ b/integrations/dd-sdk-android-compose/src/main/kotlin/com/datadog/android/compose/internal/InternalInteractionTracking.kt @@ -30,7 +30,7 @@ internal suspend fun trackSwipe( isRtl: Boolean, attributes: Map ) { - trackDragInteraction( + trackDragInteraction( interactionSource, onStart = { interactions, start -> interactions[start] = SwipeStartProps( @@ -85,6 +85,7 @@ internal suspend fun trackScroll( ) } +@Suppress("ThrowingInternalException") internal suspend fun trackDragInteraction( interactionSource: InteractionSource, onStart: ( @@ -126,7 +127,6 @@ internal suspend fun trackDragInteraction( } } } catch (ce: CancellationException) { - @Suppress("ThrowingInternalException") throw ce } catch (@Suppress("TooGenericExceptionCaught") e: Exception) { Log.e(LOG_TAG, "Exception during drag interactions tracking", e) diff --git a/integrations/dd-sdk-android-compose/src/test/kotlin/ComposeActionTrackingStrategyTest.kt b/integrations/dd-sdk-android-compose/src/test/kotlin/ComposeActionTrackingStrategyTest.kt index 839e307407..a6694ebd6b 100644 --- a/integrations/dd-sdk-android-compose/src/test/kotlin/ComposeActionTrackingStrategyTest.kt +++ b/integrations/dd-sdk-android-compose/src/test/kotlin/ComposeActionTrackingStrategyTest.kt @@ -9,6 +9,7 @@ package com.datadog.android.compose import android.view.View +import androidx.compose.runtime.collection.MutableVector import androidx.compose.runtime.collection.mutableVectorOf import androidx.compose.ui.geometry.Rect import androidx.compose.ui.node.LayoutNode @@ -293,14 +294,22 @@ class ComposeActionTrackingStrategyTest { ) ) } - whenever(nodeList[0].zSortedChildren) doReturn mutableVectorOf(nodeList[1], nodeList[2]) - whenever(nodeList[1].zSortedChildren) doReturn mutableVectorOf(nodeList[3], nodeList[4]) - whenever(nodeList[2].zSortedChildren) doReturn mutableVectorOf() - whenever(nodeList[3].zSortedChildren) doReturn mutableVectorOf() - whenever(nodeList[4].zSortedChildren) doReturn mutableVectorOf() + val children0: MutableVector = childrenOf(nodeList[1], nodeList[2]) + val children1: MutableVector = childrenOf(nodeList[3], nodeList[4]) + val children2: MutableVector = childrenOf(*emptyArray()) + val children3: MutableVector = childrenOf(*emptyArray()) + val children4: MutableVector = childrenOf(*emptyArray()) + whenever(nodeList[0].zSortedChildren) doReturn children0 + whenever(nodeList[1].zSortedChildren) doReturn children1 + whenever(nodeList[2].zSortedChildren) doReturn children2 + whenever(nodeList[3].zSortedChildren) doReturn children3 + whenever(nodeList[4].zSortedChildren) doReturn children4 whenever(mockAndroidComposeView.root) doReturn nodeList[0] } + private fun childrenOf(vararg nodes: LayoutNode): MutableVector = + mutableVectorOf(*nodes) + private fun mockLayoutNode( forge: Forge, tagName: String, diff --git a/integrations/dd-sdk-android-compose/transitiveDependencies b/integrations/dd-sdk-android-compose/transitiveDependencies index 5e590c64c4..524c2bf371 100644 --- a/integrations/dd-sdk-android-compose/transitiveDependencies +++ b/integrations/dd-sdk-android-compose/transitiveDependencies @@ -42,7 +42,7 @@ androidx.savedstate:savedstate:1.2.1 : 19 Kb androidx.versionedparcelable:versionedparcelable:1.1.1 : 30 Kb org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.0 : 963 b org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.0 : 968 b -org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb +org.jetbrains.kotlin:kotlin-stdlib:2.3.20 : 1762 Kb org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4 : 19 Kb org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.6.4 : 1442 Kb org.jetbrains:annotations:13.0 : 17 Kb diff --git a/integrations/dd-sdk-android-cronet/api/compiler-meta.txt b/integrations/dd-sdk-android-cronet/api/compiler-meta.txt index a098d10949..f757d782cb 100644 --- a/integrations/dd-sdk-android-cronet/api/compiler-meta.txt +++ b/integrations/dd-sdk-android-cronet/api/compiler-meta.txt @@ -1,2 +1,2 @@ -kotlin_abi_version=2.0.0 -jvm_bytecode_version=11 +kotlin_abi_version=2.1.0 +jvm_bytecode_version=17 diff --git a/integrations/dd-sdk-android-cronet/build.gradle.kts b/integrations/dd-sdk-android-cronet/build.gradle.kts index 80e1e34e0e..b3901a1294 100644 --- a/integrations/dd-sdk-android-cronet/build.gradle.kts +++ b/integrations/dd-sdk-android-cronet/build.gradle.kts @@ -4,15 +4,14 @@ * Copyright 2016-Present Datadog, Inc. */ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("datadogBuildConfig") // Publish @@ -81,7 +80,7 @@ unMock { } datadogBuild { - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyAndroidLibraryConfig() applyJunitConfig() applyJavadocConfig() diff --git a/integrations/dd-sdk-android-cronet/src/test/kotlin/com/datadog/android/cronet/internal/CronetUrlRequestTest.kt b/integrations/dd-sdk-android-cronet/src/test/kotlin/com/datadog/android/cronet/internal/CronetUrlRequestTest.kt index 5fc9d50c66..0f0233f911 100644 --- a/integrations/dd-sdk-android-cronet/src/test/kotlin/com/datadog/android/cronet/internal/CronetUrlRequestTest.kt +++ b/integrations/dd-sdk-android-cronet/src/test/kotlin/com/datadog/android/cronet/internal/CronetUrlRequestTest.kt @@ -16,7 +16,7 @@ import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.annotation.BoolForgery import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension -import org.assertj.core.api.AssertionsForInterfaceTypes.assertThat +import org.assertj.core.api.Assertions.assertThat import org.chromium.net.UrlRequest import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test diff --git a/integrations/dd-sdk-android-cronet/transitiveDependencies b/integrations/dd-sdk-android-cronet/transitiveDependencies index 06e5cdfa40..41651cc76c 100644 --- a/integrations/dd-sdk-android-cronet/transitiveDependencies +++ b/integrations/dd-sdk-android-cronet/transitiveDependencies @@ -3,8 +3,8 @@ Dependencies List androidx.annotation:annotation-jvm:1.9.1 : 59 Kb org.chromium.net:cronet-api:143.7445.0 : 82 Kb org.chromium.net:cronet-shared:143.7445.0 : 97 Kb -org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb +org.jetbrains.kotlin:kotlin-stdlib:2.3.20 : 1762 Kb org.jetbrains:annotations:13.0 : 17 Kb -Total transitive dependencies size : 1962 Kb +Total transitive dependencies size : 2018 Kb diff --git a/integrations/dd-sdk-android-fresco/api/compiler-meta.txt b/integrations/dd-sdk-android-fresco/api/compiler-meta.txt index a098d10949..f757d782cb 100644 --- a/integrations/dd-sdk-android-fresco/api/compiler-meta.txt +++ b/integrations/dd-sdk-android-fresco/api/compiler-meta.txt @@ -1,2 +1,2 @@ -kotlin_abi_version=2.0.0 -jvm_bytecode_version=11 +kotlin_abi_version=2.1.0 +jvm_bytecode_version=17 diff --git a/integrations/dd-sdk-android-fresco/build.gradle.kts b/integrations/dd-sdk-android-fresco/build.gradle.kts index 0c398a1178..1f9bd50fe1 100644 --- a/integrations/dd-sdk-android-fresco/build.gradle.kts +++ b/integrations/dd-sdk-android-fresco/build.gradle.kts @@ -4,15 +4,14 @@ * Copyright 2016-Present Datadog, Inc. */ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("datadogBuildConfig") // Publish @@ -60,7 +59,7 @@ dependencies { } datadogBuild { - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyAndroidLibraryConfig() applyJunitConfig() applyJavadocConfig() diff --git a/integrations/dd-sdk-android-fresco/transitiveDependencies b/integrations/dd-sdk-android-fresco/transitiveDependencies index d5e5f05e68..716a53d44d 100644 --- a/integrations/dd-sdk-android-fresco/transitiveDependencies +++ b/integrations/dd-sdk-android-fresco/transitiveDependencies @@ -16,7 +16,7 @@ com.squareup.okhttp3:okhttp:4.12.0 : 771 Kb com.squareup.okio:okio-jvm:3.6.0 : 351 Kb org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.10 : 959 b org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10 : 965 b -org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb +org.jetbrains.kotlin:kotlin-stdlib:2.3.20 : 1762 Kb org.jetbrains:annotations:13.0 : 17 Kb Total transitive dependencies size : 4 Mb diff --git a/integrations/dd-sdk-android-glide/api/compiler-meta.txt b/integrations/dd-sdk-android-glide/api/compiler-meta.txt index a098d10949..f757d782cb 100644 --- a/integrations/dd-sdk-android-glide/api/compiler-meta.txt +++ b/integrations/dd-sdk-android-glide/api/compiler-meta.txt @@ -1,2 +1,2 @@ -kotlin_abi_version=2.0.0 -jvm_bytecode_version=11 +kotlin_abi_version=2.1.0 +jvm_bytecode_version=17 diff --git a/integrations/dd-sdk-android-glide/build.gradle.kts b/integrations/dd-sdk-android-glide/build.gradle.kts index 58cfc9b72e..4f6f4ed877 100644 --- a/integrations/dd-sdk-android-glide/build.gradle.kts +++ b/integrations/dd-sdk-android-glide/build.gradle.kts @@ -4,15 +4,14 @@ * Copyright 2016-Present Datadog, Inc. */ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("datadogBuildConfig") // Publish @@ -62,7 +61,7 @@ dependencies { } datadogBuild { - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyAndroidLibraryConfig() applyJunitConfig() applyJavadocConfig() diff --git a/integrations/dd-sdk-android-glide/transitiveDependencies b/integrations/dd-sdk-android-glide/transitiveDependencies index b91cd13465..81132f48fa 100644 --- a/integrations/dd-sdk-android-glide/transitiveDependencies +++ b/integrations/dd-sdk-android-glide/transitiveDependencies @@ -34,7 +34,7 @@ com.squareup.okhttp3:okhttp:4.12.0 : 771 Kb com.squareup.okio:okio-jvm:3.6.0 : 351 Kb org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.10 : 959 b org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10 : 965 b -org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb +org.jetbrains.kotlin:kotlin-stdlib:2.3.20 : 1762 Kb org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4 : 19 Kb org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.6.4 : 1442 Kb org.jetbrains:annotations:13.0 : 17 Kb diff --git a/integrations/dd-sdk-android-okhttp-otel/api/compiler-meta.txt b/integrations/dd-sdk-android-okhttp-otel/api/compiler-meta.txt index a098d10949..f757d782cb 100644 --- a/integrations/dd-sdk-android-okhttp-otel/api/compiler-meta.txt +++ b/integrations/dd-sdk-android-okhttp-otel/api/compiler-meta.txt @@ -1,2 +1,2 @@ -kotlin_abi_version=2.0.0 -jvm_bytecode_version=11 +kotlin_abi_version=2.1.0 +jvm_bytecode_version=17 diff --git a/integrations/dd-sdk-android-okhttp-otel/build.gradle.kts b/integrations/dd-sdk-android-okhttp-otel/build.gradle.kts index d03b0e977c..bb61ded3df 100644 --- a/integrations/dd-sdk-android-okhttp-otel/build.gradle.kts +++ b/integrations/dd-sdk-android-okhttp-otel/build.gradle.kts @@ -4,15 +4,14 @@ * Copyright 2016-Present Datadog, Inc. */ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("datadogBuildConfig") // Publish @@ -62,7 +61,7 @@ dependencies { } datadogBuild { - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyAndroidLibraryConfig() applyJunitConfig() applyJavadocConfig() diff --git a/integrations/dd-sdk-android-okhttp-otel/transitiveDependencies b/integrations/dd-sdk-android-okhttp-otel/transitiveDependencies index 127e86c4fa..338601b53a 100644 --- a/integrations/dd-sdk-android-okhttp-otel/transitiveDependencies +++ b/integrations/dd-sdk-android-okhttp-otel/transitiveDependencies @@ -6,8 +6,8 @@ io.opentelemetry:opentelemetry-api:1.40.0 : 138 Kb io.opentelemetry:opentelemetry-context:1.40.0 : 46 Kb org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.10 : 959 b org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10 : 965 b -org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb +org.jetbrains.kotlin:kotlin-stdlib:2.3.20 : 1762 Kb org.jetbrains:annotations:13.0 : 17 Kb -Total transitive dependencies size : 2 Mb +Total transitive dependencies size : 3 Mb diff --git a/integrations/dd-sdk-android-okhttp/api/compiler-meta.txt b/integrations/dd-sdk-android-okhttp/api/compiler-meta.txt index a098d10949..f757d782cb 100644 --- a/integrations/dd-sdk-android-okhttp/api/compiler-meta.txt +++ b/integrations/dd-sdk-android-okhttp/api/compiler-meta.txt @@ -1,2 +1,2 @@ -kotlin_abi_version=2.0.0 -jvm_bytecode_version=11 +kotlin_abi_version=2.1.0 +jvm_bytecode_version=17 diff --git a/integrations/dd-sdk-android-okhttp/build.gradle.kts b/integrations/dd-sdk-android-okhttp/build.gradle.kts index 1fed1bcca6..4b1e711ec2 100644 --- a/integrations/dd-sdk-android-okhttp/build.gradle.kts +++ b/integrations/dd-sdk-android-okhttp/build.gradle.kts @@ -6,15 +6,14 @@ @file:Suppress("StringLiteralDuplication") -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("com.google.devtools.ksp") id("datadogBuildConfig") @@ -80,7 +79,7 @@ unMock { } datadogBuild { - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyAndroidLibraryConfig() applyJunitConfig() applyJavadocConfig() diff --git a/integrations/dd-sdk-android-okhttp/src/main/kotlin/com/datadog/android/okhttp/trace/TracingInterceptor.kt b/integrations/dd-sdk-android-okhttp/src/main/kotlin/com/datadog/android/okhttp/trace/TracingInterceptor.kt index 8e50adec9a..9c33c84334 100644 --- a/integrations/dd-sdk-android-okhttp/src/main/kotlin/com/datadog/android/okhttp/trace/TracingInterceptor.kt +++ b/integrations/dd-sdk-android-okhttp/src/main/kotlin/com/datadog/android/okhttp/trace/TracingInterceptor.kt @@ -50,6 +50,7 @@ import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response import java.net.HttpURLConnection +import java.util.Locale import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicReference import com.datadog.android.okhttp.TraceContext as DeprecatedTraceContext @@ -587,6 +588,7 @@ internal constructor( W3C_TRACEPARENT_KEY, @Suppress("UnsafeThirdPartyFunctionCall") // Format string is static W3C_TRACEPARENT_DROP_SAMPLING_DECISION.format( + Locale.US, traceId.padStart(length = W3C_TRACE_ID_LENGTH, padChar = '0'), spanId.padStart(length = W3C_PARENT_ID_LENGTH, padChar = '0') ) @@ -594,7 +596,7 @@ internal constructor( // TODO RUM-2121 3rd party vendor information will be erased @Suppress("UnsafeThirdPartyFunctionCall") // Format string is static var traceStateHeader = W3C_TRACESTATE_DROP_SAMPLING_DECISION - .format(spanId.padStart(length = W3C_PARENT_ID_LENGTH, padChar = '0')) + .format(Locale.US, spanId.padStart(length = W3C_PARENT_ID_LENGTH, padChar = '0')) if (traceOrigin != null) { traceStateHeader += ";o:$traceOrigin" } diff --git a/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/trace/TracingInterceptorTestsExt.kt b/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/trace/TracingInterceptorTestsExt.kt index bea3d68f53..933c4fe750 100644 --- a/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/trace/TracingInterceptorTestsExt.kt +++ b/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/trace/TracingInterceptorTestsExt.kt @@ -37,7 +37,7 @@ internal fun DatadogPropagation.wheneverInjectThenThrow(throwable: Throwable) { internal fun DatadogPropagation.wheneverInjectThenValueToHeaders(key: String, value: String) { doAnswer { invocation -> val carrier = invocation.getArgument(1) - val setter = invocation.getArgument<(carrier: Request.Builder, key: String, value: String) -> Unit>(2) + val setter = invocation.getArgument<(Request.Builder, String, String) -> Unit>(2) setter.invoke(carrier, key, value) } .whenever(this) diff --git a/integrations/dd-sdk-android-okhttp/transitiveDependencies b/integrations/dd-sdk-android-okhttp/transitiveDependencies index 37e7b3fcfb..d271ae1c2c 100644 --- a/integrations/dd-sdk-android-okhttp/transitiveDependencies +++ b/integrations/dd-sdk-android-okhttp/transitiveDependencies @@ -5,7 +5,7 @@ com.squareup.okhttp3:okhttp:4.12.0 : 771 Kb com.squareup.okio:okio-jvm:3.6.0 : 351 Kb org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.10 : 959 b org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10 : 965 b -org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb +org.jetbrains.kotlin:kotlin-stdlib:2.3.20 : 1762 Kb org.jetbrains:annotations:13.0 : 17 Kb Total transitive dependencies size : 2 Mb diff --git a/integrations/dd-sdk-android-rum-coroutines/api/compiler-meta.txt b/integrations/dd-sdk-android-rum-coroutines/api/compiler-meta.txt index a098d10949..f757d782cb 100644 --- a/integrations/dd-sdk-android-rum-coroutines/api/compiler-meta.txt +++ b/integrations/dd-sdk-android-rum-coroutines/api/compiler-meta.txt @@ -1,2 +1,2 @@ -kotlin_abi_version=2.0.0 -jvm_bytecode_version=11 +kotlin_abi_version=2.1.0 +jvm_bytecode_version=17 diff --git a/integrations/dd-sdk-android-rum-coroutines/build.gradle.kts b/integrations/dd-sdk-android-rum-coroutines/build.gradle.kts index ee1664b87c..91df9623c6 100644 --- a/integrations/dd-sdk-android-rum-coroutines/build.gradle.kts +++ b/integrations/dd-sdk-android-rum-coroutines/build.gradle.kts @@ -4,15 +4,14 @@ * Copyright 2016-Present Datadog, Inc. */ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("datadogBuildConfig") // Publish @@ -58,7 +57,7 @@ dependencies { } datadogBuild { - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyAndroidLibraryConfig() applyJunitConfig() applyJavadocConfig() diff --git a/integrations/dd-sdk-android-rum-coroutines/src/main/kotlin/com/datadog/android/rum/coroutines/FlowExt.kt b/integrations/dd-sdk-android-rum-coroutines/src/main/kotlin/com/datadog/android/rum/coroutines/FlowExt.kt index 9b39cfa8c0..5ccb05fa00 100644 --- a/integrations/dd-sdk-android-rum-coroutines/src/main/kotlin/com/datadog/android/rum/coroutines/FlowExt.kt +++ b/integrations/dd-sdk-android-rum-coroutines/src/main/kotlin/com/datadog/android/rum/coroutines/FlowExt.kt @@ -25,7 +25,7 @@ internal const val ERROR_FLOW: String = "Coroutine Flow error" * * @return the new [Flow] instance. */ -@Suppress("TooGenericExceptionCaught") +@Suppress("TooGenericExceptionCaught", "SuspendFunSwallowedCancellation") fun Flow.sendErrorToDatadog(sdkCore: SdkCore = Datadog.getInstance()): Flow { return flow { try { diff --git a/integrations/dd-sdk-android-rum-coroutines/transitiveDependencies b/integrations/dd-sdk-android-rum-coroutines/transitiveDependencies index ef1c5009ab..286c966fdf 100644 --- a/integrations/dd-sdk-android-rum-coroutines/transitiveDependencies +++ b/integrations/dd-sdk-android-rum-coroutines/transitiveDependencies @@ -1,9 +1,7 @@ Dependencies List -org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.20 : 963 b -org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.20 : 969 b -org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb -org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.3 : 1514 Kb +org.jetbrains.kotlin:kotlin-stdlib:2.3.20 : 1762 Kb +org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.10.2 : 1444 Kb org.jetbrains:annotations:23.0.0 : 28 Kb Total transitive dependencies size : 3 Mb diff --git a/integrations/dd-sdk-android-rx/api/compiler-meta.txt b/integrations/dd-sdk-android-rx/api/compiler-meta.txt index a098d10949..f757d782cb 100644 --- a/integrations/dd-sdk-android-rx/api/compiler-meta.txt +++ b/integrations/dd-sdk-android-rx/api/compiler-meta.txt @@ -1,2 +1,2 @@ -kotlin_abi_version=2.0.0 -jvm_bytecode_version=11 +kotlin_abi_version=2.1.0 +jvm_bytecode_version=17 diff --git a/integrations/dd-sdk-android-rx/build.gradle.kts b/integrations/dd-sdk-android-rx/build.gradle.kts index ee935726c2..74caf5a82e 100644 --- a/integrations/dd-sdk-android-rx/build.gradle.kts +++ b/integrations/dd-sdk-android-rx/build.gradle.kts @@ -4,15 +4,14 @@ * Copyright 2016-Present Datadog, Inc. */ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("datadogBuildConfig") // Publish @@ -60,7 +59,7 @@ dependencies { } datadogBuild { - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyAndroidLibraryConfig() applyJunitConfig() applyJavadocConfig() diff --git a/integrations/dd-sdk-android-rx/transitiveDependencies b/integrations/dd-sdk-android-rx/transitiveDependencies index a0586aba0e..1f9a84b04b 100644 --- a/integrations/dd-sdk-android-rx/transitiveDependencies +++ b/integrations/dd-sdk-android-rx/transitiveDependencies @@ -5,7 +5,7 @@ com.squareup.okio:okio-jvm:3.6.0 : 351 Kb io.reactivex.rxjava3:rxjava:3.0.0 : 2 Mb org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.10 : 959 b org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10 : 965 b -org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb +org.jetbrains.kotlin:kotlin-stdlib:2.3.20 : 1762 Kb org.jetbrains:annotations:13.0 : 17 Kb org.reactivestreams:reactive-streams:1.0.3 : 11 Kb diff --git a/integrations/dd-sdk-android-sqldelight/api/apiSurface b/integrations/dd-sdk-android-sqldelight/api/apiSurface index 80118056fb..03f4dfe7c7 100644 --- a/integrations/dd-sdk-android-sqldelight/api/apiSurface +++ b/integrations/dd-sdk-android-sqldelight/api/apiSurface @@ -1,7 +1,7 @@ -class com.datadog.android.sqldelight.DatadogSqliteCallback : com.squareup.sqldelight.android.AndroidSqliteDriver.Callback - constructor(com.squareup.sqldelight.db.SqlDriver.Schema, com.datadog.android.api.SdkCore = Datadog.getInstance()) +class com.datadog.android.sqldelight.DatadogSqliteCallback : app.cash.sqldelight.driver.android.AndroidSqliteDriver.Callback + constructor(app.cash.sqldelight.db.SqlSchema>, com.datadog.android.api.SdkCore = Datadog.getInstance()) override fun onCorruption(androidx.sqlite.db.SupportSQLiteDatabase) -fun T.transactionTraced(String, Boolean = false, TransactionWithSpanAndWithoutReturn.() -> Unit) -fun T.transactionTracedWithResult(String, Boolean = false, TransactionWithSpanAndWithReturn.() -> R): R -interface com.datadog.android.sqldelight.TransactionWithSpanAndWithReturn : com.squareup.sqldelight.TransactionWithReturn, com.datadog.android.trace.api.span.DatadogSpan -interface com.datadog.android.sqldelight.TransactionWithSpanAndWithoutReturn : com.squareup.sqldelight.TransactionWithoutReturn, com.datadog.android.trace.api.span.DatadogSpan +fun T.transactionTraced(String, Boolean = false, TransactionWithSpanAndWithoutReturn.() -> Unit) +fun T.transactionTracedWithResult(String, Boolean = false, TransactionWithSpanAndWithReturn.() -> R): R +interface com.datadog.android.sqldelight.TransactionWithSpanAndWithReturn : app.cash.sqldelight.TransactionWithReturn, com.datadog.android.trace.api.span.DatadogSpan +interface com.datadog.android.sqldelight.TransactionWithSpanAndWithoutReturn : app.cash.sqldelight.TransactionWithoutReturn, com.datadog.android.trace.api.span.DatadogSpan diff --git a/integrations/dd-sdk-android-sqldelight/api/compiler-meta.txt b/integrations/dd-sdk-android-sqldelight/api/compiler-meta.txt index a098d10949..f757d782cb 100644 --- a/integrations/dd-sdk-android-sqldelight/api/compiler-meta.txt +++ b/integrations/dd-sdk-android-sqldelight/api/compiler-meta.txt @@ -1,2 +1,2 @@ -kotlin_abi_version=2.0.0 -jvm_bytecode_version=11 +kotlin_abi_version=2.1.0 +jvm_bytecode_version=17 diff --git a/integrations/dd-sdk-android-sqldelight/build.gradle.kts b/integrations/dd-sdk-android-sqldelight/build.gradle.kts index d8de3718a0..b2be68ef03 100644 --- a/integrations/dd-sdk-android-sqldelight/build.gradle.kts +++ b/integrations/dd-sdk-android-sqldelight/build.gradle.kts @@ -4,15 +4,14 @@ * Copyright 2016-Present Datadog, Inc. */ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("datadogBuildConfig") // Publish @@ -62,7 +61,7 @@ dependencies { } datadogBuild { - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyAndroidLibraryConfig() applyJunitConfig() applyJavadocConfig() diff --git a/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/DatadogSqliteCallback.kt b/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/DatadogSqliteCallback.kt index c8983a5b08..ad94b94141 100644 --- a/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/DatadogSqliteCallback.kt +++ b/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/DatadogSqliteCallback.kt @@ -7,13 +7,14 @@ package com.datadog.android.sqldelight import androidx.sqlite.db.SupportSQLiteDatabase +import app.cash.sqldelight.db.QueryResult +import app.cash.sqldelight.db.SqlSchema +import app.cash.sqldelight.driver.android.AndroidSqliteDriver import com.datadog.android.Datadog import com.datadog.android.api.SdkCore import com.datadog.android.rum.GlobalRumMonitor import com.datadog.android.rum.RumAttributes import com.datadog.android.rum.RumErrorSource -import com.squareup.sqldelight.android.AndroidSqliteDriver -import com.squareup.sqldelight.db.SqlDriver import java.util.Locale /** @@ -28,7 +29,7 @@ import java.util.Locale class DatadogSqliteCallback @JvmOverloads constructor( - schema: SqlDriver.Schema, + schema: SqlSchema>, private val sdkCore: SdkCore = Datadog.getInstance() ) : AndroidSqliteDriver.Callback(schema) { diff --git a/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/SqlDelightExt.kt b/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/SqlDelightExt.kt index 77272fc420..531ea7af2a 100644 --- a/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/SqlDelightExt.kt +++ b/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/SqlDelightExt.kt @@ -6,12 +6,11 @@ package com.datadog.android.sqldelight +import app.cash.sqldelight.Transacter import com.datadog.android.sqldelight.internal.TransactionWithSpanAndWithReturnImpl import com.datadog.android.sqldelight.internal.TransactionWithSpanAndWithoutReturnImpl import com.datadog.android.sqldelight.internal.withinSpan import com.datadog.android.trace.GlobalDatadogTracer -import com.squareup.sqldelight.Transacter -import com.squareup.sqldelight.Transacter.Transaction /** * Starts a [Transaction] and runs [body] in that transaction. diff --git a/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/TransactionWithSpanAndWithReturn.kt b/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/TransactionWithSpanAndWithReturn.kt index a3f96bc027..4b40e9773d 100644 --- a/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/TransactionWithSpanAndWithReturn.kt +++ b/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/TransactionWithSpanAndWithReturn.kt @@ -6,8 +6,8 @@ package com.datadog.android.sqldelight +import app.cash.sqldelight.TransactionWithReturn import com.datadog.android.trace.api.span.DatadogSpan -import com.squareup.sqldelight.TransactionWithReturn /** * An object that implements both [DatadogSpan] and [TransactionWithReturn]. diff --git a/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/TransactionWithSpanAndWithoutReturn.kt b/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/TransactionWithSpanAndWithoutReturn.kt index 9297e53ad9..c3619c3f18 100644 --- a/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/TransactionWithSpanAndWithoutReturn.kt +++ b/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/TransactionWithSpanAndWithoutReturn.kt @@ -6,8 +6,8 @@ package com.datadog.android.sqldelight +import app.cash.sqldelight.TransactionWithoutReturn import com.datadog.android.trace.api.span.DatadogSpan -import com.squareup.sqldelight.TransactionWithoutReturn /** * An object that implements both [DatadogSpan] and [TransactionWithoutReturn]. diff --git a/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/internal/TransactionWithSpanAndWithReturnImpl.kt b/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/internal/TransactionWithSpanAndWithReturnImpl.kt index c727acc3e8..03d278fa7f 100644 --- a/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/internal/TransactionWithSpanAndWithReturnImpl.kt +++ b/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/internal/TransactionWithSpanAndWithReturnImpl.kt @@ -6,9 +6,9 @@ package com.datadog.android.sqldelight.internal +import app.cash.sqldelight.TransactionWithReturn import com.datadog.android.sqldelight.TransactionWithSpanAndWithReturn import com.datadog.android.trace.api.span.DatadogSpan -import com.squareup.sqldelight.TransactionWithReturn internal class TransactionWithSpanAndWithReturnImpl( private val span: DatadogSpan, diff --git a/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/internal/TransactionWithSpanAndWithoutReturnImpl.kt b/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/internal/TransactionWithSpanAndWithoutReturnImpl.kt index c37aee05a4..bad7e5e94e 100644 --- a/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/internal/TransactionWithSpanAndWithoutReturnImpl.kt +++ b/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/internal/TransactionWithSpanAndWithoutReturnImpl.kt @@ -6,9 +6,9 @@ package com.datadog.android.sqldelight.internal +import app.cash.sqldelight.TransactionWithoutReturn import com.datadog.android.sqldelight.TransactionWithSpanAndWithoutReturn import com.datadog.android.trace.api.span.DatadogSpan -import com.squareup.sqldelight.TransactionWithoutReturn internal class TransactionWithSpanAndWithoutReturnImpl( private val span: DatadogSpan, diff --git a/integrations/dd-sdk-android-sqldelight/src/test/kotlin/com/datadog/android/sqldelight/SqlDelightExtTest.kt b/integrations/dd-sdk-android-sqldelight/src/test/kotlin/com/datadog/android/sqldelight/SqlDelightExtTest.kt index c6bf9463dc..6df775050d 100644 --- a/integrations/dd-sdk-android-sqldelight/src/test/kotlin/com/datadog/android/sqldelight/SqlDelightExtTest.kt +++ b/integrations/dd-sdk-android-sqldelight/src/test/kotlin/com/datadog/android/sqldelight/SqlDelightExtTest.kt @@ -6,15 +6,15 @@ package com.datadog.android.sqldelight +import app.cash.sqldelight.Transacter +import app.cash.sqldelight.TransactionWithReturn +import app.cash.sqldelight.TransactionWithoutReturn import com.datadog.android.trace.GlobalDatadogTracer import com.datadog.android.trace.api.scope.DatadogScope import com.datadog.android.trace.api.span.DatadogSpan import com.datadog.android.trace.api.span.DatadogSpanBuilder import com.datadog.android.trace.api.tracer.DatadogTracer import com.datadog.tools.unit.forge.BaseConfigurator -import com.squareup.sqldelight.Transacter -import com.squareup.sqldelight.TransactionWithReturn -import com.squareup.sqldelight.TransactionWithoutReturn import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.annotation.Forgery import fr.xgouchet.elmyr.annotation.StringForgery diff --git a/integrations/dd-sdk-android-sqldelight/transitiveDependencies b/integrations/dd-sdk-android-sqldelight/transitiveDependencies index 66f82d9e37..b278a7282f 100644 --- a/integrations/dd-sdk-android-sqldelight/transitiveDependencies +++ b/integrations/dd-sdk-android-sqldelight/transitiveDependencies @@ -1,15 +1,15 @@ Dependencies List -androidx.annotation:annotation:1.0.0 : 24 Kb -androidx.sqlite:sqlite:2.1.0 : 10 Kb +androidx.annotation:annotation-jvm:1.9.1 : 59 Kb +androidx.sqlite:sqlite-android:2.6.2 : 37 Kb +app.cash.sqldelight:android-driver:2.3.2 : 21 Kb +app.cash.sqldelight:runtime-jvm:2.3.2 : 61 Kb com.squareup.okhttp3:okhttp:4.12.0 : 771 Kb com.squareup.okio:okio-jvm:3.6.0 : 351 Kb -com.squareup.sqldelight:android-driver:1.5.5 : 23 Kb -com.squareup.sqldelight:runtime-jvm:1.5.5 : 44 Kb org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.10 : 959 b org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10 : 965 b -org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb +org.jetbrains.kotlin:kotlin-stdlib:2.3.20 : 1762 Kb org.jetbrains:annotations:13.0 : 17 Kb -Total transitive dependencies size : 2 Mb +Total transitive dependencies size : 3 Mb diff --git a/integrations/dd-sdk-android-timber/api/compiler-meta.txt b/integrations/dd-sdk-android-timber/api/compiler-meta.txt index a098d10949..f757d782cb 100644 --- a/integrations/dd-sdk-android-timber/api/compiler-meta.txt +++ b/integrations/dd-sdk-android-timber/api/compiler-meta.txt @@ -1,2 +1,2 @@ -kotlin_abi_version=2.0.0 -jvm_bytecode_version=11 +kotlin_abi_version=2.1.0 +jvm_bytecode_version=17 diff --git a/integrations/dd-sdk-android-timber/build.gradle.kts b/integrations/dd-sdk-android-timber/build.gradle.kts index e929937ded..9231fb8ae2 100644 --- a/integrations/dd-sdk-android-timber/build.gradle.kts +++ b/integrations/dd-sdk-android-timber/build.gradle.kts @@ -4,15 +4,14 @@ * Copyright 2016-Present Datadog, Inc. */ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("datadogBuildConfig") // Publish @@ -59,7 +58,7 @@ dependencies { } datadogBuild { - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyAndroidLibraryConfig() applyJunitConfig() applyJavadocConfig() diff --git a/integrations/dd-sdk-android-timber/transitiveDependencies b/integrations/dd-sdk-android-timber/transitiveDependencies index fd2a4cfbbc..acb38a49b3 100644 --- a/integrations/dd-sdk-android-timber/transitiveDependencies +++ b/integrations/dd-sdk-android-timber/transitiveDependencies @@ -1,8 +1,8 @@ Dependencies List com.jakewharton.timber:timber:5.0.1 : 31 Kb -org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb +org.jetbrains.kotlin:kotlin-stdlib:2.3.20 : 1762 Kb org.jetbrains:annotations:13.0 : 17 Kb -Total transitive dependencies size : 1755 Kb +Total transitive dependencies size : 1811 Kb diff --git a/integrations/dd-sdk-android-trace-coroutines/api/compiler-meta.txt b/integrations/dd-sdk-android-trace-coroutines/api/compiler-meta.txt index a098d10949..f757d782cb 100644 --- a/integrations/dd-sdk-android-trace-coroutines/api/compiler-meta.txt +++ b/integrations/dd-sdk-android-trace-coroutines/api/compiler-meta.txt @@ -1,2 +1,2 @@ -kotlin_abi_version=2.0.0 -jvm_bytecode_version=11 +kotlin_abi_version=2.1.0 +jvm_bytecode_version=17 diff --git a/integrations/dd-sdk-android-trace-coroutines/build.gradle.kts b/integrations/dd-sdk-android-trace-coroutines/build.gradle.kts index 30a59d4f1c..800711cd50 100644 --- a/integrations/dd-sdk-android-trace-coroutines/build.gradle.kts +++ b/integrations/dd-sdk-android-trace-coroutines/build.gradle.kts @@ -4,15 +4,14 @@ * Copyright 2016-Present Datadog, Inc. */ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("datadogBuildConfig") // Publish @@ -59,7 +58,7 @@ dependencies { } datadogBuild { - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyAndroidLibraryConfig() applyJunitConfig() applyJavadocConfig() diff --git a/integrations/dd-sdk-android-trace-coroutines/transitiveDependencies b/integrations/dd-sdk-android-trace-coroutines/transitiveDependencies index ef1c5009ab..286c966fdf 100644 --- a/integrations/dd-sdk-android-trace-coroutines/transitiveDependencies +++ b/integrations/dd-sdk-android-trace-coroutines/transitiveDependencies @@ -1,9 +1,7 @@ Dependencies List -org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.20 : 963 b -org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.20 : 969 b -org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb -org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.3 : 1514 Kb +org.jetbrains.kotlin:kotlin-stdlib:2.3.20 : 1762 Kb +org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.10.2 : 1444 Kb org.jetbrains:annotations:23.0.0 : 28 Kb Total transitive dependencies size : 3 Mb diff --git a/integrations/dd-sdk-android-tv/api/compiler-meta.txt b/integrations/dd-sdk-android-tv/api/compiler-meta.txt index a098d10949..f757d782cb 100644 --- a/integrations/dd-sdk-android-tv/api/compiler-meta.txt +++ b/integrations/dd-sdk-android-tv/api/compiler-meta.txt @@ -1,2 +1,2 @@ -kotlin_abi_version=2.0.0 -jvm_bytecode_version=11 +kotlin_abi_version=2.1.0 +jvm_bytecode_version=17 diff --git a/integrations/dd-sdk-android-tv/build.gradle.kts b/integrations/dd-sdk-android-tv/build.gradle.kts index 3c4a35d1a0..580204927f 100644 --- a/integrations/dd-sdk-android-tv/build.gradle.kts +++ b/integrations/dd-sdk-android-tv/build.gradle.kts @@ -4,15 +4,14 @@ * Copyright 2016-Present Datadog, Inc. */ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("datadogBuildConfig") // Publish @@ -58,7 +57,7 @@ dependencies { } datadogBuild { - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyAndroidLibraryConfig() applyJunitConfig() applyJavadocConfig() diff --git a/integrations/dd-sdk-android-tv/transitiveDependencies b/integrations/dd-sdk-android-tv/transitiveDependencies index badf109cd1..83d3a10e81 100644 --- a/integrations/dd-sdk-android-tv/transitiveDependencies +++ b/integrations/dd-sdk-android-tv/transitiveDependencies @@ -30,7 +30,7 @@ androidx.slidingpanelayout:slidingpanelayout:1.0.0 : 22 Kb androidx.swiperefreshlayout:swiperefreshlayout:1.0.0 : 32 Kb androidx.versionedparcelable:versionedparcelable:1.0.0 : 26 Kb androidx.viewpager:viewpager:1.0.0 : 52 Kb -org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb +org.jetbrains.kotlin:kotlin-stdlib:2.3.20 : 1762 Kb org.jetbrains:annotations:13.0 : 17 Kb Total transitive dependencies size : 4 Mb diff --git a/reliability/core-it/build.gradle.kts b/reliability/core-it/build.gradle.kts index 7c73555cd8..84e38d4eab 100644 --- a/reliability/core-it/build.gradle.kts +++ b/reliability/core-it/build.gradle.kts @@ -6,15 +6,15 @@ import com.datadog.gradle.config.AndroidConfig import com.datadog.gradle.config.java17 -import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.application") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("datadogBuildConfig") // Analysis tools @@ -95,6 +95,6 @@ dependencies { } datadogBuild { - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyJunitConfig() } diff --git a/reliability/core-it/src/androidTest/kotlin/com/datadog/android/core/integration/tests/InternalSdkCoreTest.kt b/reliability/core-it/src/androidTest/kotlin/com/datadog/android/core/integration/tests/InternalSdkCoreTest.kt index 8cab8b6f67..994a219460 100644 --- a/reliability/core-it/src/androidTest/kotlin/com/datadog/android/core/integration/tests/InternalSdkCoreTest.kt +++ b/reliability/core-it/src/androidTest/kotlin/com/datadog/android/core/integration/tests/InternalSdkCoreTest.kt @@ -603,7 +603,7 @@ class InternalSdkCoreTest : MockServerTest() { getPackageInfo(appContext.packageName, 0) } } - } catch (e: PackageManager.NameNotFoundException) { + } catch (_: PackageManager.NameNotFoundException) { null } diff --git a/reliability/single-fit/logs/build.gradle.kts b/reliability/single-fit/logs/build.gradle.kts index 7712775b07..bd18a0e402 100644 --- a/reliability/single-fit/logs/build.gradle.kts +++ b/reliability/single-fit/logs/build.gradle.kts @@ -4,15 +4,14 @@ * Copyright 2016-Present Datadog, Inc. */ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("datadogBuildConfig") // Analysis tools @@ -58,6 +57,6 @@ unMock { datadogBuild { applyAndroidLibraryConfig() - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyJunitConfig() } diff --git a/reliability/single-fit/okhttp/build.gradle.kts b/reliability/single-fit/okhttp/build.gradle.kts index c8646f794c..47e6f76367 100644 --- a/reliability/single-fit/okhttp/build.gradle.kts +++ b/reliability/single-fit/okhttp/build.gradle.kts @@ -4,15 +4,14 @@ * Copyright 2016-Present Datadog, Inc. */ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("datadogBuildConfig") // Analysis tools @@ -56,7 +55,7 @@ dependencies { testImplementation(libs.okHttp) testImplementation(libs.okHttpMock) testImplementation(libs.gson) - testImplementation(libs.apolloRuntime) + implementation(libs.apolloRuntime) unmock(libs.robolectric) } @@ -65,10 +64,6 @@ apollo { srcDir("src/test/resources/graphql") packageName.set("com.datadog.android.testgraphql") schemaFiles.from("src/test/resources/graphql/schema.graphqls") - - outputDirConnection { - connectToKotlinSourceSet("test") - } } } @@ -87,8 +82,7 @@ datadogBuild { applyKotlinConfig( // TODO RUM-18200 We access internal members of another module in this module // This should be addressed properly, temporarily disable treating warnings as errors - evaluateWarningsAsErrors = false, - jvmBytecodeTarget = JvmTarget.JVM_11 + evaluateWarningsAsErrors = false ) applyJunitConfig() } diff --git a/reliability/single-fit/profiling/build.gradle.kts b/reliability/single-fit/profiling/build.gradle.kts index 2543b6f133..245283f998 100644 --- a/reliability/single-fit/profiling/build.gradle.kts +++ b/reliability/single-fit/profiling/build.gradle.kts @@ -4,15 +4,14 @@ * Copyright 2016-Present Datadog, Inc. */ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("datadogBuildConfig") // Analysis tools @@ -60,6 +59,6 @@ unMock { datadogBuild { applyAndroidLibraryConfig() - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyJunitConfig() } diff --git a/reliability/single-fit/rum/build.gradle.kts b/reliability/single-fit/rum/build.gradle.kts index 8feaf98da5..bd553ea066 100644 --- a/reliability/single-fit/rum/build.gradle.kts +++ b/reliability/single-fit/rum/build.gradle.kts @@ -4,15 +4,14 @@ * Copyright 2016-Present Datadog, Inc. */ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("datadogBuildConfig") // Analysis tools @@ -33,7 +32,6 @@ dependencies { implementation(project(":dd-sdk-android-internal")) implementation(project(":features:dd-sdk-android-rum")) implementation(libs.kotlin) - implementation(libs.bundles.androidXNavigation) // Testing testImplementation(project(":tools:unit")) { @@ -62,6 +60,6 @@ unMock { datadogBuild { applyAndroidLibraryConfig() - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyJunitConfig() } diff --git a/reliability/single-fit/rum/src/test/kotlin/com/datadog/android/rum/integration/ViewLoadingTimeMetricsTests.kt b/reliability/single-fit/rum/src/test/kotlin/com/datadog/android/rum/integration/ViewLoadingTimeMetricsTests.kt index a4b316b644..2b7b1e04f8 100644 --- a/reliability/single-fit/rum/src/test/kotlin/com/datadog/android/rum/integration/ViewLoadingTimeMetricsTests.kt +++ b/reliability/single-fit/rum/src/test/kotlin/com/datadog/android/rum/integration/ViewLoadingTimeMetricsTests.kt @@ -484,7 +484,7 @@ class ViewLoadingTimeMetricsTests { hasType("resource") hasViewUrl(viewKey) hasViewName(viewName) - hasResourceUrl(resourceUrl.toString()) + hasResourceUrl(resourceUrl) } .hasRumEvent(index = 2) { // View updated with event diff --git a/reliability/single-fit/trace/build.gradle.kts b/reliability/single-fit/trace/build.gradle.kts index d9c2f1bd6e..a825eef8a2 100644 --- a/reliability/single-fit/trace/build.gradle.kts +++ b/reliability/single-fit/trace/build.gradle.kts @@ -4,15 +4,14 @@ * Copyright 2016-Present Datadog, Inc. */ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("datadogBuildConfig") // Analysis tools @@ -73,6 +72,6 @@ unMock { datadogBuild { applyAndroidLibraryConfig() - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyJunitConfig() } diff --git a/reliability/stub-core/build.gradle.kts b/reliability/stub-core/build.gradle.kts index d1e7b1b909..cfe5b2551d 100644 --- a/reliability/stub-core/build.gradle.kts +++ b/reliability/stub-core/build.gradle.kts @@ -4,15 +4,14 @@ * Copyright 2016-Present Datadog, Inc. */ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("datadogBuildConfig") // Analysis tools @@ -33,7 +32,10 @@ dependencies { implementation(project(":dd-sdk-android-core")) implementation(libs.kotlin) - // Testing + // Testing — mockito-kotlin/JUnit are used from this module's MAIN source set (it is a test + // stub helper), so these belong on `implementation`, not the test classpath. + implementation(libs.bundles.jUnit5) + implementation(libs.bundles.testTools) implementation(project(":tools:unit")) { attributes { attribute( @@ -42,13 +44,11 @@ dependencies { ) } } - implementation(libs.bundles.jUnit5) - implementation(libs.bundles.testTools) implementation(libs.okHttp) implementation(libs.gson) } datadogBuild { applyAndroidLibraryConfig() - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() } diff --git a/reliability/stub-feature/build.gradle.kts b/reliability/stub-feature/build.gradle.kts index 0ed3a6fd34..4659787908 100644 --- a/reliability/stub-feature/build.gradle.kts +++ b/reliability/stub-feature/build.gradle.kts @@ -4,15 +4,16 @@ * Copyright 2016-Present Datadog, Inc. */ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import com.android.build.api.attributes.ProductFlavorAttr plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("datadogBuildConfig") // Analysis tools @@ -29,12 +30,24 @@ dependencies { implementation(project(":dd-sdk-android-core")) implementation(libs.kotlin) - // Testing + // Testing — mockito-kotlin/JUnit are used from this module's MAIN source set (it is a test + // stub helper), so these belong on `implementation`, not the test classpath. implementation(libs.bundles.jUnit5) implementation(libs.bundles.testTools) + implementation(project(":tools:unit")) { + attributes { + attribute( + ProductFlavorAttr.of("platform"), + // This module is consumed only by :reliability:core-it's androidTest (instrumented) + // classpath, which requests the "art" variant of :tools:unit. Requesting "jvm" here + // would pull a second variant of the same project and cause a capability conflict. + objects.named("art") + ) + } + } } datadogBuild { applyAndroidLibraryConfig() - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() } diff --git a/sample/automotive/build.gradle.kts b/sample/automotive/build.gradle.kts index 7e79069d1b..e099676b35 100644 --- a/sample/automotive/build.gradle.kts +++ b/sample/automotive/build.gradle.kts @@ -11,12 +11,13 @@ import com.datadog.gradle.config.taskConfig import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.application") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("datadogBuildConfig") alias(libs.plugins.datadogGradlePlugin) } diff --git a/sample/benchmark/build.gradle.kts b/sample/benchmark/build.gradle.kts index 338c2e99ef..6973a4f447 100644 --- a/sample/benchmark/build.gradle.kts +++ b/sample/benchmark/build.gradle.kts @@ -10,12 +10,13 @@ import com.datadog.gradle.config.java17 import com.datadog.gradle.plugin.InstrumentationMode plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.application") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") alias(libs.plugins.composeCompilerPlugin) alias(libs.plugins.kotlinSPGradlePlugin) kotlin("plugin.serialization") @@ -109,7 +110,6 @@ dependencies { ksp(libs.glideKsp) implementation(libs.coroutinesCore) implementation(libs.bundles.ktorClient) - implementation(libs.kotlinxSerializationJson) implementation(project(":features:dd-sdk-android-logs")) implementation(project(":features:dd-sdk-android-rum")) implementation(project(":features:dd-sdk-android-trace")) diff --git a/sample/kotlin/build.gradle.kts b/sample/kotlin/build.gradle.kts index 009e1b91d7..0c4f0448be 100644 --- a/sample/kotlin/build.gradle.kts +++ b/sample/kotlin/build.gradle.kts @@ -14,12 +14,13 @@ import com.datadog.gradle.plugin.InstrumentationMode import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.application") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") alias(libs.plugins.composeCompilerPlugin) alias(libs.plugins.sqlDelightGradlePlugin) id("com.google.devtools.ksp") @@ -28,10 +29,12 @@ plugins { } sqldelight { - database("LogsDatabase") { - packageName = "com.datadog.android.sample" - dialect = "sqlite:3.24" - sourceFolders = listOf("sqldelight") + databases { + create("LogsDatabase") { + packageName.set("com.datadog.android.sample") + srcDirs("src/main/sqldelight") + dialect(libs.sqlDelightSqlite324Dialect) + } } } @@ -85,16 +88,6 @@ android { } } - sourceSets.named("main") { - java.srcDir("src/main/kotlin") - } - sourceSets.named("test") { - java.srcDir("src/test/kotlin") - } - sourceSets.named("androidTest") { - java.srcDir("src/androidTest/kotlin") - } - packaging { resources { excludes += "META-INF/*" diff --git a/sample/kotlin/src/main/kotlin/com/datadog/android/sample/SampleApplication.kt b/sample/kotlin/src/main/kotlin/com/datadog/android/sample/SampleApplication.kt index 3f2c5bb5b6..d4438048e7 100644 --- a/sample/kotlin/src/main/kotlin/com/datadog/android/sample/SampleApplication.kt +++ b/sample/kotlin/src/main/kotlin/com/datadog/android/sample/SampleApplication.kt @@ -138,6 +138,7 @@ class SampleApplication : Application() { localServer.init(this) } + @Suppress("OVERRIDE_DEPRECATION") override fun onLowMemory() { super.onLowMemory() GlobalRumMonitor.get().addError( diff --git a/sample/kotlin/src/main/kotlin/com/datadog/android/sample/data/db/sqldelight/Database.kt b/sample/kotlin/src/main/kotlin/com/datadog/android/sample/data/db/sqldelight/Database.kt index 04c8d266ee..2a66c1c0f0 100644 --- a/sample/kotlin/src/main/kotlin/com/datadog/android/sample/data/db/sqldelight/Database.kt +++ b/sample/kotlin/src/main/kotlin/com/datadog/android/sample/data/db/sqldelight/Database.kt @@ -7,9 +7,9 @@ package com.datadog.android.sample.data.db.sqldelight import android.content.Context +import app.cash.sqldelight.driver.android.AndroidSqliteDriver import com.datadog.android.sample.LogsDatabase import com.datadog.android.sqldelight.DatadogSqliteCallback -import com.squareup.sqldelight.android.AndroidSqliteDriver internal object Database { diff --git a/sample/tv/build.gradle.kts b/sample/tv/build.gradle.kts index a92c8d0dcb..9059cbff17 100644 --- a/sample/tv/build.gradle.kts +++ b/sample/tv/build.gradle.kts @@ -12,12 +12,13 @@ import com.datadog.gradle.config.taskConfig import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.application") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("datadogBuildConfig") alias(libs.plugins.datadogGradlePlugin) } @@ -54,16 +55,6 @@ android { buildFeatures { buildConfig = true } - - sourceSets.named("main") { - java.srcDir("src/main/kotlin") - } - sourceSets.named("test") { - java.srcDir("src/test/kotlin") - } - sourceSets.named("androidTest") { - java.srcDir("src/androidTest/kotlin") - } } repositories.depotProxied(providers) { diff --git a/sample/vendor-lib/build.gradle.kts b/sample/vendor-lib/build.gradle.kts index 14ba5c5cdf..fcea9aeeba 100644 --- a/sample/vendor-lib/build.gradle.kts +++ b/sample/vendor-lib/build.gradle.kts @@ -11,12 +11,13 @@ import com.datadog.gradle.config.taskConfig import java.io.File plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("datadogBuildConfig") } @@ -53,16 +54,6 @@ android { unitTests.isReturnDefaultValues = true } - sourceSets.named("main") { - java.srcDir("src/main/kotlin") - } - sourceSets.named("test") { - java.srcDir("src/test/kotlin") - } - sourceSets.named("androidTest") { - java.srcDir("src/androidTest/kotlin") - } - packaging { resources { excludes += "META-INF/*" diff --git a/sample/wear/build.gradle.kts b/sample/wear/build.gradle.kts index 26e0486933..da53be7b6d 100644 --- a/sample/wear/build.gradle.kts +++ b/sample/wear/build.gradle.kts @@ -10,12 +10,13 @@ import com.datadog.gradle.config.configureFlavorForSampleApp import com.datadog.gradle.config.java17 plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.application") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - id("org.jetbrains.kotlin.android") } // TODO RUM-18189 Support new AGP DSL diff --git a/tools/benchmark/build.gradle.kts b/tools/benchmark/build.gradle.kts index 45e986ed5a..56a9e6bd50 100644 --- a/tools/benchmark/build.gradle.kts +++ b/tools/benchmark/build.gradle.kts @@ -5,16 +5,17 @@ */ import com.datadog.gradle.config.AndroidConfig -import com.datadog.gradle.config.java11 +import com.datadog.gradle.config.java17 import com.datadog.gradle.utils.createJsonModelsGenerationTask plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("datadogBuildConfig") // Publishing @@ -31,7 +32,7 @@ android { } namespace = "com.datadog.tools.benchmark" compileOptions { - java11() + java17() } } diff --git a/tools/detekt/build.gradle.kts b/tools/detekt/build.gradle.kts index 583797fc9b..4400507bf7 100644 --- a/tools/detekt/build.gradle.kts +++ b/tools/detekt/build.gradle.kts @@ -22,6 +22,9 @@ dependencies { testImplementation(libs.bundles.jUnit5) testImplementation(libs.bundles.testTools) testImplementation(libs.detektTest) + // The main source set only has `compileOnly(detektApi)`, so the api types the rules under test + // extend have to be put on the test runtime classpath explicitly. + testImplementation(libs.detektApi) testImplementation(libs.robolectric) } diff --git a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/DatadogProvider.kt b/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/DatadogProvider.kt index 5590504b9e..58692c7c97 100644 --- a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/DatadogProvider.kt +++ b/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/DatadogProvider.kt @@ -16,31 +16,33 @@ import com.datadog.tools.detekt.rules.sdk.ThrowingInternalException import com.datadog.tools.detekt.rules.sdk.TodoWithoutTask import com.datadog.tools.detekt.rules.sdk.UnsafeCallOnNullableType import com.datadog.tools.detekt.rules.sdk.UnsafeThirdPartyFunctionCall -import io.gitlab.arturbosch.detekt.api.Config -import io.gitlab.arturbosch.detekt.api.RuleSet -import io.gitlab.arturbosch.detekt.api.RuleSetProvider +import dev.detekt.api.Config +import dev.detekt.api.Rule +import dev.detekt.api.RuleSet +import dev.detekt.api.RuleSetId +import dev.detekt.api.RuleSetProvider /** * The [RuleSetProvider] for Datadog's SDK for Android. */ class DatadogProvider : RuleSetProvider { - override val ruleSetId: String = "datadog" + override val ruleSetId: RuleSetId = RuleSetId("datadog") - override fun instance(config: Config): RuleSet { + override fun instance(): RuleSet { return RuleSet( ruleSetId, - listOf( - CheckInternal(), - InvalidStringFormat(), - PackageNameVisibility(config), - PreferTimeProvider(config), - RequireInternal(), - ThreadSafety(config), - ThrowingInternalException(), - TodoWithoutTask(config), - UnsafeCallOnNullableType(), - UnsafeThirdPartyFunctionCall(config) + listOf<(Config) -> Rule>( + ::CheckInternal, + ::InvalidStringFormat, + ::PackageNameVisibility, + ::PreferTimeProvider, + ::RequireInternal, + ::ThreadSafety, + ::ThrowingInternalException, + ::TodoWithoutTask, + ::UnsafeCallOnNullableType, + ::UnsafeThirdPartyFunctionCall ) ) } diff --git a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/DatadogTestPyramidProvider.kt b/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/DatadogTestPyramidProvider.kt index 526aea287c..db4e38995c 100644 --- a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/DatadogTestPyramidProvider.kt +++ b/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/DatadogTestPyramidProvider.kt @@ -8,23 +8,25 @@ package com.datadog.tools.detekt import com.datadog.tools.detekt.rules.pyramid.ApiSurface import com.datadog.tools.detekt.rules.pyramid.ApiUsage -import io.gitlab.arturbosch.detekt.api.Config -import io.gitlab.arturbosch.detekt.api.RuleSet -import io.gitlab.arturbosch.detekt.api.RuleSetProvider +import dev.detekt.api.Config +import dev.detekt.api.Rule +import dev.detekt.api.RuleSet +import dev.detekt.api.RuleSetId +import dev.detekt.api.RuleSetProvider /** * The [RuleSetProvider] for Datadog's SDK for Android. */ class DatadogTestPyramidProvider : RuleSetProvider { - override val ruleSetId: String = "datadog-test-pyramid" + override val ruleSetId: RuleSetId = RuleSetId("datadog-test-pyramid") - override fun instance(config: Config): RuleSet { + override fun instance(): RuleSet { return RuleSet( ruleSetId, - listOf( - ApiUsage(config), - ApiSurface(config) + listOf<(Config) -> Rule>( + ::ApiUsage, + ::ApiSurface ) ) } diff --git a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/ext/KaTypeExt.kt b/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/ext/KaTypeExt.kt new file mode 100644 index 0000000000..6e6643d167 --- /dev/null +++ b/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/ext/KaTypeExt.kt @@ -0,0 +1,106 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + +package com.datadog.tools.detekt.ext + +import org.jetbrains.kotlin.analysis.api.KaSession +import org.jetbrains.kotlin.analysis.api.resolution.KaExplicitReceiverValue +import org.jetbrains.kotlin.analysis.api.resolution.KaReceiverValue +import org.jetbrains.kotlin.analysis.api.resolution.KaSmartCastedReceiverValue +import org.jetbrains.kotlin.analysis.api.types.KaClassType +import org.jetbrains.kotlin.analysis.api.types.KaFlexibleType +import org.jetbrains.kotlin.analysis.api.types.KaIntersectionType +import org.jetbrains.kotlin.analysis.api.types.KaStarTypeProjection +import org.jetbrains.kotlin.analysis.api.types.KaType +import org.jetbrains.kotlin.analysis.api.types.KaTypeParameterType + +/** + * The placeholder used when a type can't be resolved. + */ +internal const val UNKNOWN_TYPE: String = "UNKNOWN" + +/** + * Renders the fully qualified name of the given [type]. + * + * @param type the type to render + * @param treatGenericAsSuper replaces a generic type (e.g. ``) by its super type + * @param includeTypeArguments includes the type arguments in the rendered name + * (e.g. if false, the type `List` is rendered as `kotlin.collections.List`) + */ +internal fun KaSession.fqTypeName( + type: KaType, + treatGenericAsSuper: Boolean = true, + includeTypeArguments: Boolean = true +): String { + val bareType = when (type) { + // Types coming from Java are flexible (e.g. `String!`), because the Kotlin compiler doesn't + // know whether they're nullable or not. The class name comes from the lower bound, while + // nullability is decided by the type as a whole further down, so platform types stay nullable. + is KaFlexibleType -> type.lowerBound + // A smart cast produces an intersection (e.g. `Runnable & Future<*>`). The type the value was + // narrowed to is the one being called, and comes first in the conjuncts. + is KaIntersectionType -> type.conjuncts.firstOrNull() ?: type + + else -> type + } + + val resolved = if (bareType is KaTypeParameterType && treatGenericAsSuper) { + // Treat generic types as their closest supertype; an unbounded type parameter is an `Any?` + bareType.symbol.upperBounds.firstOrNull() ?: builtinTypes.nullableAny + } else { + bareType + } + + val fqName = when (resolved) { + is KaClassType -> resolved.classId.asFqNameString() + is KaTypeParameterType -> resolved.name.asString() + else -> null + } + + if (fqName == null) { + println("Unable to get fqName for ${type.javaClass} $type") + return UNKNOWN_TYPE + } + + val arguments = if (includeTypeArguments && resolved is KaClassType && resolved.typeArguments.isNotEmpty()) { + resolved.typeArguments.joinToString(", ", prefix = "<", postfix = ">") { projection -> + val argumentType = projection.type + if (projection is KaStarTypeProjection || argumentType == null) { + "*" + } else { + fqTypeName(argumentType, treatGenericAsSuper) + } + } + } else { + "" + } + + return if (type.isNullable) { + "$fqName$arguments?" + } else { + fqName + arguments + } +} + +/** + * Renders the fully qualified name of a call receiver's type, always as a non nullable type. + */ +internal fun KaSession.fqReceiverTypeName( + receiver: KaReceiverValue, + treatGenericAsSuper: Boolean = true, + includeTypeArguments: Boolean = true +): String = fqTypeName(receiver.type, treatGenericAsSuper, includeTypeArguments).removeSuffix("?") + +/** + * Unwraps a receiver which was smart cast, to get to the receiver as it was written in the source. + */ +internal fun KaReceiverValue.unwrapSmartCast(): KaReceiverValue = + if (this is KaSmartCastedReceiverValue) original.unwrapSmartCast() else this + +/** + * Whether this receiver was explicitly written at the call site (as opposed to an implicit `this`). + */ +internal fun KaReceiverValue.isExplicit(): Boolean = unwrapSmartCast() is KaExplicitReceiverValue diff --git a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/ext/KotlinTypeExt.kt b/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/ext/KotlinTypeExt.kt deleted file mode 100644 index 62d03b632b..0000000000 --- a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/ext/KotlinTypeExt.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.tools.detekt.ext - -import org.jetbrains.kotlin.descriptors.buildPossiblyInnerType -import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull -import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.types.checker.SimpleClassicTypeSystemContext.getArguments -import org.jetbrains.kotlin.types.isNullable -import org.jetbrains.kotlin.types.typeUtil.supertypes - -internal fun KotlinType.fqTypeName( - treatGenericAsSuper: Boolean = true, - includeTypeArguments: Boolean = true -): String { - val descriptor = if ((constructor.toString().length == 1) && treatGenericAsSuper) { - // Treat generic types as their closest supertype (usually Any) - supertypes().firstOrNull()?.buildPossiblyInnerType()?.classifierDescriptor - } else { - buildPossiblyInnerType()?.classifierDescriptor - } - val fqName = descriptor?.fqNameOrNull()?.asString() - if (fqName == null) { - println("Unable to get fqName for ${this.javaClass} $this") - return "UNKNOWN" - } - val arguments = if (getArguments().isNotEmpty()) { - if (includeTypeArguments) { - arguments.joinToString(", ", prefix = "<", postfix = ">") { - if (it.isStarProjection) "*" else it.type.fqTypeName(treatGenericAsSuper) - } - } else { - "" - } - } else { - "" - } - return if (isNullable()) { - "$fqName$arguments?" - } else { - fqName + arguments - } -} diff --git a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/ext/ReceiverExt.kt b/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/ext/ReceiverExt.kt deleted file mode 100644 index 017d78c109..0000000000 --- a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/ext/ReceiverExt.kt +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.tools.detekt.ext - -import io.gitlab.arturbosch.detekt.rules.fqNameOrNull -import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.calls.util.getType -import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull -import org.jetbrains.kotlin.resolve.scopes.receivers.ClassQualifier -import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver -import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver -import org.jetbrains.kotlin.resolve.scopes.receivers.PackageQualifier -import org.jetbrains.kotlin.resolve.scopes.receivers.Receiver -import org.jetbrains.kotlin.types.FlexibleType -import org.jetbrains.kotlin.types.lowerIfFlexible -import org.jetbrains.kotlin.types.typeUtil.makeNotNullable - -internal fun Receiver.type( - bindingContext: BindingContext, - treatGenericAsSuper: Boolean, - includeTypeArguments: Boolean -): String? { - return when (this) { - is ExpressionReceiver -> { - val resolvedType = expression.getType(bindingContext) - if (resolvedType is FlexibleType) { - // types from java are flexible because the Kotlin Compiler doesn't know whether - // they're nullable or not. Using the lowerBound makes it nonNullable - resolvedType.lowerIfFlexible() - .makeNotNullable() - .fqTypeName( - treatGenericAsSuper = treatGenericAsSuper, - includeTypeArguments = includeTypeArguments - ) - } else { - // Convert all types to nonNullable - resolvedType?.makeNotNullable() - ?.fqTypeName( - treatGenericAsSuper = treatGenericAsSuper, - includeTypeArguments = includeTypeArguments - ) - } - } - - is ClassQualifier -> { - descriptor.fqNameOrNull()?.toString() - } - - is ImplicitReceiver -> { - type.fqNameOrNull()?.toString() - } - - is PackageQualifier -> { - descriptor.fqName.asString() - } - - else -> { - println("DD: Unknown receiver type ${this.javaClass}") - null - } - } -} diff --git a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/AbstractCallExpressionRule.kt b/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/AbstractCallExpressionRule.kt index 067030968a..fad892090a 100644 --- a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/AbstractCallExpressionRule.kt +++ b/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/AbstractCallExpressionRule.kt @@ -6,20 +6,21 @@ package com.datadog.tools.detekt.rules +import com.datadog.tools.detekt.ext.fqReceiverTypeName import com.datadog.tools.detekt.ext.fqTypeName -import com.datadog.tools.detekt.ext.type -import io.gitlab.arturbosch.detekt.api.Config -import org.jetbrains.kotlin.descriptors.CallableDescriptor -import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor -import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor -import org.jetbrains.kotlin.descriptors.containingPackage +import com.datadog.tools.detekt.ext.isExplicit +import dev.detekt.api.Config +import dev.detekt.api.RequiresAnalysisApi +import org.jetbrains.kotlin.analysis.api.KaSession +import org.jetbrains.kotlin.analysis.api.analyze +import org.jetbrains.kotlin.analysis.api.resolution.KaFunctionCall +import org.jetbrains.kotlin.analysis.api.resolution.KaImplicitInvokeCall +import org.jetbrains.kotlin.analysis.api.resolution.singleFunctionCallOrNull +import org.jetbrains.kotlin.analysis.api.resolution.symbol +import org.jetbrains.kotlin.analysis.api.signatures.KaVariableSignature +import org.jetbrains.kotlin.analysis.api.symbols.KaConstructorSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaValueParameterSymbol import org.jetbrains.kotlin.psi.KtCallExpression -import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall -import org.jetbrains.kotlin.resolve.calls.util.getCall -import org.jetbrains.kotlin.resolve.calls.util.getImplicitReceiverValue -import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall -import org.jetbrains.kotlin.resolve.calls.util.getType /** * An abstract Detekt rule resolving function calls. @@ -28,6 +29,7 @@ import org.jetbrains.kotlin.resolve.calls.util.getType * and delegates the handling of the call to the child implementation. * * @param ruleSetConfig the detekt ruleSet configuration + * @param description the description of the rule * @param simplifyLocalTypes if a call in class `com.example.foo.A` uses type `com.example.foo.B`, * refer to the latter as `B` * @param treatGenericAsSuper if a call uses a generic (e.g. ), @@ -36,11 +38,12 @@ import org.jetbrains.kotlin.resolve.calls.util.getType * (e.g. if false, the type `List` would only be reported as `List`) */ abstract class AbstractCallExpressionRule( - ruleSetConfig: Config = Config.empty, + ruleSetConfig: Config, + description: String, private val simplifyLocalTypes: Boolean = false, private val treatGenericAsSuper: Boolean = true, private val includeTypeArguments: Boolean = true -) : AbstractTypedRule(ruleSetConfig) { +) : AbstractTypedRule(ruleSetConfig, description), RequiresAnalysisApi { /** * A representation for a function with resolved types. @@ -60,69 +63,115 @@ abstract class AbstractCallExpressionRule( // region Rule - @Suppress("ReturnCount") override fun visitCallExpression(expression: KtCallExpression) { super.visitCallExpression(expression) - if (bindingContext == BindingContext.EMPTY) { - return - } - val resolvedCall = expression.getResolvedCall(bindingContext) - if (resolvedCall == null) { - println("Cannot resolve call for ${expression.getCall(bindingContext)}. Is classpath complete?") - return + val resolvedFunctionCall = analyze(expression) { resolveFunctionCall(expression) } ?: return + + visitResolvedFunctionCall(expression, resolvedFunctionCall) + } + + // endregion + + // region Internal + + @Suppress("ReturnCount") + private fun KaSession.resolveFunctionCall(expression: KtCallExpression): ResolvedFunCall? { + val call = expression.resolveToCall()?.singleFunctionCallOrNull() + if (call == null) { + println("Cannot resolve call for ${expression.text}. Is classpath complete?") + return null } - val call = resolvedCall.call - val returnType = expression.getType(bindingContext) - ?.fqTypeName(treatGenericAsSuper, includeTypeArguments) - ?: return - - val receiverType = listOf( - call.explicitReceiver, - call.dispatchReceiver, - resolvedCall.getImplicitReceiverValue() - ) - .firstNotNullOfOrNull { - it?.type(bindingContext, treatGenericAsSuper, includeTypeArguments) - } - val callDescriptor = resolvedCall.candidateDescriptor - val (containerFqName, functionName) = if (callDescriptor is ClassConstructorDescriptor) { + val returnType = expression.expressionType + ?.let { fqTypeName(it, treatGenericAsSuper, includeTypeArguments) } + ?: return null + + val receiverType = resolveReceiverType(call) + val symbol = call.symbol + val callContainingPackage = symbol.callableId?.packageName?.asString().orEmpty() + + val (containerFqName, functionName) = if (symbol is KaConstructorSymbol) { returnType to "constructor" } else { - val calleeName = call.calleeExpression?.node?.text ?: "UNKNOWNFUN" - val callContainingPackage = callDescriptor.containingPackage()?.toString().orEmpty() - if (receiverType == null) { - callContainingPackage to calleeName + val calleeName = if (call is KaImplicitInvokeCall) { + // Invoking a function value (`messageBuilder()` for a `() -> String` parameter) says + // nothing about third party API surface: the callee is whatever the caller supplied, + // and the lambda's body is analysed where it is declared. Name it after `invoke` rather + // than after the variable holding it, so callers can filter it out. + INVOKE_FUN_NAME } else { - "$receiverType" to calleeName + expression.calleeExpression?.text ?: "UNKNOWNFUN" } + // Statics and companion members have no value receiver, but are named after the qualifier + // written at the call site, i.e. their declaring class. Top level functions have no + // declaring class and are named after their package instead. + val container = receiverType + ?: symbol.callableId?.classId?.asFqNameString() + ?: callContainingPackage + container to calleeName } - val arguments = resolveParameterTypes(resolvedCall, containerFqName) - val resolvedFunctionCall = ResolvedFunCall( + val arguments = resolveParameterTypes(call, containerFqName) + + return ResolvedFunCall( call = "$containerFqName.$functionName(${arguments.joinToString(", ")})", containerFqName = containerFqName, functionName = functionName, - containingPackage = callDescriptor.containingPackage()?.toString().orEmpty(), + containingPackage = callContainingPackage, arguments = arguments ) + } - visitResolvedFunctionCall(expression, resolvedFunctionCall) + /** + * Resolves the type of the call's receiver, preferring the receiver explicitly written at the + * call site over an implicit one, so that a call on a subclass is reported on the subclass. + */ + private fun KaSession.resolveReceiverType(call: KaFunctionCall<*>): String? { + val receivers = listOfNotNull(call.dispatchReceiver, call.extensionReceiver) + return (receivers.filter { it.isExplicit() } + receivers.filterNot { it.isExplicit() }) + .asSequence() + .map { fqReceiverTypeName(it, treatGenericAsSuper, includeTypeArguments) } + // A type qualifier written at the call site (the `Thread` of `Thread.currentThread()`) is + // reported as an explicit receiver typed `Unit`, because it references a type rather than + // a value. It carries no receiver type; such calls are named after their declaring class. + .firstOrNull { it != UNIT_TYPE } } - private fun resolveParameterTypes( - resolvedCall: ResolvedCall, + private fun KaSession.resolveParameterTypes( + call: KaFunctionCall<*>, containerFqName: String ): List { - return resolvedCall.valueArguments - // Ensure the arguments when named are in original order - .toSortedMap { p0, p1 -> (p0?.index ?: Int.MAX_VALUE) - (p1?.index ?: -1) } - .map { it.key.parameterType(containerFqName) } + // the signature's parameters have their generics substituted with the types used at the call + // site, e.g. `listOf(window)` resolves to `listOf(android.view.Window)` + return call.signature.valueParameters.map { + parameterType(renderParameterType(it), containerFqName) + } } - private fun ValueParameterDescriptor.parameterType(containerFqName: String): String { - val fullType = type.fqTypeName(treatGenericAsSuper, includeTypeArguments) + /** + * Renders the type of a single parameter. A `vararg` parameter is declared as an array, and is + * reported as such, even though the Analysis API exposes its element type instead. + */ + private fun KaSession.renderParameterType( + parameter: KaVariableSignature + ): String { + val rendered = fqTypeName(parameter.returnType, treatGenericAsSuper, includeTypeArguments) + if (!parameter.symbol.isVararg) return rendered + + // Only a parameter *declared* with a primitive element type is a specialised array. A generic + // one stays a boxing `Array` even when the call site substitutes a primitive, so the declared + // type decides, e.g. `byteArrayOf(vararg Byte)` is a ByteArray but `setOf(vararg T)` is not. + val declaredElement = fqTypeName( + parameter.symbol.returnType, + treatGenericAsSuper = false, + includeTypeArguments = false + ) + return primitiveArrays[declaredElement] + ?: if (includeTypeArguments) "$ARRAY_TYPE<$rendered>" else ARRAY_TYPE + } + + private fun parameterType(fullType: String, containerFqName: String): String { val (nonNullType, suffix) = if (fullType.endsWith('?')) { fullType.substringBeforeLast('?') to "?" } else { @@ -169,6 +218,20 @@ abstract class AbstractCallExpressionRule( // endregion companion object { + private const val UNIT_TYPE = "kotlin.Unit" + private const val INVOKE_FUN_NAME = "invoke" + private const val ARRAY_TYPE = "kotlin.Array" private val containerFqNameRegex = Regex("^([a-z0-9-]+(\\.[a-z0-9-]+)*)((\\.[A-Z][a-zA-Z0-9-]+)*)$") + + private val primitiveArrays = mapOf( + "kotlin.Boolean" to "kotlin.BooleanArray", + "kotlin.Byte" to "kotlin.ByteArray", + "kotlin.Char" to "kotlin.CharArray", + "kotlin.Double" to "kotlin.DoubleArray", + "kotlin.Float" to "kotlin.FloatArray", + "kotlin.Int" to "kotlin.IntArray", + "kotlin.Long" to "kotlin.LongArray", + "kotlin.Short" to "kotlin.ShortArray" + ) } } diff --git a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/AbstractTypedRule.kt b/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/AbstractTypedRule.kt index 92f165275f..d7cba221fb 100644 --- a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/AbstractTypedRule.kt +++ b/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/AbstractTypedRule.kt @@ -6,21 +6,22 @@ package com.datadog.tools.detekt.rules -import io.gitlab.arturbosch.detekt.api.Config -import io.gitlab.arturbosch.detekt.api.Rule +import dev.detekt.api.Config +import dev.detekt.api.Rule import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtImportDirective import org.jetbrains.kotlin.psi.KtPackageDirective -import org.jetbrains.kotlin.resolve.BindingContext /** * An abstract Detekt rule keeping track of imports to resolve types found in the code. * * @param ruleSetConfig the detekt ruleSet configuration + * @param description the description of the rule */ abstract class AbstractTypedRule( - ruleSetConfig: Config = Config.empty -) : Rule(ruleSetConfig) { + ruleSetConfig: Config, + description: String +) : Rule(ruleSetConfig, description) { private val imports = mutableMapOf() private var packageName = "" @@ -28,9 +29,6 @@ abstract class AbstractTypedRule( // region Rule override fun visitKtFile(file: KtFile) { - if (bindingContext == BindingContext.EMPTY) { - println("\nMissing BindingContext when checking file:${file.virtualFilePath}") - } imports.clear() super.visitKtFile(file) } diff --git a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/pyramid/ApiSurface.kt b/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/pyramid/ApiSurface.kt index b30e1fc1a7..4b0946d9a1 100644 --- a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/pyramid/ApiSurface.kt +++ b/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/pyramid/ApiSurface.kt @@ -7,13 +7,9 @@ package com.datadog.tools.detekt.rules.pyramid import com.datadog.tools.detekt.rules.AbstractTypedRule -import io.gitlab.arturbosch.detekt.api.Config -import io.gitlab.arturbosch.detekt.api.Debt -import io.gitlab.arturbosch.detekt.api.Issue -import io.gitlab.arturbosch.detekt.api.Severity -import io.gitlab.arturbosch.detekt.api.config -import io.gitlab.arturbosch.detekt.api.internal.RequiresTypeResolution -import io.gitlab.arturbosch.detekt.rules.isOverride +import dev.detekt.api.Config +import dev.detekt.api.RequiresAnalysisApi +import dev.detekt.api.config import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.psi.KtFunctionType @@ -33,10 +29,10 @@ import java.io.File /** * @active */ -@RequiresTypeResolution class ApiSurface( - ruleSetConfig: Config -) : AbstractTypedRule(ruleSetConfig) { + ruleSetConfig: Config = Config.empty +) : AbstractTypedRule(ruleSetConfig, "This rule reports api surface."), + RequiresAnalysisApi { private val outputFileName: String by config(defaultValue = "apiSurface.log") private val outputFile: File by lazy { @@ -48,13 +44,6 @@ class ApiSurface( // region Rule - override val issue: Issue = Issue( - javaClass.simpleName, - Severity.Maintainability, - "This rule reports api surface.", - Debt.FIVE_MINS - ) - override fun visitObjectDeclaration(declaration: KtObjectDeclaration) { val hasIgnoredKeyword = ignoredKeywords.any { declaration.hasModifier(it) } val isIgnoredClass = declaration.fqName?.asString() in ignoredClasses @@ -114,7 +103,7 @@ class ApiSurface( val hasIgnoredAnnotation = function.annotationEntries.any { it.shortName?.asString()?.resolveFullType() in ignoredAnnotations } - val isOverride = function.isOverride() + val isOverride = function.hasModifier(KtTokens.OVERRIDE_KEYWORD) @Suppress("ComplexCondition") if (hasIgnoredKeyword || hasIgnoredName || hasIgnoredAnnotation || isOverride) { diff --git a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/pyramid/ApiUsage.kt b/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/pyramid/ApiUsage.kt index bb51b0945d..7fda8d8809 100644 --- a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/pyramid/ApiUsage.kt +++ b/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/pyramid/ApiUsage.kt @@ -7,12 +7,8 @@ package com.datadog.tools.detekt.rules.pyramid import com.datadog.tools.detekt.rules.AbstractCallExpressionRule -import io.gitlab.arturbosch.detekt.api.Config -import io.gitlab.arturbosch.detekt.api.Debt -import io.gitlab.arturbosch.detekt.api.Issue -import io.gitlab.arturbosch.detekt.api.Severity -import io.gitlab.arturbosch.detekt.api.config -import io.gitlab.arturbosch.detekt.api.internal.RequiresTypeResolution +import dev.detekt.api.Config +import dev.detekt.api.config import org.jetbrains.kotlin.psi.KtCallExpression import org.jetbrains.kotlin.psi.KtNamedFunction import java.io.File @@ -20,10 +16,14 @@ import java.io.File /** * @active */ -@RequiresTypeResolution class ApiUsage( - config: Config -) : AbstractCallExpressionRule(config, simplifyLocalTypes = true, includeTypeArguments = false) { + config: Config = Config.empty +) : AbstractCallExpressionRule( + config, + "This rule reports api usages.", + simplifyLocalTypes = true, + includeTypeArguments = false +) { private val outputFileName: String by config(defaultValue = "apiUsage.log") private val outputFile: File by lazy { @@ -36,13 +36,6 @@ class ApiUsage( // region Rule - override val issue: Issue = Issue( - javaClass.simpleName, - Severity.Maintainability, - "This rule reports api usages.", - Debt.FIVE_MINS - ) - override fun visitNamedFunction(function: KtNamedFunction) { val annotations = function.annotationEntries.mapNotNull { it.shortName?.asString()?.resolveFullType() diff --git a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/CheckInternal.kt b/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/CheckInternal.kt index 977849a542..fb9d258dc8 100644 --- a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/CheckInternal.kt +++ b/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/CheckInternal.kt @@ -7,26 +7,19 @@ package com.datadog.tools.detekt.rules.sdk import com.datadog.tools.detekt.ext.isContainingEntryPointPublic -import io.gitlab.arturbosch.detekt.api.CodeSmell -import io.gitlab.arturbosch.detekt.api.Debt -import io.gitlab.arturbosch.detekt.api.Entity -import io.gitlab.arturbosch.detekt.api.Issue -import io.gitlab.arturbosch.detekt.api.Rule -import io.gitlab.arturbosch.detekt.api.Severity +import dev.detekt.api.Config +import dev.detekt.api.Entity +import dev.detekt.api.Finding +import dev.detekt.api.Rule import org.jetbrains.kotlin.psi.KtCallExpression /** * A rule to detekt `check`, `checkNotNull` calls. * @active */ -class CheckInternal : Rule() { - - override val issue: Issue = Issue( - javaClass.simpleName, - Severity.Defect, - "This rule reports when an exception is thrown.", - Debt.TWENTY_MINS - ) +class CheckInternal( + config: Config = Config.empty +) : Rule(config, "This rule reports when an exception is thrown.") { override fun visitCallExpression(expression: KtCallExpression) { val callee = expression.calleeExpression @@ -37,8 +30,7 @@ class CheckInternal : Rule() { !expression.isContainingEntryPointPublic() ) { report( - CodeSmell( - issue, + Finding( Entity.from(expression), message = "A check is called from an internal or private part of the code." ) diff --git a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/InvalidStringFormat.kt b/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/InvalidStringFormat.kt index 5993fcb3db..132689705c 100644 --- a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/InvalidStringFormat.kt +++ b/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/InvalidStringFormat.kt @@ -7,31 +7,24 @@ package com.datadog.tools.detekt.rules.sdk import com.datadog.tools.detekt.ext.fqTypeName -import com.datadog.tools.detekt.ext.type -import io.gitlab.arturbosch.detekt.api.CodeSmell -import io.gitlab.arturbosch.detekt.api.Debt -import io.gitlab.arturbosch.detekt.api.Entity -import io.gitlab.arturbosch.detekt.api.Issue -import io.gitlab.arturbosch.detekt.api.Rule -import io.gitlab.arturbosch.detekt.api.Severity -import org.jetbrains.kotlin.descriptors.CallableDescriptor -import org.jetbrains.kotlin.descriptors.VariableDescriptor +import com.datadog.tools.detekt.ext.unwrapSmartCast +import dev.detekt.api.Config +import dev.detekt.api.Entity +import dev.detekt.api.Finding +import dev.detekt.api.RequiresAnalysisApi +import dev.detekt.api.Rule +import org.jetbrains.kotlin.analysis.api.KaSession +import org.jetbrains.kotlin.analysis.api.analyze +import org.jetbrains.kotlin.analysis.api.resolution.KaExplicitReceiverValue +import org.jetbrains.kotlin.analysis.api.resolution.KaFunctionCall +import org.jetbrains.kotlin.analysis.api.resolution.KaReceiverValue +import org.jetbrains.kotlin.analysis.api.resolution.singleFunctionCallOrNull import org.jetbrains.kotlin.psi.KtCallExpression import org.jetbrains.kotlin.psi.KtDotQualifiedExpression import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtReferenceExpression import org.jetbrains.kotlin.psi.KtStringTemplateExpression import org.jetbrains.kotlin.psi.psiUtil.children -import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.bindingContextUtil.getReferenceTargets -import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall -import org.jetbrains.kotlin.resolve.calls.model.VarargValueArgument -import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall -import org.jetbrains.kotlin.resolve.calls.util.getType -import org.jetbrains.kotlin.resolve.scopes.receivers.ClassQualifier -import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver -import org.jetbrains.kotlin.types.lowerIfFlexible -import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull import java.util.Locale /** @@ -40,76 +33,71 @@ import java.util.Locale * * @active */ -class InvalidStringFormat : Rule() { +class InvalidStringFormat( + config: Config = Config.empty +) : Rule(config, "This rule reports when a String format pattern does not match the provided arguments."), + RequiresAnalysisApi { + + /** + * The format string and argument types resolved for a `format` call. + * + * @param formatString the format pattern, when it could be resolved + * @param formatArgs the fully qualified type names of the arguments passed to the pattern + */ + private data class ResolvedFormatCall( + val formatString: String?, + val formatArgs: List + ) // region Rule - override val issue: Issue = Issue( - javaClass.simpleName, - Severity.Defect, - "This rule reports when a String format pattern does not match the provided arguments.", - Debt.TWENTY_MINS - ) - - @Suppress("ReturnCount") override fun visitCallExpression(expression: KtCallExpression) { super.visitCallExpression(expression) - if (bindingContext == BindingContext.EMPTY) { - return - } - - val resolvedCall = expression.getResolvedCall(bindingContext) ?: return - val call = resolvedCall.call - - // check receiver type - val explicitReceiver = call.explicitReceiver - val receiverType = explicitReceiver?.type( - bindingContext, - treatGenericAsSuper = true, - includeTypeArguments = true - ) - if (receiverType != STRING_CLASS) return // check method call - val calleeExpression = call.calleeExpression?.node?.text - if (calleeExpression != FORMAT_METHOD) return + if (expression.calleeExpression?.text != FORMAT_METHOD) return - val formatString = extractFormatString(resolvedCall) - val formatArgs = extractFormatArgs(resolvedCall) + val resolved = analyze(expression) { resolveFormatCall(expression) } ?: return - checkFormat(expression, formatString, formatArgs) + checkFormat(expression, resolved.formatString, resolved.formatArgs) } // endregion // region Internal - private fun extractFormatString( - resolvedCall: ResolvedCall - ): String? { - val call = resolvedCall.call - val stringExpression = when (val explicitReceiver = call.explicitReceiver) { - is ClassQualifier -> { - // String.format(…) - val arguments = resolvedCall.valueArguments.toList().firstOrNull { - it.first.type.fqTypeName() == STRING_CLASS - }?.second?.arguments - if (arguments != null && arguments.size == 1) { - arguments.first().getArgumentExpression() - } else { - null - } - } + @Suppress("ReturnCount") + private fun KaSession.resolveFormatCall(expression: KtCallExpression): ResolvedFormatCall? { + val call = expression.resolveToCall()?.singleFunctionCallOrNull() ?: return null - is ExpressionReceiver -> { - // "…".format(…) - explicitReceiver.expression - } + val receiver = call.extensionReceiver ?: call.dispatchReceiver ?: return null - else -> { - println("Unknown receiver:$explicitReceiver") - null - } + // check receiver type. `"…".format(…)` is an extension on String, while `String.format(…)` + // is an extension on String's companion object. + val receiverTypeName = fqTypeName(receiver.type).removeSuffix("?") + val isCompanionReceiver = receiverTypeName == STRING_COMPANION_CLASS + if (receiverTypeName != STRING_CLASS && !isCompanionReceiver) return null + + return ResolvedFormatCall( + formatString = extractFormatString(call, receiver, isCompanionReceiver), + formatArgs = extractFormatArgs(call, isCompanionReceiver) + ) + } + + private fun KaSession.extractFormatString( + call: KaFunctionCall<*>, + receiver: KaReceiverValue, + isCompanionReceiver: Boolean + ): String? { + val stringExpression = if (isCompanionReceiver) { + // String.format(…): the pattern is the `format` parameter + call.valueArgumentMapping + .filterValues { !it.symbol.isVararg && fqTypeName(it.returnType) == STRING_CLASS } + .keys + .singleOrNull() + } else { + // "…".format(…): the pattern is the receiver itself + (receiver.unwrapSmartCast() as? KaExplicitReceiverValue)?.expression } val expression = if (stringExpression is KtDotQualifiedExpression) { @@ -121,45 +109,34 @@ class InvalidStringFormat : Rule() { return resolveStringExpression(expression) } - private fun resolveStringExpression(stringExpression: KtExpression?): String? { + private fun KaSession.resolveStringExpression(stringExpression: KtExpression?): String? { return if (stringExpression is KtStringTemplateExpression) { stringExpression.node .children() .filter { it.text != "\"" } .joinToString("") { it.text } } else if (stringExpression is KtReferenceExpression) { - val referenceTarget = stringExpression.getReferenceTargets(bindingContext) - .firstIsInstanceOrNull() - - if (referenceTarget is VariableDescriptor && !referenceTarget.isVar) { - val compileTimeInitializer = referenceTarget.compileTimeInitializer - if (compileTimeInitializer != null) { - compileTimeInitializer.value as? String - } else { - println("Unable to get compileTimeInitializer for $referenceTarget") - null - } - } else { - println("Unknown referenceTarget:$referenceTarget") - null + val constantValue = stringExpression.evaluate()?.value as? String + if (constantValue == null) { + println("Unable to resolve a constant value for $stringExpression") } + constantValue } else { println("Unknown string expression: $stringExpression") null } } - private fun extractFormatArgs( - resolvedCall: ResolvedCall + private fun KaSession.extractFormatArgs( + call: KaFunctionCall<*>, + isCompanionReceiver: Boolean ): List { - val receiver = resolvedCall.call.explicitReceiver - val formatParams = resolvedCall.valueArgumentsByIndex?.last() as? VarargValueArgument + val rawTypes = call.valueArgumentMapping + .filterValues { it.symbol.isVararg } + .keys + .map { argument -> argument.expressionType?.let { fqTypeName(it) } } - val rawTypes = formatParams?.arguments.orEmpty() - .map { - it.getArgumentExpression()?.getType(bindingContext)?.lowerIfFlexible()?.fqTypeName() - } - return if (receiver is ClassQualifier && rawTypes.firstOrNull() == LOCALE_CLASS) { + return if (isCompanionReceiver && rawTypes.firstOrNull() == LOCALE_CLASS) { rawTypes.drop(1) } else { rawTypes @@ -172,9 +149,9 @@ class InvalidStringFormat : Rule() { formatArgs: List ) { if (formatString == null) { - report(CodeSmell(issue, Entity.from(expression), ERROR_UNKNOWN_FORMAT_STRING)) + report(Finding(Entity.from(expression), ERROR_UNKNOWN_FORMAT_STRING)) } else if (formatArgs.isEmpty()) { - report(CodeSmell(issue, Entity.from(expression), ERROR_UNKNOWN_FORMAT_STRING)) + report(Finding(Entity.from(expression), ERROR_UNKNOWN_FORMAT_STRING)) } else { val specifiers = SPECIFIER_REGEX.findAll(formatString).toList() checkSpecifiers(expression, specifiers, formatArgs) @@ -194,7 +171,7 @@ class InvalidStringFormat : Rule() { val argIndex = (ref ?: ++indexNoRef) - 1 if (argIndex >= formatArgs.size) { val message = ERROR_INVALID_ARGUMENT_COUNT.format(Locale.US, matchResult.value) - report(CodeSmell(issue, Entity.from(expression), message)) + report(Finding(Entity.from(expression), message)) } else { val argType = formatArgs[argIndex] checkArgumentType(expression, argType, type) @@ -230,11 +207,11 @@ class InvalidStringFormat : Rule() { if (allowedTypes == null) { val message = ERROR_INVALID_ARGUMENT_TYPE + "Unknown specifier %$type." - report(CodeSmell(issue, Entity.from(expression), message)) + report(Finding(Entity.from(expression), message)) } else if (!allowedTypes.contentEquals(ANY_TYPES) && argType !in allowedTypes) { val message = ERROR_INVALID_ARGUMENT_TYPE + " Expected one of ${allowedTypes.joinToString()}; but was $argType." - report(CodeSmell(issue, Entity.from(expression), message)) + report(Finding(Entity.from(expression), message)) } } @@ -243,6 +220,7 @@ class InvalidStringFormat : Rule() { companion object { private const val LOCALE_CLASS = "java.util.Locale" private const val STRING_CLASS = "kotlin.String" + private const val STRING_COMPANION_CLASS = "kotlin.String.Companion" private const val FORMAT_METHOD = "format" private const val SPECIFIER_STRING = 's' diff --git a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/PackageNameVisibility.kt b/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/PackageNameVisibility.kt index f831028190..2925f24886 100644 --- a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/PackageNameVisibility.kt +++ b/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/PackageNameVisibility.kt @@ -7,14 +7,11 @@ package com.datadog.tools.detekt.rules.sdk import com.datadog.tools.detekt.rules.AbstractTypedRule -import io.gitlab.arturbosch.detekt.api.CodeSmell -import io.gitlab.arturbosch.detekt.api.Config -import io.gitlab.arturbosch.detekt.api.Debt -import io.gitlab.arturbosch.detekt.api.Entity -import io.gitlab.arturbosch.detekt.api.Issue -import io.gitlab.arturbosch.detekt.api.Severity -import io.gitlab.arturbosch.detekt.api.config -import io.gitlab.arturbosch.detekt.api.internal.RequiresTypeResolution +import dev.detekt.api.Config +import dev.detekt.api.Entity +import dev.detekt.api.Finding +import dev.detekt.api.RequiresAnalysisApi +import dev.detekt.api.config import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.KtModifierListOwner import org.jetbrains.kotlin.psi.KtNamedDeclaration @@ -28,21 +25,17 @@ import org.jetbrains.kotlin.psi.KtProperty * `com.datadog.android.*.internal.*` package name. * @active */ -@RequiresTypeResolution class PackageNameVisibility( - ruleSetConfig: Config -) : AbstractTypedRule(ruleSetConfig) { + ruleSetConfig: Config = Config.empty +) : AbstractTypedRule( + ruleSetConfig, + "This rule reports when a top level declaration's visibility doesn't match the package naming policy." +), + RequiresAnalysisApi { private val withBreakingChanges: Boolean by config(defaultValue = true) private val ignoredAnnotations: List by config(defaultValue = emptyList()) - override val issue: Issue = Issue( - javaClass.simpleName, - Severity.Warning, - "This rule reports when a top level declaration's visibility doesn't match the package naming policy.", - Debt.FIVE_MINS - ) - private var currentPackageName: String = "" private var isCurrentPackageInternal: Boolean = false private var isEscapePackage: Boolean = false @@ -63,8 +56,7 @@ class PackageNameVisibility( if (isDeclarationInternal && !isCurrentPackageInternal) { if (decl !is KtProperty) { report( - CodeSmell( - issue, + Finding( Entity.from(decl), "Type ${decl.name} is marked Internal but is not in an internal package: $currentPackageName." ) @@ -73,8 +65,7 @@ class PackageNameVisibility( } else if (isCurrentPackageInternal && !isDeclarationInternal && !isEscapePackage) { if (withBreakingChanges && !isIgnoredAnnotation) { report( - CodeSmell( - issue, + Finding( Entity.from(decl), "Type ${decl.name} is marked Public but is in an internal package: $currentPackageName." ) diff --git a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/PreferTimeProvider.kt b/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/PreferTimeProvider.kt index 4eb4f3444b..196b5f7791 100644 --- a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/PreferTimeProvider.kt +++ b/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/PreferTimeProvider.kt @@ -6,14 +6,11 @@ package com.datadog.tools.detekt.rules.sdk -import io.gitlab.arturbosch.detekt.api.CodeSmell -import io.gitlab.arturbosch.detekt.api.Config -import io.gitlab.arturbosch.detekt.api.Debt -import io.gitlab.arturbosch.detekt.api.Entity -import io.gitlab.arturbosch.detekt.api.Issue -import io.gitlab.arturbosch.detekt.api.Rule -import io.gitlab.arturbosch.detekt.api.Severity -import io.gitlab.arturbosch.detekt.api.config +import dev.detekt.api.Config +import dev.detekt.api.Entity +import dev.detekt.api.Finding +import dev.detekt.api.Rule +import dev.detekt.api.config import org.jetbrains.kotlin.psi.KtCallExpression import org.jetbrains.kotlin.psi.KtDotQualifiedExpression import org.jetbrains.kotlin.psi.KtFile @@ -28,7 +25,10 @@ import org.jetbrains.kotlin.psi.KtFile */ class PreferTimeProvider( ruleSetConfig: Config = Config.empty -) : Rule(ruleSetConfig) { +) : Rule( + ruleSetConfig, + "Prefer using TimeProvider instead of static system time calls for better testability." +) { private val allowedFiles: List by config(defaultValue = DEFAULT_ALLOWED_FILES) { it.map { pattern -> Regex(pattern) } @@ -36,13 +36,6 @@ class PreferTimeProvider( private var currentFileName: String = "" - override val issue: Issue = Issue( - javaClass.simpleName, - Severity.Defect, - "Prefer using TimeProvider instead of static system time calls for better testability.", - Debt.TEN_MINS - ) - override fun visitKtFile(file: KtFile) { currentFileName = file.name super.visitKtFile(file) @@ -56,8 +49,7 @@ class PreferTimeProvider( val calleeName = expression.calleeExpression?.text ?: return if (calleeName in KOTLIN_TIME_FUNCTIONS) { report( - CodeSmell( - issue, + Finding( Entity.from(expression), "Use TimeProvider instead of $calleeName() for better testability." ) @@ -74,8 +66,7 @@ class PreferTimeProvider( val detectedMethod = detectProhibitedTimeMethod(expressionText) if (detectedMethod != null) { report( - CodeSmell( - issue, + Finding( Entity.from(expression), "Use TimeProvider instead of $detectedMethod for better testability." ) diff --git a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/RequireInternal.kt b/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/RequireInternal.kt index ee8234ce65..03568aaf41 100644 --- a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/RequireInternal.kt +++ b/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/RequireInternal.kt @@ -7,26 +7,19 @@ package com.datadog.tools.detekt.rules.sdk import com.datadog.tools.detekt.ext.isContainingEntryPointPublic -import io.gitlab.arturbosch.detekt.api.CodeSmell -import io.gitlab.arturbosch.detekt.api.Debt -import io.gitlab.arturbosch.detekt.api.Entity -import io.gitlab.arturbosch.detekt.api.Issue -import io.gitlab.arturbosch.detekt.api.Rule -import io.gitlab.arturbosch.detekt.api.Severity +import dev.detekt.api.Config +import dev.detekt.api.Entity +import dev.detekt.api.Finding +import dev.detekt.api.Rule import org.jetbrains.kotlin.psi.KtCallExpression /** * A rule to detekt `check`, `checkNotNull` calls. * @active */ -class RequireInternal : Rule() { - - override val issue: Issue = Issue( - javaClass.simpleName, - Severity.Defect, - "This rule reports when an exception is thrown.", - Debt.TWENTY_MINS - ) +class RequireInternal( + config: Config = Config.empty +) : Rule(config, "This rule reports when an exception is thrown.") { override fun visitCallExpression(expression: KtCallExpression) { val callee = expression.calleeExpression @@ -34,8 +27,7 @@ class RequireInternal : Rule() { val isCheckNotNullMethod = callee?.textMatches(REQUIRE_NOT_NULL_LITERAL) == true if ((isCheckMethod || isCheckNotNullMethod) && !expression.isContainingEntryPointPublic()) { report( - CodeSmell( - issue, + Finding( Entity.from(expression), message = "A require is called from an internal or private part of the code." ) diff --git a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/ThreadSafety.kt b/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/ThreadSafety.kt index 529f3b297f..494c1c0c13 100644 --- a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/ThreadSafety.kt +++ b/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/ThreadSafety.kt @@ -6,28 +6,22 @@ package com.datadog.tools.detekt.rules.sdk -import com.datadog.tools.detekt.ext.fqTypeName +import com.datadog.tools.detekt.ext.fqReceiverTypeName import com.datadog.tools.detekt.rules.AbstractTypedRule -import io.gitlab.arturbosch.detekt.api.CodeSmell -import io.gitlab.arturbosch.detekt.api.Config -import io.gitlab.arturbosch.detekt.api.Debt -import io.gitlab.arturbosch.detekt.api.Entity -import io.gitlab.arturbosch.detekt.api.Issue -import io.gitlab.arturbosch.detekt.api.Severity -import io.gitlab.arturbosch.detekt.api.config -import io.gitlab.arturbosch.detekt.rules.fqNameOrNull -import org.jetbrains.kotlin.descriptors.CallableDescriptor -import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor +import dev.detekt.api.Config +import dev.detekt.api.Entity +import dev.detekt.api.Finding +import dev.detekt.api.RequiresAnalysisApi +import dev.detekt.api.config +import org.jetbrains.kotlin.analysis.api.KaSession +import org.jetbrains.kotlin.analysis.api.analyze +import org.jetbrains.kotlin.analysis.api.annotations.KaAnnotated +import org.jetbrains.kotlin.analysis.api.resolution.KaFunctionCall +import org.jetbrains.kotlin.analysis.api.resolution.singleFunctionCallOrNull +import org.jetbrains.kotlin.analysis.api.resolution.symbol import org.jetbrains.kotlin.psi.KtCallExpression -import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtNameReferenceExpression import org.jetbrains.kotlin.psi.KtNamedFunction -import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall -import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall -import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull -import org.jetbrains.kotlin.types.SimpleType -import org.jetbrains.kotlin.types.getAbbreviation /** * A rule to ensure thread safety is ensured. @@ -36,8 +30,9 @@ import org.jetbrains.kotlin.types.getAbbreviation * @active */ class ThreadSafety( - ruleSetConfig: Config -) : AbstractTypedRule(ruleSetConfig) { + ruleSetConfig: Config = Config.empty +) : AbstractTypedRule(ruleSetConfig, "This rule reports when a method is called from the wrong thread."), + RequiresAnalysisApi { private val workerThreadSwitchingCalls: List by config(defaultValue = emptyList()) private val mainThreadSwitchingCalls: List by config(defaultValue = emptyList()) @@ -72,25 +67,21 @@ class ThreadSafety( } } + /** + * The information resolved for a call, gathered in a single analysis session. + * + * @param calleeGroup the thread group the called function is annotated with + * @param fullNames the fully qualified names the call can be referred to + */ + private data class ResolvedThreadCall( + val calleeGroup: ThreadGroup, + val fullNames: Set + ) + private val parentFunGroupStack: MutableList = mutableListOf(ThreadGroup.UNKNOWN) // region Rule - override val issue = Issue( - javaClass.simpleName, - Severity.Security, - "This rule reports when a method is called from the wrong thread.", - Debt.TWENTY_MINS - ) - - override fun visitKtFile(file: KtFile) { - if (bindingContext == BindingContext.EMPTY) { - println("Missing BindingContext when checking file:${file.virtualFilePath}") - return - } - super.visitKtFile(file) - } - override fun visitNamedFunction(function: KtNamedFunction) { val parentFunGroup: ThreadGroup = function.annotationEntries.mapNotNull { it.shortName?.asString()?.toMethodGroup() @@ -104,17 +95,14 @@ class ThreadSafety( override fun visitCallExpression(expression: KtCallExpression) { var wrapCallWith: ThreadGroup? = null - val resolvedCall = expression.getResolvedCall(bindingContext) - if (resolvedCall != null) { - val callDescriptor = resolvedCall.candidateDescriptor + val resolvedCall = analyze(expression) { resolveThreadCall(expression) } - checkCallExpression(expression, callDescriptor) - - val callFullNames = resolveCallFullNames(resolvedCall) + if (resolvedCall != null) { + checkCallExpression(expression, resolvedCall.calleeGroup) - wrapCallWith = if (workerThreadSwitchingCalls.any { callFullNames.contains(it) }) { + wrapCallWith = if (workerThreadSwitchingCalls.any { resolvedCall.fullNames.contains(it) }) { ThreadGroup.WORKER - } else if (mainThreadSwitchingCalls.any { callFullNames.contains(it) }) { + } else if (mainThreadSwitchingCalls.any { resolvedCall.fullNames.contains(it) }) { ThreadGroup.MAIN } else { null @@ -143,46 +131,37 @@ class ThreadSafety( // region Internal - private fun Iterable.extractMethodGroup(): ThreadGroup { - return firstNotNullOfOrNull { annotationDescriptor -> - val type = annotationDescriptor.type - when (type) { - is SimpleType -> { - val typeName = try { - type.fqNameOrNull()?.shortName()?.asString() - } catch (e: IllegalStateException) { - System.err.println(e.message) - null - } - if (typeName == null) { - println("\nUNABLE to get annotation name for $annotationDescriptor") - } - typeName?.toMethodGroup() - } + private fun KaSession.resolveThreadCall(expression: KtCallExpression): ResolvedThreadCall? { + val call = expression.resolveToCall()?.singleFunctionCallOrNull() ?: return null + return ResolvedThreadCall( + calleeGroup = extractMethodGroup(call.symbol), + fullNames = resolveCallFullNames(call) + ) + } - else -> { - println("\nUnknown type class for $type (${type.javaClass})") - null - } + private fun extractMethodGroup(symbol: KaAnnotated): ThreadGroup { + return symbol.annotations.firstNotNullOfOrNull { annotation -> + val typeName = annotation.classId?.shortClassName?.asString() + if (typeName == null) { + println("\nUNABLE to get annotation name for $annotation") } + typeName?.toMethodGroup() } ?: ThreadGroup.UNKNOWN } private fun String.toMethodGroup(): ThreadGroup? { - return ThreadGroup.values() + return ThreadGroup.entries .firstOrNull { it.className == this } } - private fun checkCallExpression(expression: KtCallExpression, callDescriptor: CallableDescriptor) { + private fun checkCallExpression(expression: KtCallExpression, calleeGroup: ThreadGroup) { val parentFunGroup = parentFunGroupStack.first() - val calleeGroup = callDescriptor.annotations.extractMethodGroup() val allowedCall = ThreadGroup.allowedCalls[parentFunGroup]?.contains(calleeGroup) if (allowedCall != true) { report( - CodeSmell( - issue, + Finding( Entity.from(expression), "Calling a ${calleeGroup.asAnnotation()} annotated fun " + "from a ${parentFunGroup.asAnnotation()} annotated fun " + @@ -192,27 +171,26 @@ class ThreadSafety( } } - private fun resolveCallFullNames(resolvedCall: ResolvedCall): Set { - val resolvedName = resolvedCall.candidateDescriptor.fqNameOrNull()?.asString() - if (resolvedName == null) return emptySet() - - val hostType = resolvedCall.dispatchReceiver - ?.type - - return if (hostType != null) { - val aliasedName = hostType - .getAbbreviation() - ?.constructor - ?.declarationDescriptor - ?.fqNameOrNull() - ?.let { - resolvedName.replace(hostType.fqTypeName(includeTypeArguments = false), it.asString()) - } + private fun KaSession.resolveCallFullNames(call: KaFunctionCall<*>): Set { + val callableId = call.symbol.callableId ?: return emptySet() + val resolvedName = callableId.asSingleFqName().asString() - listOf(resolvedName, aliasedName).filterNotNull().toSet() - } else { - setOf(resolvedName) - } + val dispatchReceiver = call.dispatchReceiver ?: return setOf(resolvedName) + val hostType = dispatchReceiver.type + val hostTypeName = fqReceiverTypeName(dispatchReceiver, includeTypeArguments = false) + + // `callableId` names the class that *declares* the function, but a call is usually configured + // under the type it is made on — `android.widget.LinearLayout.post` is declared on + // `android.view.View`. Report both spellings so either one matches. + val hostTypeCallName = "$hostTypeName.${callableId.callableName.asString()}" + + // when the host type is a type alias, also report the call under the aliased name + val aliasedName = hostType.abbreviation + ?.classId + ?.asFqNameString() + ?.let { resolvedName.replace(hostTypeName, it) } + + return listOfNotNull(resolvedName, hostTypeCallName, aliasedName).toSet() } // endregion diff --git a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/ThrowingInternalException.kt b/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/ThrowingInternalException.kt index f6328b6359..02352bf236 100644 --- a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/ThrowingInternalException.kt +++ b/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/ThrowingInternalException.kt @@ -7,32 +7,24 @@ package com.datadog.tools.detekt.rules.sdk import com.datadog.tools.detekt.ext.isContainingEntryPointPublic -import io.gitlab.arturbosch.detekt.api.CodeSmell -import io.gitlab.arturbosch.detekt.api.Debt -import io.gitlab.arturbosch.detekt.api.Entity -import io.gitlab.arturbosch.detekt.api.Issue -import io.gitlab.arturbosch.detekt.api.Rule -import io.gitlab.arturbosch.detekt.api.Severity +import dev.detekt.api.Config +import dev.detekt.api.Entity +import dev.detekt.api.Finding +import dev.detekt.api.Rule import org.jetbrains.kotlin.psi.KtThrowExpression /** * A rule to detekt thrown exceptions. * @active */ -class ThrowingInternalException : Rule() { - - override val issue: Issue = Issue( - javaClass.simpleName, - Severity.Defect, - "This rule reports when an exception is thrown from a private or internal class.", - Debt.TWENTY_MINS - ) +class ThrowingInternalException( + config: Config = Config.empty +) : Rule(config, "This rule reports when an exception is thrown from a private or internal class.") { override fun visitThrowExpression(expression: KtThrowExpression) { if (!expression.isContainingEntryPointPublic()) { report( - CodeSmell( - issue, + Finding( Entity.from(expression), message = "An exception is thrown from an internal or private part of the code." ) diff --git a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/TodoWithoutTask.kt b/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/TodoWithoutTask.kt index 122bc76103..787890caad 100644 --- a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/TodoWithoutTask.kt +++ b/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/TodoWithoutTask.kt @@ -6,15 +6,12 @@ package com.datadog.tools.detekt.rules.sdk -import io.gitlab.arturbosch.detekt.api.CodeSmell -import io.gitlab.arturbosch.detekt.api.Config -import io.gitlab.arturbosch.detekt.api.Debt -import io.gitlab.arturbosch.detekt.api.Entity -import io.gitlab.arturbosch.detekt.api.Issue -import io.gitlab.arturbosch.detekt.api.Rule -import io.gitlab.arturbosch.detekt.api.Severity -import io.gitlab.arturbosch.detekt.api.config -import org.jetbrains.kotlin.com.intellij.psi.PsiComment +import com.intellij.psi.PsiComment +import dev.detekt.api.Config +import dev.detekt.api.Entity +import dev.detekt.api.Finding +import dev.detekt.api.Rule +import dev.detekt.api.config import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.KtObjectDeclaration @@ -25,18 +22,11 @@ import org.jetbrains.kotlin.psi.KtProperty * @active */ class TodoWithoutTask( - ruleSetConfig: Config -) : Rule(ruleSetConfig) { + ruleSetConfig: Config = Config.empty +) : Rule(ruleSetConfig, "This rule reports when a TODO comment is missing a task number.") { private val deprecatedPrefixes: List by config(defaultValue = emptyList()) - override val issue: Issue = Issue( - javaClass.simpleName, - Severity.Defect, - "This rule reports when a TODO comment is missing a task number.", - Debt.TEN_MINS - ) - override fun visitObjectDeclaration(declaration: KtObjectDeclaration) { declaration.docComment?.let { reportIfInvalid(it) } super.visitObjectDeclaration(declaration) @@ -93,8 +83,7 @@ class TodoWithoutTask( message: String ) { report( - CodeSmell( - issue, + Finding( Entity.from(comment), message ) diff --git a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/UnsafeCallOnNullableType.kt b/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/UnsafeCallOnNullableType.kt index 8f854a4c21..ee447f1d8c 100644 --- a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/UnsafeCallOnNullableType.kt +++ b/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/UnsafeCallOnNullableType.kt @@ -6,12 +6,10 @@ package com.datadog.tools.detekt.rules.sdk -import io.gitlab.arturbosch.detekt.api.CodeSmell -import io.gitlab.arturbosch.detekt.api.Debt -import io.gitlab.arturbosch.detekt.api.Entity -import io.gitlab.arturbosch.detekt.api.Issue -import io.gitlab.arturbosch.detekt.api.Rule -import io.gitlab.arturbosch.detekt.api.Severity +import dev.detekt.api.Config +import dev.detekt.api.Entity +import dev.detekt.api.Finding +import dev.detekt.api.Rule import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.KtUnaryExpression @@ -21,22 +19,16 @@ import org.jetbrains.kotlin.psi.KtUnaryExpression * * This rule will report any use of `!!`, even on non-nullable types. */ -class UnsafeCallOnNullableType : Rule() { - - override val issue: Issue = Issue( - javaClass.simpleName, - Severity.Defect, - "This rule reports when an Unsafe null cast is used (ie: using !!).", - Debt.TWENTY_MINS - ) +class UnsafeCallOnNullableType( + config: Config = Config.empty +) : Rule(config, "This rule reports when an Unsafe null cast is used (ie: using !!).") { override fun visitUnaryExpression(expression: KtUnaryExpression) { super.visitUnaryExpression(expression) if (expression.operationToken == KtTokens.EXCLEXCL) { report( - CodeSmell( - issue, + Finding( Entity.from(expression), "Calling !! on a nullable type will throw a " + "NullPointerException at runtime in case the value is null. " + diff --git a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/UnsafeThirdPartyFunctionCall.kt b/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/UnsafeThirdPartyFunctionCall.kt index b2bb16e807..3d9163e54d 100644 --- a/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/UnsafeThirdPartyFunctionCall.kt +++ b/tools/detekt/src/main/kotlin/com/datadog/tools/detekt/rules/sdk/UnsafeThirdPartyFunctionCall.kt @@ -8,17 +8,13 @@ package com.datadog.tools.detekt.rules.sdk import com.datadog.tools.detekt.ext.fqTypeName import com.datadog.tools.detekt.rules.AbstractCallExpressionRule -import io.gitlab.arturbosch.detekt.api.CodeSmell -import io.gitlab.arturbosch.detekt.api.Config -import io.gitlab.arturbosch.detekt.api.Debt -import io.gitlab.arturbosch.detekt.api.Entity -import io.gitlab.arturbosch.detekt.api.Issue -import io.gitlab.arturbosch.detekt.api.Severity -import io.gitlab.arturbosch.detekt.api.config -import io.gitlab.arturbosch.detekt.api.internal.RequiresTypeResolution +import dev.detekt.api.Config +import dev.detekt.api.Entity +import dev.detekt.api.Finding +import dev.detekt.api.config +import org.jetbrains.kotlin.analysis.api.analyze import org.jetbrains.kotlin.psi.KtCallExpression import org.jetbrains.kotlin.psi.KtTryExpression -import org.jetbrains.kotlin.resolve.BindingContext import java.util.Stack /** @@ -28,10 +24,14 @@ import java.util.Stack * Third party functions are detected based on an internal package prefix: any method which has a * package name with this prefix is considered first party, anything else is third party. */ -@RequiresTypeResolution class UnsafeThirdPartyFunctionCall( - config: Config -) : AbstractCallExpressionRule(config, includeTypeArguments = false) { + config: Config = Config.empty +) : AbstractCallExpressionRule( + config, + "This rule reports when a call to an unsafe third party method is made " + + "(i.e. a function throwing an uncaught exception).", + includeTypeArguments = false +) { private val internalPackagePrefix: String by config(defaultValue = "") private val treatUnknownFunctionAsThrowing: Boolean by config(defaultValue = true) @@ -59,19 +59,12 @@ class UnsafeThirdPartyFunctionCall( // region Rule - override val issue: Issue = Issue( - javaClass.simpleName, - Severity.Defect, - "This rule reports when a call to an unsafe third party method is made " + - "(i.e. a function throwing an uncaught exception).", - Debt.TWENTY_MINS - ) - override fun visitTryExpression(expression: KtTryExpression) { val caughtTypes = expression.catchClauses - .mapNotNull { - val typeReference = it.catchParameter?.typeReference - bindingContext.get(BindingContext.TYPE, typeReference)?.fqTypeName() + .mapNotNull { catchClause -> + catchClause.catchParameter?.typeReference?.let { typeReference -> + analyze(typeReference) { fqTypeName(typeReference.type) } + } } caughtExceptions.push(caughtTypes) super.visitTryExpression(expression) @@ -141,7 +134,7 @@ class UnsafeThirdPartyFunctionCall( expression: KtCallExpression, message: String ) { - report(CodeSmell(issue, Entity.from(expression), message = message)) + report(Finding(Entity.from(expression), message = message)) } // endregion diff --git a/tools/detekt/src/main/resources/META-INF/services/io.gitlab.arturbosch.detekt.api.RuleSetProvider b/tools/detekt/src/main/resources/META-INF/services/dev.detekt.api.RuleSetProvider similarity index 100% rename from tools/detekt/src/main/resources/META-INF/services/io.gitlab.arturbosch.detekt.api.RuleSetProvider rename to tools/detekt/src/main/resources/META-INF/services/dev.detekt.api.RuleSetProvider diff --git a/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/pyramid/ApiSurfaceTest.kt b/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/pyramid/ApiSurfaceTest.kt index 905555f5e9..64fec7f3a1 100644 --- a/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/pyramid/ApiSurfaceTest.kt +++ b/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/pyramid/ApiSurfaceTest.kt @@ -6,15 +6,13 @@ package com.datadog.tools.detekt.rules.pyramid +import dev.detekt.test.TestConfig +import dev.detekt.test.lintWithContext +import dev.detekt.test.utils.KotlinEnvironmentContainer +import dev.detekt.test.utils.createEnvironment import fr.xgouchet.elmyr.junit5.ForgeExtension -import io.github.detekt.test.utils.KotlinCoreEnvironmentWrapper -import io.github.detekt.test.utils.createEnvironment -import io.gitlab.arturbosch.detekt.test.TestConfig -import io.gitlab.arturbosch.detekt.test.assertThat -import io.gitlab.arturbosch.detekt.test.compileAndLintWithContext import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import java.io.File @@ -22,18 +20,12 @@ import java.io.File @ExtendWith(ForgeExtension::class) internal class ApiSurfaceTest { - lateinit var kotlinEnv: KotlinCoreEnvironmentWrapper + val kotlinEnv: KotlinEnvironmentContainer = createEnvironment() val fileName = "apiSurfaceOutput.log" - @BeforeEach - fun setup() { - kotlinEnv = createEnvironment() - } - @AfterEach fun tearDown() { - kotlinEnv.dispose() File(fileName).delete() } @@ -55,7 +47,7 @@ internal class ApiSurfaceTest { // When val findings = ApiSurface(config) - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(0) @@ -85,7 +77,7 @@ internal class ApiSurfaceTest { // When val findings = ApiSurface(config) - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(0) @@ -115,7 +107,7 @@ internal class ApiSurfaceTest { // When val findings = ApiSurface(config) - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(0) diff --git a/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/CheckInternalTest.kt b/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/CheckInternalTest.kt index 266687aac5..50bec83aa2 100644 --- a/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/CheckInternalTest.kt +++ b/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/CheckInternalTest.kt @@ -6,10 +6,10 @@ package com.datadog.tools.detekt.rules.sdk +import dev.detekt.test.lint import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.junit5.ForgeExtension -import io.gitlab.arturbosch.detekt.test.assertThat -import io.gitlab.arturbosch.detekt.test.lint +import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith diff --git a/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/InvalidStringFormatTest.kt b/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/InvalidStringFormatTest.kt index 405cb63039..89a759c68c 100644 --- a/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/InvalidStringFormatTest.kt +++ b/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/InvalidStringFormatTest.kt @@ -6,30 +6,18 @@ package com.datadog.tools.detekt.rules.sdk +import dev.detekt.test.lintWithContext +import dev.detekt.test.utils.KotlinEnvironmentContainer +import dev.detekt.test.utils.createEnvironment import fr.xgouchet.elmyr.junit5.ForgeExtension -import io.github.detekt.test.utils.KotlinCoreEnvironmentWrapper -import io.github.detekt.test.utils.createEnvironment -import io.gitlab.arturbosch.detekt.test.assertThat -import io.gitlab.arturbosch.detekt.test.compileAndLintWithContext -import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.BeforeEach +import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @ExtendWith(ForgeExtension::class) internal class InvalidStringFormatTest { - lateinit var kotlinEnv: KotlinCoreEnvironmentWrapper - - @BeforeEach - fun setup() { - kotlinEnv = createEnvironment() - } - - @AfterEach - fun tearDown() { - kotlinEnv.dispose() - } + val kotlinEnv: KotlinEnvironmentContainer = createEnvironment() // region Test unresolved format string @@ -46,7 +34,7 @@ internal class InvalidStringFormatTest { // When val findings = InvalidStringFormat() - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(1) @@ -65,7 +53,7 @@ internal class InvalidStringFormatTest { // When val findings = InvalidStringFormat() - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(1) @@ -83,7 +71,7 @@ internal class InvalidStringFormatTest { // When val findings = InvalidStringFormat() - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(1) @@ -101,7 +89,7 @@ internal class InvalidStringFormatTest { // When val findings = InvalidStringFormat() - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(1) @@ -119,7 +107,7 @@ internal class InvalidStringFormatTest { // When val findings = InvalidStringFormat() - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(1) @@ -137,7 +125,7 @@ internal class InvalidStringFormatTest { // When val findings = InvalidStringFormat() - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(1) @@ -159,7 +147,7 @@ internal class InvalidStringFormatTest { // When val findings = InvalidStringFormat() - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(1) @@ -178,7 +166,7 @@ internal class InvalidStringFormatTest { // When val findings = InvalidStringFormat() - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(1) @@ -197,7 +185,7 @@ internal class InvalidStringFormatTest { // When val findings = InvalidStringFormat() - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(1) @@ -217,7 +205,7 @@ internal class InvalidStringFormatTest { // When val findings = InvalidStringFormat() - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(1) @@ -237,7 +225,7 @@ internal class InvalidStringFormatTest { // When val findings = InvalidStringFormat() - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(1) @@ -259,7 +247,7 @@ internal class InvalidStringFormatTest { // When val findings = InvalidStringFormat() - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(1) @@ -277,7 +265,7 @@ internal class InvalidStringFormatTest { // When val findings = InvalidStringFormat() - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(1) @@ -296,7 +284,7 @@ internal class InvalidStringFormatTest { // When val findings = InvalidStringFormat() - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(1) @@ -315,7 +303,7 @@ internal class InvalidStringFormatTest { // When val findings = InvalidStringFormat() - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(1) @@ -335,7 +323,7 @@ internal class InvalidStringFormatTest { // When val findings = InvalidStringFormat() - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(1) @@ -355,7 +343,7 @@ internal class InvalidStringFormatTest { // When val findings = InvalidStringFormat() - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(1) @@ -377,7 +365,7 @@ internal class InvalidStringFormatTest { // When val findings = InvalidStringFormat() - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(1) @@ -399,7 +387,7 @@ internal class InvalidStringFormatTest { // When val findings = InvalidStringFormat() - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(2) @@ -417,7 +405,7 @@ internal class InvalidStringFormatTest { // When val findings = InvalidStringFormat() - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(3) @@ -435,7 +423,7 @@ internal class InvalidStringFormatTest { // When val findings = InvalidStringFormat() - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(4) @@ -453,7 +441,7 @@ internal class InvalidStringFormatTest { // When val findings = InvalidStringFormat() - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(1) @@ -482,7 +470,7 @@ internal class InvalidStringFormatTest { // When val findings = InvalidStringFormat() - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).isEmpty() @@ -506,7 +494,7 @@ internal class InvalidStringFormatTest { // When val findings = InvalidStringFormat() - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).isEmpty() @@ -528,7 +516,7 @@ internal class InvalidStringFormatTest { // When val findings = InvalidStringFormat() - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).isEmpty() @@ -552,7 +540,7 @@ internal class InvalidStringFormatTest { // When val findings = InvalidStringFormat() - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).isEmpty() @@ -577,7 +565,7 @@ internal class InvalidStringFormatTest { // When val findings = InvalidStringFormat() - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).isEmpty() @@ -595,7 +583,7 @@ internal class InvalidStringFormatTest { // When val findings = InvalidStringFormat() - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).isEmpty() @@ -613,7 +601,7 @@ internal class InvalidStringFormatTest { // When val findings = InvalidStringFormat() - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).isEmpty() @@ -641,7 +629,7 @@ internal class InvalidStringFormatTest { // When val findings = InvalidStringFormat() - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).isEmpty() @@ -665,7 +653,7 @@ internal class InvalidStringFormatTest { // When val findings = InvalidStringFormat() - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(1) @@ -689,7 +677,7 @@ internal class InvalidStringFormatTest { // When val findings = InvalidStringFormat() - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).isEmpty() diff --git a/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/PackageNameVisibilityTest.kt b/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/PackageNameVisibilityTest.kt index d6d3166c38..03b6faede6 100644 --- a/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/PackageNameVisibilityTest.kt +++ b/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/PackageNameVisibilityTest.kt @@ -7,17 +7,15 @@ package com.datadog.tools.detekt.rules.sdk import com.datadog.tools.detekt.rules.test.FakeAnnotation +import dev.detekt.test.TestConfig +import dev.detekt.test.lintWithContext +import dev.detekt.test.utils.KotlinEnvironmentContainer +import dev.detekt.test.utils.createEnvironment import fr.xgouchet.elmyr.annotation.BoolForgery import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.annotation.StringForgeryType import fr.xgouchet.elmyr.junit5.ForgeExtension -import io.github.detekt.test.utils.KotlinCoreEnvironmentWrapper -import io.github.detekt.test.utils.createEnvironment -import io.gitlab.arturbosch.detekt.test.TestConfig -import io.gitlab.arturbosch.detekt.test.assertThat -import io.gitlab.arturbosch.detekt.test.lint -import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.BeforeEach +import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import org.junit.jupiter.params.ParameterizedTest @@ -28,7 +26,7 @@ import java.util.stream.Stream @ExtendWith(ForgeExtension::class) class PackageNameVisibilityTest { - lateinit var kotlinEnv: KotlinCoreEnvironmentWrapper + val kotlinEnv: KotlinEnvironmentContainer = createEnvironment() @BoolForgery var fakeWithBreakingChanges: Boolean = false @@ -36,16 +34,6 @@ class PackageNameVisibilityTest { @StringForgery(StringForgeryType.ALPHABETICAL) lateinit var fakeIgnoredAnnotation: String - @BeforeEach - fun setup() { - kotlinEnv = createEnvironment() - } - - @AfterEach - fun tearDown() { - kotlinEnv.dispose() - } - @ParameterizedTest @MethodSource("types") fun `detekt internal top level type in public package`(type: String) { @@ -58,7 +46,7 @@ class PackageNameVisibilityTest { } """.trimIndent() - val findings = PackageNameVisibility(config).lint(code) + val findings = PackageNameVisibility(config).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(1) } @@ -73,7 +61,7 @@ class PackageNameVisibilityTest { } """.trimIndent() - val findings = PackageNameVisibility(config).lint(code) + val findings = PackageNameVisibility(config).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(1) } @@ -92,7 +80,7 @@ class PackageNameVisibilityTest { } """.trimIndent() - val findings = PackageNameVisibility(config).lint(code) + val findings = PackageNameVisibility(config).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(1) } @@ -107,7 +95,7 @@ class PackageNameVisibilityTest { internal $field foo: String = "" """.trimIndent() - val findings = PackageNameVisibility(config).lint(code) + val findings = PackageNameVisibility(config).lintWithContext(kotlinEnv, code) assertThat(findings).isEmpty() } @@ -123,7 +111,7 @@ class PackageNameVisibilityTest { } """.trimIndent() - val findings = PackageNameVisibility(config).lint(code) + val findings = PackageNameVisibility(config).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(1) } @@ -138,7 +126,7 @@ class PackageNameVisibilityTest { } """.trimIndent() - val findings = PackageNameVisibility(config).lint(code) + val findings = PackageNameVisibility(config).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(1) } @@ -153,7 +141,7 @@ class PackageNameVisibilityTest { } """.trimIndent() - val findings = PackageNameVisibility(config).lint(code) + val findings = PackageNameVisibility(config).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(1) } @@ -168,7 +156,7 @@ class PackageNameVisibilityTest { $field foo: String = "" """.trimIndent() - val findings = PackageNameVisibility(config).lint(code) + val findings = PackageNameVisibility(config).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(1) } @@ -184,7 +172,7 @@ class PackageNameVisibilityTest { } """.trimIndent() - val findings = PackageNameVisibility(config).lint(code) + val findings = PackageNameVisibility(config).lintWithContext(kotlinEnv, code) assertThat(findings).isEmpty() } @@ -199,7 +187,7 @@ class PackageNameVisibilityTest { } """.trimIndent() - val findings = PackageNameVisibility(config).lint(code) + val findings = PackageNameVisibility(config).lintWithContext(kotlinEnv, code) assertThat(findings).isEmpty() } @@ -214,7 +202,7 @@ class PackageNameVisibilityTest { } """.trimIndent() - val findings = PackageNameVisibility(config).lint(code) + val findings = PackageNameVisibility(config).lintWithContext(kotlinEnv, code) assertThat(findings).isEmpty() } @@ -229,7 +217,7 @@ class PackageNameVisibilityTest { internal $field foo: String = "" """.trimIndent() - val findings = PackageNameVisibility(config).lint(code) + val findings = PackageNameVisibility(config).lintWithContext(kotlinEnv, code) assertThat(findings).isEmpty() } @@ -249,7 +237,7 @@ class PackageNameVisibilityTest { } """.trimIndent() - val findings = PackageNameVisibility(config).lint(code) + val findings = PackageNameVisibility(config).lintWithContext(kotlinEnv, code) assertThat(findings).isEmpty() } @@ -268,7 +256,7 @@ class PackageNameVisibilityTest { } """.trimIndent() - val findings = PackageNameVisibility(config).lint(code) + val findings = PackageNameVisibility(config).lintWithContext(kotlinEnv, code) assertThat(findings).isEmpty() } @@ -287,7 +275,7 @@ class PackageNameVisibilityTest { } """.trimIndent() - val findings = PackageNameVisibility(config).lint(code) + val findings = PackageNameVisibility(config).lintWithContext(kotlinEnv, code) assertThat(findings).isEmpty() } @@ -306,7 +294,7 @@ class PackageNameVisibilityTest { internal $field foo: String = "" """.trimIndent() - val findings = PackageNameVisibility(config).lint(code) + val findings = PackageNameVisibility(config).lintWithContext(kotlinEnv, code) assertThat(findings).isEmpty() } @@ -323,7 +311,7 @@ class PackageNameVisibilityTest { } """.trimIndent() - val findings = PackageNameVisibility(config).lint(code) + val findings = PackageNameVisibility(config).lintWithContext(kotlinEnv, code) assertThat(findings).isEmpty() } @@ -339,7 +327,7 @@ class PackageNameVisibilityTest { } """.trimIndent() - val findings = PackageNameVisibility(config).lint(code) + val findings = PackageNameVisibility(config).lintWithContext(kotlinEnv, code) assertThat(findings).isEmpty() } @@ -355,7 +343,7 @@ class PackageNameVisibilityTest { } """.trimIndent() - val findings = PackageNameVisibility(config).lint(code) + val findings = PackageNameVisibility(config).lintWithContext(kotlinEnv, code) assertThat(findings).isEmpty() } @@ -372,7 +360,7 @@ class PackageNameVisibilityTest { } """.trimIndent() - val findings = PackageNameVisibility(config).lint(code) + val findings = PackageNameVisibility(config).lintWithContext(kotlinEnv, code) assertThat(findings).isEmpty() } @@ -389,7 +377,7 @@ class PackageNameVisibilityTest { } """.trimIndent() - val findings = PackageNameVisibility(config).lint(code) + val findings = PackageNameVisibility(config).lintWithContext(kotlinEnv, code) assertThat(findings).isEmpty() } @@ -405,7 +393,7 @@ class PackageNameVisibilityTest { } """.trimIndent() - val findings = PackageNameVisibility(config).lint(code) + val findings = PackageNameVisibility(config).lintWithContext(kotlinEnv, code) assertThat(findings).isEmpty() } @@ -421,7 +409,7 @@ class PackageNameVisibilityTest { } """.trimIndent() - val findings = PackageNameVisibility(config).lint(code) + val findings = PackageNameVisibility(config).lintWithContext(kotlinEnv, code) assertThat(findings).isEmpty() } @@ -438,7 +426,7 @@ class PackageNameVisibilityTest { } """.trimIndent() - val findings = PackageNameVisibility(config).lint(code) + val findings = PackageNameVisibility(config).lintWithContext(kotlinEnv, code) assertThat(findings).isEmpty() } diff --git a/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/PreferTimeProviderTest.kt b/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/PreferTimeProviderTest.kt index 70cf87c50f..7d8547dec2 100644 --- a/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/PreferTimeProviderTest.kt +++ b/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/PreferTimeProviderTest.kt @@ -6,12 +6,12 @@ package com.datadog.tools.detekt.rules.sdk +import dev.detekt.api.Config +import dev.detekt.test.TestConfig +import dev.detekt.test.lint import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.junit5.ForgeExtension -import io.gitlab.arturbosch.detekt.api.Config -import io.gitlab.arturbosch.detekt.test.TestConfig -import io.gitlab.arturbosch.detekt.test.assertThat -import io.gitlab.arturbosch.detekt.test.lint +import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith diff --git a/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/RequireInternalTest.kt b/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/RequireInternalTest.kt index 7e2e63c0e4..a0b9729489 100644 --- a/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/RequireInternalTest.kt +++ b/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/RequireInternalTest.kt @@ -6,10 +6,10 @@ package com.datadog.tools.detekt.rules.sdk +import dev.detekt.test.lint import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.junit5.ForgeExtension -import io.gitlab.arturbosch.detekt.test.assertThat -import io.gitlab.arturbosch.detekt.test.lint +import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith diff --git a/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/ThreadSafetyTest.kt b/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/ThreadSafetyTest.kt index 2f8a4c1ace..9c29c6e97b 100644 --- a/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/ThreadSafetyTest.kt +++ b/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/ThreadSafetyTest.kt @@ -6,47 +6,24 @@ package com.datadog.tools.detekt.rules.sdk -import android.webkit.JavascriptInterface -import androidx.annotation.MainThread -import io.github.detekt.test.utils.KotlinCoreEnvironmentWrapper -import io.github.detekt.test.utils.createEnvironment -import io.gitlab.arturbosch.detekt.api.Config -import io.gitlab.arturbosch.detekt.test.TestConfig -import io.gitlab.arturbosch.detekt.test.assertThat -import io.gitlab.arturbosch.detekt.test.compileAndLintWithContext -import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.BeforeEach +import dev.detekt.api.Config +import dev.detekt.test.TestConfig +import dev.detekt.test.lintWithContext +import dev.detekt.test.utils.KotlinEnvironmentContainer +import dev.detekt.test.utils.createEnvironment +import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test -import java.io.File class ThreadSafetyTest { - lateinit var kotlinEnv: KotlinCoreEnvironmentWrapper + // detekt 2.x derives `jvmClasspathRoots` from the test JVM's context classloader, which already + // carries androidx-annotation and robolectric's android-all — so the `@MainThread` and + // `@JavascriptInterface` symbols the snippets below reference resolve without passing explicit + // roots, as detekt 1.x required. + val kotlinEnv: KotlinEnvironmentContainer = createEnvironment() - lateinit var fakeConfig: Config - - @BeforeEach - fun setup() { - fakeConfig = TestConfig() - kotlinEnv = createEnvironment( - // by some reason all Android-related classes are not discovered by DetektKt compiler, - // so need to add them explicitly - additionalRootPaths = listOf( - MainThread::class, - // alternatively we could get it by reading sdk.dir property and pulling android.jar - // for the right API, but since we need only a single annotation symbol, let's just - // import robolectric instead - JavascriptInterface::class - ).map { - File(it.java.protectionDomain.codeSource.location.path) - } - ) - } - - @AfterEach - fun tearDown() { - kotlinEnv.dispose() - } + // A handful of tests replace this with a config of their own before linting. + var fakeConfig: Config = TestConfig() @Test fun `detekt call from javascript thread to javascript interface`() { @@ -66,7 +43,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(1) } @@ -89,7 +66,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(1) } @@ -112,7 +89,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(1) } @@ -135,7 +112,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(1) } @@ -158,7 +135,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(1) } @@ -179,7 +156,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(1) } @@ -202,7 +179,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(0) } @@ -224,7 +201,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(0) } @@ -247,7 +224,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(1) } @@ -270,7 +247,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(1) } @@ -293,7 +270,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(1) } @@ -314,7 +291,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(1) } @@ -337,7 +314,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(1) } @@ -360,7 +337,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(1) } @@ -383,7 +360,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(0) } @@ -405,7 +382,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(0) } @@ -428,7 +405,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(1) } @@ -449,7 +426,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(1) } @@ -472,7 +449,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(1) } @@ -495,7 +472,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(1) } @@ -517,7 +494,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(0) } @@ -540,7 +517,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(0) } @@ -563,7 +540,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(1) } @@ -584,7 +561,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(1) } @@ -607,7 +584,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(0) } @@ -630,7 +607,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(0) } @@ -653,7 +630,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(0) } @@ -676,7 +653,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(0) } @@ -698,7 +675,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(0) } @@ -719,7 +696,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(0) } @@ -740,7 +717,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(0) } @@ -761,7 +738,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(0) } @@ -782,7 +759,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(0) } @@ -803,7 +780,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(0) } @@ -824,7 +801,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(0) } @@ -842,7 +819,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(0) } @@ -869,7 +846,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(0) } @@ -901,7 +878,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(0) } @@ -933,7 +910,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(0) } @@ -960,7 +937,7 @@ class ThreadSafetyTest { } """.trimIndent() - val findings = ThreadSafety(fakeConfig).compileAndLintWithContext(kotlinEnv.env, code) + val findings = ThreadSafety(fakeConfig).lintWithContext(kotlinEnv, code) assertThat(findings).hasSize(0) } } diff --git a/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/ThrowingInternalExceptionTest.kt b/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/ThrowingInternalExceptionTest.kt index 386d20cb17..18a3820692 100644 --- a/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/ThrowingInternalExceptionTest.kt +++ b/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/ThrowingInternalExceptionTest.kt @@ -6,10 +6,10 @@ package com.datadog.tools.detekt.rules.sdk +import dev.detekt.test.lint import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.junit5.ForgeExtension -import io.gitlab.arturbosch.detekt.test.assertThat -import io.gitlab.arturbosch.detekt.test.lint +import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith diff --git a/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/TodoWithoutTaskTest.kt b/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/TodoWithoutTaskTest.kt index c8c401e357..444414f042 100644 --- a/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/TodoWithoutTaskTest.kt +++ b/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/TodoWithoutTaskTest.kt @@ -6,13 +6,13 @@ package com.datadog.tools.detekt.rules.sdk +import dev.detekt.api.Config +import dev.detekt.test.TestConfig +import dev.detekt.test.lint import fr.xgouchet.elmyr.annotation.IntForgery import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.junit5.ForgeExtension -import io.gitlab.arturbosch.detekt.api.Config -import io.gitlab.arturbosch.detekt.test.TestConfig -import io.gitlab.arturbosch.detekt.test.assertThat -import io.gitlab.arturbosch.detekt.test.lint +import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith diff --git a/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/UnsafeCallOnNullableTypeTest.kt b/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/UnsafeCallOnNullableTypeTest.kt index 67c2fca6fa..3f6fa48d6b 100644 --- a/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/UnsafeCallOnNullableTypeTest.kt +++ b/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/UnsafeCallOnNullableTypeTest.kt @@ -6,9 +6,9 @@ package com.datadog.tools.detekt.rules.sdk +import dev.detekt.test.lint import fr.xgouchet.elmyr.junit5.ForgeExtension -import io.gitlab.arturbosch.detekt.test.assertThat -import io.gitlab.arturbosch.detekt.test.lint +import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith diff --git a/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/UnsafeThirdPartyFunctionCallTest.kt b/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/UnsafeThirdPartyFunctionCallTest.kt index b2ea325a08..3d7f93f30a 100644 --- a/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/UnsafeThirdPartyFunctionCallTest.kt +++ b/tools/detekt/src/test/kotlin/com/datadog/tools/detekt/rules/sdk/UnsafeThirdPartyFunctionCallTest.kt @@ -6,32 +6,20 @@ package com.datadog.tools.detekt.rules.sdk +import dev.detekt.api.Config +import dev.detekt.test.TestConfig +import dev.detekt.test.lintWithContext +import dev.detekt.test.utils.KotlinEnvironmentContainer +import dev.detekt.test.utils.createEnvironment import fr.xgouchet.elmyr.junit5.ForgeExtension -import io.github.detekt.test.utils.KotlinCoreEnvironmentWrapper -import io.github.detekt.test.utils.createEnvironment -import io.gitlab.arturbosch.detekt.api.Config -import io.gitlab.arturbosch.detekt.test.TestConfig -import io.gitlab.arturbosch.detekt.test.assertThat -import io.gitlab.arturbosch.detekt.test.compileAndLintWithContext -import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.BeforeEach +import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @ExtendWith(ForgeExtension::class) internal class UnsafeThirdPartyFunctionCallTest { - lateinit var kotlinEnv: KotlinCoreEnvironmentWrapper - - @BeforeEach - fun setup() { - kotlinEnv = createEnvironment() - } - - @AfterEach - fun tearDown() { - kotlinEnv.dispose() - } + val kotlinEnv: KotlinEnvironmentContainer = createEnvironment() @Test fun `ignore call on internal type`() { @@ -48,7 +36,7 @@ internal class UnsafeThirdPartyFunctionCallTest { // When val findings = UnsafeThirdPartyFunctionCall(config) - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(0) @@ -72,7 +60,7 @@ internal class UnsafeThirdPartyFunctionCallTest { // When val findings = UnsafeThirdPartyFunctionCall(config) - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(1) @@ -100,7 +88,7 @@ internal class UnsafeThirdPartyFunctionCallTest { // When val findings = UnsafeThirdPartyFunctionCall(config) - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(0) @@ -120,7 +108,7 @@ internal class UnsafeThirdPartyFunctionCallTest { // When val findings = UnsafeThirdPartyFunctionCall(TestConfig()) - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(1) @@ -143,7 +131,7 @@ internal class UnsafeThirdPartyFunctionCallTest { // When val findings = UnsafeThirdPartyFunctionCall(config) - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(1) @@ -163,7 +151,7 @@ internal class UnsafeThirdPartyFunctionCallTest { // When val findings = UnsafeThirdPartyFunctionCall(TestConfig()) - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(1) @@ -184,7 +172,7 @@ internal class UnsafeThirdPartyFunctionCallTest { // When val findings = UnsafeThirdPartyFunctionCall(TestConfig()) - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(1) @@ -214,7 +202,7 @@ internal class UnsafeThirdPartyFunctionCallTest { // When val findings = UnsafeThirdPartyFunctionCall(config) - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(1) @@ -243,7 +231,7 @@ internal class UnsafeThirdPartyFunctionCallTest { // When val findings = UnsafeThirdPartyFunctionCall(config) - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(1) @@ -268,7 +256,7 @@ internal class UnsafeThirdPartyFunctionCallTest { // When val findings = UnsafeThirdPartyFunctionCall(TestConfig()) - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(1) @@ -300,7 +288,7 @@ internal class UnsafeThirdPartyFunctionCallTest { // When val findings = UnsafeThirdPartyFunctionCall(config) - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(0) @@ -330,7 +318,7 @@ internal class UnsafeThirdPartyFunctionCallTest { // When val findings = UnsafeThirdPartyFunctionCall(config) - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(0) @@ -360,7 +348,7 @@ internal class UnsafeThirdPartyFunctionCallTest { // When val findings = UnsafeThirdPartyFunctionCall(config) - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(0) @@ -392,7 +380,7 @@ internal class UnsafeThirdPartyFunctionCallTest { // When val findings = UnsafeThirdPartyFunctionCall(config) - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(0) @@ -431,7 +419,7 @@ internal class UnsafeThirdPartyFunctionCallTest { // When val findings = UnsafeThirdPartyFunctionCall(config) - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(0) @@ -455,7 +443,7 @@ internal class UnsafeThirdPartyFunctionCallTest { // When val findings = UnsafeThirdPartyFunctionCall(config) - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(0) @@ -481,7 +469,7 @@ internal class UnsafeThirdPartyFunctionCallTest { // When val findings = UnsafeThirdPartyFunctionCall(config) - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(0) @@ -502,7 +490,7 @@ internal class UnsafeThirdPartyFunctionCallTest { // When val findings = UnsafeThirdPartyFunctionCall(config) - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(1) @@ -523,7 +511,7 @@ internal class UnsafeThirdPartyFunctionCallTest { // When val findings = UnsafeThirdPartyFunctionCall(config) - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(0) @@ -544,7 +532,7 @@ internal class UnsafeThirdPartyFunctionCallTest { // When val findings = UnsafeThirdPartyFunctionCall(config) - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(1) @@ -565,7 +553,7 @@ internal class UnsafeThirdPartyFunctionCallTest { // When val findings = UnsafeThirdPartyFunctionCall(config) - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(0) @@ -589,7 +577,7 @@ internal class UnsafeThirdPartyFunctionCallTest { // When val findings = UnsafeThirdPartyFunctionCall(Config.empty) - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(0) @@ -598,7 +586,7 @@ internal class UnsafeThirdPartyFunctionCallTest { @Test fun `ignore kotlin helper calls { with + run + also + println }`() { // Given - val config = TestConfig("knownSafeThirdPartyCalls" to "java.io.File.readBytes()") + val config = TestConfig("knownSafeThirdPartyCalls" to listOf("java.io.File.readBytes()")) val code = """ import java.io.File @@ -616,7 +604,7 @@ internal class UnsafeThirdPartyFunctionCallTest { // When val findings = UnsafeThirdPartyFunctionCall(config) - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(0) @@ -636,7 +624,7 @@ internal class UnsafeThirdPartyFunctionCallTest { // When val findings = UnsafeThirdPartyFunctionCall(Config.empty) - .compileAndLintWithContext(kotlinEnv.env, code) + .lintWithContext(kotlinEnv, code) // Then assertThat(findings).hasSize(0) diff --git a/tools/lint/src/main/kotlin/com/datadog/android/lint/InternalApiUsageDetector.kt b/tools/lint/src/main/kotlin/com/datadog/android/lint/InternalApiUsageDetector.kt index a34e0ebbd8..bb96306a6d 100644 --- a/tools/lint/src/main/kotlin/com/datadog/android/lint/InternalApiUsageDetector.kt +++ b/tools/lint/src/main/kotlin/com/datadog/android/lint/InternalApiUsageDetector.kt @@ -17,6 +17,7 @@ import com.android.tools.lint.detector.api.JavaContext import com.android.tools.lint.detector.api.Scope import com.android.tools.lint.detector.api.Severity import com.android.tools.lint.detector.api.SourceCodeScanner +import org.jetbrains.uast.UClass import org.jetbrains.uast.UElement import org.jetbrains.uast.getContainingUFile @@ -28,9 +29,8 @@ class InternalApiUsageDetector : Detector(), SourceCodeScanner { override fun isApplicableAnnotationUsage(type: AnnotationUsageType): Boolean { return type == AnnotationUsageType.METHOD_CALL || - type == AnnotationUsageType.ASSIGNMENT_RHS || type == AnnotationUsageType.CLASS_REFERENCE || - super.isApplicableAnnotationUsage(type) + type == AnnotationUsageType.FIELD_REFERENCE } override fun visitAnnotationUsage( @@ -39,6 +39,11 @@ class InternalApiUsageDetector : Detector(), SourceCodeScanner { annotationInfo: AnnotationInfo, usageInfo: AnnotationUsageInfo ) { + // For class-level annotations, only CLASS_REFERENCE represents actual usage. + // In lint 32.x with K2 UAST, constructor calls fire under multiple usage types + // (CLASS_REFERENCE, METHOD_CALL, ASSIGNMENT_RHS, etc.) causing duplicate reports. + if (annotationInfo.annotated is UClass && usageInfo.type != AnnotationUsageType.CLASS_REFERENCE) return + val packageName = element.getContainingUFile()?.packageName if (!packageName.isNullOrEmpty() && !packageName.startsWith("com.datadog")) { context.report( diff --git a/tools/noopfactory/src/main/kotlin/com/datadog/tools/noopfactory/NoOpFactorySymbolProcessor.kt b/tools/noopfactory/src/main/kotlin/com/datadog/tools/noopfactory/NoOpFactorySymbolProcessor.kt index c81f231d4d..e39c8c2ec0 100644 --- a/tools/noopfactory/src/main/kotlin/com/datadog/tools/noopfactory/NoOpFactorySymbolProcessor.kt +++ b/tools/noopfactory/src/main/kotlin/com/datadog/tools/noopfactory/NoOpFactorySymbolProcessor.kt @@ -20,6 +20,7 @@ import com.google.devtools.ksp.symbol.ClassKind import com.google.devtools.ksp.symbol.KSAnnotated import com.google.devtools.ksp.symbol.KSCallableReference import com.google.devtools.ksp.symbol.KSClassDeclaration +import com.google.devtools.ksp.symbol.KSDeclaration import com.google.devtools.ksp.symbol.KSFunctionDeclaration import com.google.devtools.ksp.symbol.KSPropertyDeclaration import com.google.devtools.ksp.symbol.KSType @@ -67,7 +68,6 @@ class NoOpFactorySymbolProcessor( ) : SymbolProcessor { private var invoked = false - private var requireOptInAnnotations: Set = emptySet() private val generatedNames = mutableMapOf() /** @inheritdoc */ @@ -76,7 +76,6 @@ class NoOpFactorySymbolProcessor( logger.info("Already invoked, ignoring") return emptyList() } - resolveRequireOptInAnnotations(resolver) val result = mutableListOf() resolver.getSymbolsWithAnnotation(NoOpImplementation::class.java.canonicalName) .filterIsInstance() @@ -98,16 +97,17 @@ class NoOpFactorySymbolProcessor( return result } - private fun resolveRequireOptInAnnotations(resolver: Resolver) { - requireOptInAnnotations = resolver - .getSymbolsWithAnnotation("kotlin.RequiresOptIn") - .filterIsInstance() - .filter { it.classKind == ClassKind.ANNOTATION_CLASS } - .map { - logger.info("Found RequiresOptIn annotation: ${it.qualifiedName?.asString()}") - it.asType(emptyList()) - } - .toSet() + /** + * Whether this declaration is an opt-in marker, i.e. an annotation class itself annotated with + * [RequiresOptIn]. This is checked on the declaration directly rather than by collecting every marker + * in the round up front: `Resolver.getSymbolsWithAnnotation` doesn't see markers declared in + * dependencies, and comparing `KSType` instances resolved through different paths is unreliable. + */ + private fun KSDeclaration.isRequiresOptInMarker(): Boolean { + if ((this as? KSClassDeclaration)?.classKind != ClassKind.ANNOTATION_CLASS) return false + return annotations.any { + it.annotationType.resolve().declaration.qualifiedName?.asString() == REQUIRES_OPT_IN_NAME + } } // region Internal Generation @@ -437,7 +437,7 @@ class NoOpFactorySymbolProcessor( // check if the function has any RequiresOptIn annotations and add OptIn annotation for the override function functionDeclaration .annotations - .filter { it.annotationType.resolve() in requireOptInAnnotations } + .filter { it.annotationType.resolve().declaration.isRequiresOptInMarker() } .forEach { funSpecBuilder.addAnnotation( AnnotationSpec.builder(ClassName("kotlin", "OptIn")) @@ -751,6 +751,7 @@ class NoOpFactorySymbolProcessor( companion object { private val ignoredFunctions = arrayOf("equals(other:kotlin.Any?)", "hashCode()", "toString()") private const val KOTLIN_COLLECTIONS_PACKAGE = "kotlin.collections" + private const val REQUIRES_OPT_IN_NAME = "kotlin.RequiresOptIn" private val VALID_KOTLIN_IDENTIFIER = Regex("^[A-Za-z_][A-Za-z0-9_]*$") } } diff --git a/tools/noopfactory/src/test/kotlin/com/datadog/tools/noopfactory/NoOpFactoryProviderTest.kt b/tools/noopfactory/src/test/kotlin/com/datadog/tools/noopfactory/NoOpFactoryProviderTest.kt index 1b43c8331b..7e7eeb0ea9 100644 --- a/tools/noopfactory/src/test/kotlin/com/datadog/tools/noopfactory/NoOpFactoryProviderTest.kt +++ b/tools/noopfactory/src/test/kotlin/com/datadog/tools/noopfactory/NoOpFactoryProviderTest.kt @@ -11,10 +11,12 @@ import com.google.devtools.ksp.processing.Dependencies import com.google.devtools.ksp.processing.SymbolProcessor import com.google.devtools.ksp.processing.SymbolProcessorEnvironment import com.google.devtools.ksp.processing.SymbolProcessorProvider +import com.tschuchort.compiletesting.JvmCompilationResult import com.tschuchort.compiletesting.KotlinCompilation import com.tschuchort.compiletesting.SourceFile -import com.tschuchort.compiletesting.symbolProcessorProviders +import com.tschuchort.compiletesting.configureKsp import org.assertj.core.api.Assertions.assertThat +import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.CsvSource @@ -22,6 +24,7 @@ import java.io.File import java.io.OutputStream import java.nio.file.FileAlreadyExistsException +@OptIn(ExperimentalCompilerApi::class) internal class NoOpFactoryProviderTest { @ParameterizedTest @@ -50,13 +53,16 @@ internal class NoOpFactoryProviderTest { val srcFile = File(checkNotNull(javaClass.getResource("/src/$srcFileName")).file) val experimentalApiAnnotationFile = File(checkNotNull(javaClass.getResource("/src/ExperimentalApi.kt")).file) val genFile = File(checkNotNull(javaClass.getResource("/gen/$genFileName")).file) - val kotlinSource = SourceFile.fromPath(srcFile) - val experimentalApiAnnotationSource = SourceFile.fromPath(experimentalApiAnnotationFile) + val kotlinSource = SourceFile.new(srcFile.name, srcFile.readText()) + val experimentalApiAnnotationSource = SourceFile.new( + experimentalApiAnnotationFile.name, + experimentalApiAnnotationFile.readText() + ) val result = KotlinCompilation().apply { inheritClassPath = true sources = listOf(kotlinSource, experimentalApiAnnotationSource) - symbolProcessorProviders = listOf(NoOpFactoryProvider()) + configureKsp { symbolProcessorProviders.add(NoOpFactoryProvider()) } }.compile() assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK) @@ -72,12 +78,12 @@ internal class NoOpFactoryProviderTest { ) fun `ignores invalid types`(srcFileName: String) { val srcFile = File(checkNotNull(javaClass.getResource("/src/$srcFileName")).file) - val kotlinSource = SourceFile.fromPath(srcFile) + val kotlinSource = SourceFile.new(srcFile.name, srcFile.readText()) val result = KotlinCompilation().apply { inheritClassPath = true sources = listOf(kotlinSource) - symbolProcessorProviders = listOf(NoOpFactoryProvider()) + configureKsp { symbolProcessorProviders.add(NoOpFactoryProvider()) } }.compile() assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK) @@ -100,12 +106,12 @@ internal class NoOpFactoryProviderTest { ) fun `ignores interfaces with restricted visibility`(srcFileName: String, noOpFileName: String) { val srcFile = File(checkNotNull(javaClass.getResource("/src/$srcFileName")).file) - val kotlinSource = SourceFile.fromPath(srcFile) + val kotlinSource = SourceFile.new(srcFile.name, srcFile.readText()) val result = KotlinCompilation().apply { inheritClassPath = true sources = listOf(kotlinSource) - symbolProcessorProviders = listOf(NoOpFactoryProvider()) + configureKsp { symbolProcessorProviders.add(NoOpFactoryProvider()) } }.compile() assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK) @@ -120,8 +126,8 @@ internal class NoOpFactoryProviderTest { // When val result = KotlinCompilation().apply { inheritClassPath = true - sources = listOf(SourceFile.fromPath(srcFile)) - symbolProcessorProviders = listOf(NoOpFactoryProvider()) + sources = listOf(SourceFile.new(srcFile.name, srcFile.readText())) + configureKsp { symbolProcessorProviders.add(NoOpFactoryProvider()) } }.compile() // Then @@ -152,8 +158,8 @@ internal class NoOpFactoryProviderTest { // When val result = KotlinCompilation().apply { inheritClassPath = true - sources = listOf(SourceFile.fromPath(srcFile)) - symbolProcessorProviders = listOf(NoOpFactoryProvider()) + sources = listOf(SourceFile.new(srcFile.name, srcFile.readText())) + configureKsp { symbolProcessorProviders.add(NoOpFactoryProvider()) } }.compile() // Then @@ -183,8 +189,8 @@ internal class NoOpFactoryProviderTest { // When val result = KotlinCompilation().apply { inheritClassPath = true - sources = listOf(SourceFile.fromPath(srcFile)) - symbolProcessorProviders = listOf(NoOpFactoryProvider()) + sources = listOf(SourceFile.new(srcFile.name, srcFile.readText())) + configureKsp { symbolProcessorProviders.add(NoOpFactoryProvider()) } }.compile() // Then @@ -213,8 +219,8 @@ internal class NoOpFactoryProviderTest { // When val result = KotlinCompilation().apply { inheritClassPath = true - sources = listOf(SourceFile.fromPath(srcFile)) - symbolProcessorProviders = listOf(NoOpFactoryProvider()) + sources = listOf(SourceFile.new(srcFile.name, srcFile.readText())) + configureKsp { symbolProcessorProviders.add(NoOpFactoryProvider()) } }.compile() // Then @@ -234,8 +240,8 @@ internal class NoOpFactoryProviderTest { // When val result = KotlinCompilation().apply { inheritClassPath = true - sources = listOf(SourceFile.fromPath(srcFile)) - symbolProcessorProviders = listOf(ThrowingCodeGeneratorProvider()) + sources = listOf(SourceFile.new(srcFile.name, srcFile.readText())) + configureKsp { symbolProcessorProviders.add(ThrowingCodeGeneratorProvider()) } }.compile() // Then @@ -255,8 +261,8 @@ internal class NoOpFactoryProviderTest { // When val result = KotlinCompilation().apply { inheritClassPath = true - sources = listOf(SourceFile.fromPath(srcFile)) - symbolProcessorProviders = listOf(ThrowingCodeGeneratorProvider()) + sources = listOf(SourceFile.new(srcFile.name, srcFile.readText())) + configureKsp { symbolProcessorProviders.add(ThrowingCodeGeneratorProvider()) } }.compile() // Then @@ -268,14 +274,14 @@ internal class NoOpFactoryProviderTest { ) } - private fun KotlinCompilation.Result.assertNothingGenerated( + private fun JvmCompilationResult.assertNothingGenerated( generatedFileName: String ) { assertThat(sourceFor(generatedFileName)) .isNull() } - private fun KotlinCompilation.Result.assertGeneratedFileEquals( + private fun JvmCompilationResult.assertGeneratedFileEquals( generatedFileName: String, expectedContent: String ) { @@ -283,13 +289,13 @@ internal class NoOpFactoryProviderTest { .isEqualTo(expectedContent) } - private fun KotlinCompilation.Result.sourceFor(fileName: String): String? { + private fun JvmCompilationResult.sourceFor(fileName: String): String? { val kspGeneratedSources = getKspGeneratedSources() return kspGeneratedSources.find { it.name == fileName } ?.readText() } - private fun KotlinCompilation.Result.getKspGeneratedSources(): List { + private fun JvmCompilationResult.getKspGeneratedSources(): List { val workingDir = outputDirectory.parentFile val kspWorkingDir = workingDir.resolve("ksp") val kspGeneratedDir = kspWorkingDir.resolve("sources") diff --git a/tools/noopfactory/src/test/resources/gen/NoOpEnumInterface.kt b/tools/noopfactory/src/test/resources/gen/NoOpEnumInterface.kt index 7fb65b6107..ec1e2da252 100644 --- a/tools/noopfactory/src/test/resources/gen/NoOpEnumInterface.kt +++ b/tools/noopfactory/src/test/resources/gen/NoOpEnumInterface.kt @@ -7,5 +7,5 @@ import kotlin.Suppress internal class NoOpEnumInterface : EnumInterface { override val weekDay: EnumInterface.WeekDay = EnumInterface.WeekDay.MON - override fun getWeekDay(): EnumInterface.WeekDay = EnumInterface.WeekDay.MON + override fun fetchWeekDay(): EnumInterface.WeekDay = EnumInterface.WeekDay.MON } diff --git a/tools/noopfactory/src/test/resources/src/EnumInterface.kt b/tools/noopfactory/src/test/resources/src/EnumInterface.kt index 09cb057148..2e3f8f1900 100644 --- a/tools/noopfactory/src/test/resources/src/EnumInterface.kt +++ b/tools/noopfactory/src/test/resources/src/EnumInterface.kt @@ -6,7 +6,7 @@ import com.datadog.tools.annotation.NoOpImplementation interface EnumInterface { enum class WeekDay { MON, TUE, WED, THU, FRI, SAT, SUN } - fun getWeekDay(): WeekDay + fun fetchWeekDay(): WeekDay val weekDay: WeekDay } diff --git a/tools/unit/build.gradle.kts b/tools/unit/build.gradle.kts index 37293e275b..572c56e638 100644 --- a/tools/unit/build.gradle.kts +++ b/tools/unit/build.gradle.kts @@ -4,17 +4,18 @@ * Copyright 2016-Present Datadog, Inc. */ +import com.android.build.api.variant.HostTestBuilder import com.datadog.gradle.config.AndroidConfig -import com.datadog.gradle.config.java11 -import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import com.datadog.gradle.config.java17 plugins { + // Applied before the Android plugin on purpose (not under "Analysis tools"): AGP 9 applies + // the Kotlin plugin itself, and ktlint-gradle 14.2.0 registers its Android source-set tasks + // twice when it comes after the Kotlin plugin. + id("ktlint") + // Build id("com.android.library") - // Applied before `kotlin("android")` on purpose (not under "Analysis tools"): ktlint-gradle - // 14.2.0 registers its Android source-set tasks twice when it comes after the Kotlin plugin. - id("ktlint") - kotlin("android") id("de.mobilej.unmock") id("datadogBuildConfig") } @@ -32,18 +33,7 @@ android { namespace = "com.datadog.tools.unit" compileOptions { - java11() - } - - sourceSets.named("main") { - java.srcDir("src/main/kotlin") - java.srcDir("src/main/java") - } - sourceSets.named("test") { - java.srcDir("src/test/kotlin") - } - sourceSets.named("androidTest") { - java.srcDir("src/androidTest/kotlin") + java17() } flavorDimensions += "platform" @@ -63,6 +53,16 @@ android { } } +androidComponents { + // AGP 9 disables host (unit) tests for non-test build types by default, so the + // release unit-test tasks (e.g. testJvmReleaseUnitTest, required by the root + // :unitTestTools aggregation) are no longer generated. Re-enable them here, mirroring + // androidLibraryConfig() which this module can't use because of its custom flavors. + beforeVariants { variant -> + variant.hostTests[HostTestBuilder.UNIT_TEST_TYPE]?.enable = true + } +} + dependencies { implementation(libs.kotlin) implementation(libs.bundles.jUnit5) @@ -76,11 +76,7 @@ unMock { keepStartingWith("org.json") } -// It has to target 11 even if it is for unit-tests and this lib is not client facing, because -// with bytecode of Java 17 there is an error: -// Cannot inline bytecode built with JVM target 17 into bytecode that is being built with JVM target 11 - datadogBuild { - applyKotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) + applyKotlinConfig() applyJunitConfig() } diff --git a/tools/unit/src/test/kotlin/com/datadog/tools/unit/extensions/JavaClassWithNestedStaticMock.java b/tools/unit/src/test/java/com/datadog/tools/unit/extensions/JavaClassWithNestedStaticMock.java similarity index 100% rename from tools/unit/src/test/kotlin/com/datadog/tools/unit/extensions/JavaClassWithNestedStaticMock.java rename to tools/unit/src/test/java/com/datadog/tools/unit/extensions/JavaClassWithNestedStaticMock.java diff --git a/tools/unit/src/test/kotlin/com/datadog/tools/unit/extensions/JavaClassWithStaticMock.java b/tools/unit/src/test/java/com/datadog/tools/unit/extensions/JavaClassWithStaticMock.java similarity index 100% rename from tools/unit/src/test/kotlin/com/datadog/tools/unit/extensions/JavaClassWithStaticMock.java rename to tools/unit/src/test/java/com/datadog/tools/unit/extensions/JavaClassWithStaticMock.java