diff --git a/android-sdk/build.gradle b/android-sdk/build.gradle index cb4d2a758..61e724602 100644 --- a/android-sdk/build.gradle +++ b/android-sdk/build.gradle @@ -1,5 +1,6 @@ plugins { id 'com.android.library' + id 'org.jetbrains.kotlin.android' id 'co.elastic.apm.dependency.embedder' } @@ -27,6 +28,15 @@ android { compileOptions { sourceCompatibility jvmCompatibility targetCompatibility jvmCompatibility + coreLibraryDesugaringEnabled = true + } + kotlinOptions { + jvmTarget = jvmCompatibility.toString() + } + testOptions { + unitTests { + includeAndroidResources = true + } } } @@ -38,6 +48,11 @@ shadowJar { relocate 'com.instacart.library.truetime', 'co.elastic.apm.android.truetime' } +tasks.withType(Test).configureEach { + useJUnitPlatform() + systemProperty("agent_version", project.version) +} + dependencies { api libs.opentelemetry.sdk api libs.opentelemetry.android @@ -58,7 +73,11 @@ dependencies { annotationProcessor 'co.elastic.apm.compile:processor' compileOnly 'co.elastic.apm.compile:processor' testImplementation libs.bundles.mocking - testImplementation libs.junit + testImplementation libs.bundles.junit testImplementation libs.mockwebserver testImplementation libs.opentelemetry.testing + testImplementation libs.robolectric + testImplementation libs.assertj + testRuntimeOnly libs.junit5.vintage + coreLibraryDesugaring(libs.coreLib) } \ No newline at end of file diff --git a/android-sdk/metadata/notice.properties b/android-sdk/metadata/notice.properties index 967662cd4..a2cf393d8 100644 --- a/android-sdk/metadata/notice.properties +++ b/android-sdk/metadata/notice.properties @@ -1 +1 @@ -dependencies.hash=116B097450368CFACC99BF27198E0C94 \ No newline at end of file +dependencies.hash=FC395F744DC7E6D0DFA00982EDA59801 \ No newline at end of file diff --git a/android-sdk/src/main/java/co/elastic/apm/android/sdk/ElasticApmAgent.java b/android-sdk/src/main/java/co/elastic/apm/android/sdk/ElasticApmAgent.java index 293c10765..d95440833 100644 --- a/android-sdk/src/main/java/co/elastic/apm/android/sdk/ElasticApmAgent.java +++ b/android-sdk/src/main/java/co/elastic/apm/android/sdk/ElasticApmAgent.java @@ -342,7 +342,7 @@ private PersistenceInitializer tryInitializePersistence(SignalConfiguration sign private AttributesVisitor getResourceAttributesVisitor() { return AttributesVisitor.compose( - new DeviceIdVisitor(), + new DeviceIdVisitor(configuration.deviceIdGenerator), new DeviceInfoVisitor(), new OsDescriptorVisitor(), new RuntimeDescriptorVisitor(), diff --git a/android-sdk/src/main/java/co/elastic/apm/android/sdk/ElasticApmConfiguration.java b/android-sdk/src/main/java/co/elastic/apm/android/sdk/ElasticApmConfiguration.java index 499ee97c6..4fc8d9181 100644 --- a/android-sdk/src/main/java/co/elastic/apm/android/sdk/ElasticApmConfiguration.java +++ b/android-sdk/src/main/java/co/elastic/apm/android/sdk/ElasticApmConfiguration.java @@ -23,6 +23,8 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.UUID; +import java.util.function.Supplier; import co.elastic.apm.android.sdk.configuration.logging.LogLevel; import co.elastic.apm.android.sdk.configuration.logging.LoggingPolicy; @@ -54,6 +56,7 @@ public final class ElasticApmConfiguration { public final List logFilters; public final List metricFilters; public final LoggingPolicy libraryLoggingPolicy; + final Supplier deviceIdGenerator; public static Builder builder() { return new Builder(); @@ -76,6 +79,7 @@ private ElasticApmConfiguration(Builder builder) { exportProtocol = builder.exportProtocol; libraryLoggingPolicy = builder.libraryLoggingPolicy; resource = builder.resource; + deviceIdGenerator = builder.deviceIdGenerator; spanFilters = Collections.unmodifiableList(new ArrayList<>(builder.spanFilters)); logFilters = Collections.unmodifiableList(new ArrayList<>(builder.logFilters)); metricFilters = Collections.unmodifiableList(new ArrayList<>(builder.metricFilters)); @@ -94,6 +98,7 @@ public static class Builder { private ExportProtocol exportProtocol = ExportProtocol.GRPC; private LoggingPolicy libraryLoggingPolicy = LoggingPolicy.getDefault(); private Resource resource = Resource.getDefault(); + private Supplier deviceIdGenerator; private final Set spanFilters = new HashSet<>(); private final Set logFilters = new HashSet<>(); private final Set metricFilters = new HashSet<>(); @@ -217,6 +222,11 @@ public Builder setResource(Resource resource) { return this; } + public Builder setDeviceIdGenerator(Supplier deviceIdGenerator) { + this.deviceIdGenerator = deviceIdGenerator; + return this; + } + /** * The span filter can be used to control which spans are exported and which shouldn't * leave the device. An implementation that always excludes all spans is essentially a way @@ -260,6 +270,9 @@ public ElasticApmConfiguration build() { if (persistenceConfiguration == null) { persistenceConfiguration = PersistenceConfiguration.builder().build(); } + if (deviceIdGenerator == null) { + deviceIdGenerator = () -> UUID.randomUUID().toString(); + } return new ElasticApmConfiguration(this); } } diff --git a/android-sdk/src/main/java/co/elastic/apm/android/sdk/attributes/resources/DeviceIdVisitor.java b/android-sdk/src/main/java/co/elastic/apm/android/sdk/attributes/resources/DeviceIdVisitor.java deleted file mode 100644 index f84300f5e..000000000 --- a/android-sdk/src/main/java/co/elastic/apm/android/sdk/attributes/resources/DeviceIdVisitor.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package co.elastic.apm.android.sdk.attributes.resources; - -import java.util.UUID; - -import co.elastic.apm.android.sdk.attributes.AttributesVisitor; -import co.elastic.apm.android.sdk.internal.services.Service; -import co.elastic.apm.android.sdk.internal.services.ServiceManager; -import co.elastic.apm.android.sdk.internal.services.preferences.PreferencesService; -import io.opentelemetry.api.common.AttributesBuilder; -import io.opentelemetry.semconv.ResourceAttributes; - -public class DeviceIdVisitor implements AttributesVisitor { - - private static final String DEVICE_ID_KEY = "device_id"; - - @Override - public void visit(AttributesBuilder builder) { - builder.put(ResourceAttributes.DEVICE_ID, getId()); - } - - private String getId() { - PreferencesService preferences = ServiceManager.get().getService(Service.Names.PREFERENCES); - String deviceId = preferences.retrieveString(DEVICE_ID_KEY); - - if (deviceId == null) { - deviceId = UUID.randomUUID().toString(); - preferences.store(DEVICE_ID_KEY, deviceId); - } - - return deviceId; - } -} diff --git a/android-sdk/src/main/java/co/elastic/apm/android/sdk/attributes/resources/DeviceIdVisitor.kt b/android-sdk/src/main/java/co/elastic/apm/android/sdk/attributes/resources/DeviceIdVisitor.kt new file mode 100644 index 000000000..b5fb9f0d2 --- /dev/null +++ b/android-sdk/src/main/java/co/elastic/apm/android/sdk/attributes/resources/DeviceIdVisitor.kt @@ -0,0 +1,55 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package co.elastic.apm.android.sdk.attributes.resources + +import co.elastic.apm.android.sdk.attributes.AttributesVisitor +import co.elastic.apm.android.sdk.internal.services.Service +import co.elastic.apm.android.sdk.internal.services.ServiceManager +import co.elastic.apm.android.sdk.internal.services.preferences.PreferencesService +import io.opentelemetry.api.common.AttributesBuilder +import io.opentelemetry.semconv.ResourceAttributes +import java.util.function.Supplier + +class DeviceIdVisitor(private val deviceIdGenerator: Supplier) : AttributesVisitor { + + override fun visit(builder: AttributesBuilder) { + builder.put(ResourceAttributes.DEVICE_ID, getId()) + } + + private fun getId(): String { + val preferences = + ServiceManager.get() + .getService( + Service.Names.PREFERENCES + ) + var deviceId = + preferences.retrieveString(DEVICE_ID_KEY) + + if (deviceId == null) { + deviceId = deviceIdGenerator.get() + preferences.store(DEVICE_ID_KEY, deviceId) + } + + return deviceId + } + + companion object { + private const val DEVICE_ID_KEY = "device_id" + } +} diff --git a/android-sdk/src/main/resources/META-INF/NOTICE b/android-sdk/src/main/resources/META-INF/NOTICE index c52ac542e..c1d611aad 100644 --- a/android-sdk/src/main/resources/META-INF/NOTICE +++ b/android-sdk/src/main/resources/META-INF/NOTICE @@ -7,6 +7,7 @@ This product includes software licensed under the 'Apache License Version 2.0' l - Android Lifecycle Process (https://developer.android.com/jetpack/androidx/releases/lifecycle#2.6.2) - com.github.instacart.truetime-android:library:3.5 + - Kotlin Stdlib (https://kotlinlang.org/) - okhttp (https://square.github.io/okhttp/) - OpenTelemetry Android (https://github.com/open-telemetry/opentelemetry-android) - OpenTelemetry Java (https://github.com/open-telemetry/opentelemetry-java) diff --git a/android-sdk/src/test/java/co/elastic/apm/android/sdk/integration/InstrumentationTest.kt b/android-sdk/src/test/java/co/elastic/apm/android/sdk/integration/InstrumentationTest.kt new file mode 100644 index 000000000..92b0bd01d --- /dev/null +++ b/android-sdk/src/test/java/co/elastic/apm/android/sdk/integration/InstrumentationTest.kt @@ -0,0 +1,297 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package co.elastic.apm.android.sdk.integration + +import android.Manifest +import android.content.Context +import android.net.ConnectivityManager +import android.net.Network +import android.net.NetworkCapabilities +import android.os.Build +import android.telephony.TelephonyManager +import co.elastic.apm.android.sdk.ElasticApmAgent +import co.elastic.apm.android.sdk.ElasticApmConfiguration +import co.elastic.apm.android.sdk.connectivity.Connectivity +import co.elastic.apm.android.sdk.connectivity.opentelemetry.SignalConfiguration +import io.mockk.every +import io.mockk.mockk +import io.opentelemetry.api.GlobalOpenTelemetry +import io.opentelemetry.api.OpenTelemetry +import io.opentelemetry.api.common.Attributes +import io.opentelemetry.sdk.logs.LogRecordProcessor +import io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessor +import io.opentelemetry.sdk.metrics.export.MetricReader +import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader +import io.opentelemetry.sdk.resources.Resource +import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat +import io.opentelemetry.sdk.testing.exporter.InMemoryLogRecordExporter +import io.opentelemetry.sdk.testing.exporter.InMemoryMetricExporter +import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter +import io.opentelemetry.sdk.trace.SpanProcessor +import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment +import org.robolectric.Shadows +import org.robolectric.annotation.Config +import org.robolectric.util.ReflectionHelpers + +@RunWith(RobolectricTestRunner::class) +class InstrumentationTest : SignalConfiguration { + private lateinit var spanExporter: InMemorySpanExporter + private lateinit var metricsReader: MetricReader + private lateinit var metricsExporter: InMemoryMetricExporter + private lateinit var logsExporter: InMemoryLogRecordExporter + private lateinit var originalConstants: Map + + companion object { + private const val RUNTIME_VERSION: String = "runtime-version" + private const val DEVICE_MODEL_NAME: String = "Device model name" + private const val DEVICE_MANUFACTURER: String = "Device manufacturer" + private const val SIM_OPERATOR: String = "123456" + private const val SIM_OPERATOR_NAME: String = "elasticphone" + private const val SIM_COUNTRY_ISO: String = "us" + private const val OS_BUILD: String = "OS Build" + private const val VERSION_CODE: Long = 10 + } + + @Before + fun setUp() { + spanExporter = InMemorySpanExporter.create() + metricsExporter = InMemoryMetricExporter.create() + logsExporter = InMemoryLogRecordExporter.create() + originalConstants = mapOf( + "MODEL" to Build.MODEL, + "MANUFACTURER" to Build.MANUFACTURER, + "VERSION.INCREMENTAL" to Build.VERSION.INCREMENTAL, + "java.vm.version" to System.getProperty("java.vm.version") + ) + ReflectionHelpers.setStaticField(Build::class.java, "MODEL", DEVICE_MODEL_NAME) + ReflectionHelpers.setStaticField(Build::class.java, "MANUFACTURER", DEVICE_MANUFACTURER) + ReflectionHelpers.setStaticField(Build.VERSION::class.java, "INCREMENTAL", OS_BUILD) + System.setProperty("java.vm.version", RUNTIME_VERSION) + setVersionCode(VERSION_CODE) + } + + @After + fun tearDown() { + ElasticApmAgent.resetForTest() + GlobalOpenTelemetry.resetForTest() + ReflectionHelpers.setStaticField(Build::class.java, "MODEL", originalConstants["MODEL"]) + ReflectionHelpers.setStaticField( + Build::class.java, + "MANUFACTURER", + originalConstants["MANUFACTURER"] + ) + System.setProperty("java.vm.version", originalConstants["java.vm.version"]) + setVersionCode(0) + } + + @Config(sdk = [24, Config.NEWEST_SDK]) + @Test + fun `Check resources`() { + val openTelemetry = getOtelInstance() + val expectedResource = Resource.builder() + .put("deployment.environment", "test") + .put("device.id", "device-id") + .put("device.manufacturer", DEVICE_MANUFACTURER) + .put("device.model.identifier", DEVICE_MODEL_NAME) + .put( + "os.description", + "Android ${Build.VERSION.RELEASE}, API level ${Build.VERSION.SDK_INT}, BUILD $OS_BUILD" + ) + .put("os.name", "Android") + .put("os.version", Build.VERSION.RELEASE) + .put("process.runtime.name", "Android Runtime") + .put("process.runtime.version", RUNTIME_VERSION) + .put("service.build", VERSION_CODE) + .put("service.name", "service-name") + .put("service.version", "0.0.0") + .put("telemetry.sdk.language", "java") + .put("telemetry.sdk.name", "android") + .put("telemetry.sdk.version", System.getProperty("agent_version")!!) + .build() + + openTelemetry.getTracer("SomeTracer").spanBuilder("SomeSpan").startSpan().end() + openTelemetry.logsBridge.get("LoggerScope").logRecordBuilder().emit() + openTelemetry.getMeter("MeterScope").counterBuilder("Counter").build().add(1) + metricsReader.forceFlush() + + val spanItems = spanExporter.finishedSpanItems + val logItems = logsExporter.finishedLogRecordItems + val metricItems = metricsExporter.finishedMetricItems + assertThat(spanItems).hasSize(1) + assertThat(logItems).hasSize(1) + assertThat(metricItems).hasSize(1) + assertThat(spanItems.first()).hasResource(expectedResource) + assertThat(logItems.first()).hasResource(expectedResource) + assertThat(metricItems.first()).hasResource(expectedResource) + } + + @Config(sdk = [24, Config.NEWEST_SDK]) + @Test + fun `Check global attributes`() { + val openTelemetry = getOtelInstance() + val expectedLogAttributes = Attributes.builder() + .put("session.id", "session-id") + .put("network.connection.type", "unavailable") + .build() + val expectedSpanAttributes = Attributes.builder() + .putAll(expectedLogAttributes) + .put("type", "mobile") + .put("screen.name", "unknown") + .build() + + openTelemetry.getTracer("SomeTracer").spanBuilder("SomeSpan").startSpan().end() + openTelemetry.logsBridge.get("LoggerScope").logRecordBuilder().emit() + + val spanItems = spanExporter.finishedSpanItems + val logItems = logsExporter.finishedLogRecordItems + assertThat(spanItems).hasSize(1) + assertThat(logItems).hasSize(1) + assertThat(spanItems.first()).hasAttributes(expectedSpanAttributes) + assertThat(logItems.first()).hasAttributes(expectedLogAttributes) + } + + @Config(sdk = [24, Config.NEWEST_SDK]) + @Test + fun `Check global attributes with cellular connectivity available`() { + val openTelemetry = getOtelInstance() + enableCellularDataAttr() + val expectedLogAttributes = Attributes.builder() + .put("session.id", "session-id") + .put("network.connection.type", "cell") + .put("network.connection.subtype", "EDGE") + .build() + val expectedSpanAttributes = Attributes.builder() + .putAll(expectedLogAttributes) + .put("type", "mobile") + .put("screen.name", "unknown") + .build() + + openTelemetry.getTracer("SomeTracer").spanBuilder("SomeSpan").startSpan().end() + openTelemetry.logsBridge.get("LoggerScope").logRecordBuilder().emit() + + val spanItems = spanExporter.finishedSpanItems + val logItems = logsExporter.finishedLogRecordItems + assertThat(spanItems).hasSize(1) + assertThat(logItems).hasSize(1) + assertThat(spanItems.first()).hasAttributes(expectedSpanAttributes) + assertThat(logItems.first()).hasAttributes(expectedLogAttributes) + } + + @Config(sdk = [24, Config.NEWEST_SDK]) + @Test + fun `Check global attributes with carrier info available`() { + val openTelemetry = getOtelInstance() + enableCarrierInfoAttrs() + val expectedLogAttributes = Attributes.builder() + .put("session.id", "session-id") + .put("network.connection.type", "unavailable") + .build() + val expectedSpanAttributes = Attributes.builder() + .putAll(expectedLogAttributes) + .put("network.carrier.mcc", "123") + .put("network.carrier.mnc", "456") + .put("network.carrier.name", "elasticphone") + .put("network.carrier.icc", "us") + .put("type", "mobile") + .put("screen.name", "unknown") + .build() + + openTelemetry.getTracer("SomeTracer").spanBuilder("SomeSpan").startSpan().end() + openTelemetry.logsBridge.get("LoggerScope").logRecordBuilder().emit() + + val spanItems = spanExporter.finishedSpanItems + val logItems = logsExporter.finishedLogRecordItems + assertThat(spanItems).hasSize(1) + assertThat(logItems).hasSize(1) + assertThat(spanItems.first()).hasAttributes(expectedSpanAttributes) + assertThat(logItems.first()).hasAttributes(expectedLogAttributes) + } + + private fun enableCellularDataAttr() { + val application = RuntimeEnvironment.getApplication() + Shadows.shadowOf(application) + .grantPermissions(Manifest.permission.READ_PHONE_STATE) + val shadowConnectivityManager = + Shadows.shadowOf(application.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?) + val shadowTelephonyManager = + Shadows.shadowOf(application.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager?) + val callbacks = ArrayList(shadowConnectivityManager.networkCallbacks) + val defaultNetworkCallback = callbacks[0] + + val capabilities = mockk() + every { + capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) + } returns true + shadowTelephonyManager.setDataNetworkType(TelephonyManager.NETWORK_TYPE_EDGE) + + defaultNetworkCallback.onCapabilitiesChanged(mockk(), capabilities) + } + + private fun enableCarrierInfoAttrs() { + val shadowTelephonyManager = + Shadows.shadowOf( + RuntimeEnvironment.getApplication() + .getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager? + ) + shadowTelephonyManager.setSimOperator(SIM_OPERATOR) + shadowTelephonyManager.setSimState(TelephonyManager.SIM_STATE_READY) + shadowTelephonyManager.setSimOperatorName(SIM_OPERATOR_NAME) + shadowTelephonyManager.setSimCountryIso(SIM_COUNTRY_ISO) + } + + private fun getOtelInstance(): OpenTelemetry { + ElasticApmAgent.initialize( + RuntimeEnvironment.getApplication(), ElasticApmConfiguration.builder() + .setServiceName("service-name") + .setServiceVersion("0.0.0") + .setDeploymentEnvironment("test") + .setSignalConfiguration(this) + .setDeviceIdGenerator { "device-id" } + .setSessionIdGenerator { "session-id" } + .build(), + Connectivity.simple("http://localhost") + ) + return GlobalOpenTelemetry.get() + } + + override fun getSpanProcessor(): SpanProcessor { + return SimpleSpanProcessor.create(spanExporter) + } + + override fun getLogProcessor(): LogRecordProcessor { + return SimpleLogRecordProcessor.create(logsExporter) + } + + override fun getMetricReader(): MetricReader { + metricsReader = PeriodicMetricReader.create(metricsExporter) + return metricsReader + } + + private fun setVersionCode(versionCode: Long) { + Shadows.shadowOf(RuntimeEnvironment.getApplication().packageManager) + .getInternalMutablePackageInfo(RuntimeEnvironment.getApplication().packageName).versionCode = + versionCode.toInt() + } +} \ No newline at end of file diff --git a/android-test/android-test-common/build.gradle b/android-test/android-test-common/build.gradle index 9fa4f8668..2c6458120 100644 --- a/android-test/android-test-common/build.gradle +++ b/android-test/android-test-common/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'com.android.library' version "$agp_version" + alias(rootLibs.plugins.androidLib) } android { @@ -27,6 +27,6 @@ android { } dependencies { - api "junit:junit:$junit_version" - api "co.elastic.apm:android-sdk:$agent_version" + api libs.junit + api "co.elastic.apm:android-sdk" } \ No newline at end of file diff --git a/android-test/app/build.gradle b/android-test/app/build.gradle index 27fea9356..326b0f4c7 100644 --- a/android-test/app/build.gradle +++ b/android-test/app/build.gradle @@ -1,18 +1,8 @@ -buildscript { - repositories { - mavenLocal() - google() - mavenCentral() - } - dependencies { - classpath "com.android.tools.build:gradle:$agp_version" - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.0" - classpath "co.elastic.apm:android-plugin:$agent_version" - } +plugins { + alias(rootLibs.plugins.androidApp) + alias(rootLibs.plugins.kotlin.android) + id 'co.elastic.apm.android' } -apply plugin: 'com.android.application' -apply plugin: 'org.jetbrains.kotlin.android' -apply plugin: 'co.elastic.apm.android' android { namespace = "co.elastic.apm.android.test" @@ -21,7 +11,7 @@ android { unitTests { includeAndroidResources = true all { - systemProperty "agentVersion", agent_version + systemProperty "agentVersion", agentVersion } } animationsDisabled = true @@ -61,22 +51,16 @@ elasticApm { } dependencies { - implementation 'androidx.core:core-ktx:1.7.0' - implementation 'androidx.appcompat:appcompat:1.5.1' - implementation 'androidx.constraintlayout:constraintlayout:2.1.4' - implementation "androidx.test.espresso:espresso-idling-resource:$espresso_version" implementation project(':android-test-common') - implementation "androidx.fragment:fragment-testing:1.6.1" - coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.4") - testImplementation "io.opentelemetry:opentelemetry-exporter-otlp:1.28.0" - testImplementation "org.mockito:mockito-core:$mockito_version" - testImplementation "org.mockito:mockito-inline:$mockito_version" - testImplementation 'org.robolectric:robolectric:4.12.1' - testImplementation "com.squareup.okhttp3:mockwebserver:$mockwebserver_version" - androidTestImplementation "androidx.test:core:1.5.0" - androidTestImplementation 'androidx.test.ext:junit:1.1.5' - androidTestImplementation 'androidx.test:runner:1.5.2' - androidTestImplementation "androidx.test.espresso:espresso-core:$espresso_version" - androidTestImplementation "androidx.test.espresso:espresso-contrib:$espresso_version" - androidTestImplementation "com.squareup.okhttp3:mockwebserver:$mockwebserver_version" + implementation libs.appCompat + implementation libs.espresso.idlingResource + implementation libs.fragmentTesting + coreLibraryDesugaring libs.coreLib + testImplementation libs.openTelemetry.exporter + testImplementation libs.robolectric + testImplementation libs.mockito + testImplementation libs.mockWebServer + androidTestImplementation libs.espresso.core + androidTestImplementation libs.espresso.contrib + androidTestImplementation libs.mockWebServer } \ No newline at end of file diff --git a/android-test/app/src/androidTest/java/co/elastic/apm/android/test/base/ActivityEspressoTest.java b/android-test/app/src/androidTest/java/co/elastic/apm/android/test/base/ActivityEspressoTest.java index beb49aafb..789566bfa 100644 --- a/android-test/app/src/androidTest/java/co/elastic/apm/android/test/base/ActivityEspressoTest.java +++ b/android-test/app/src/androidTest/java/co/elastic/apm/android/test/base/ActivityEspressoTest.java @@ -5,11 +5,9 @@ import androidx.test.core.app.ActivityScenario; import androidx.test.espresso.IdlingRegistry; import androidx.test.espresso.IdlingResource; -import androidx.test.ext.junit.rules.ActivityScenarioRule; import org.junit.After; import org.junit.Before; -import org.junit.Rule; import co.elastic.apm.android.test.activities.espresso.IdlingResourceProvider; diff --git a/android-test/app/src/test/java/co/elastic/apm/android/test/attributes/common/ResourcesApp.java b/android-test/app/src/test/java/co/elastic/apm/android/test/attributes/common/ResourcesApp.java deleted file mode 100644 index 788bfdfc3..000000000 --- a/android-test/app/src/test/java/co/elastic/apm/android/test/attributes/common/ResourcesApp.java +++ /dev/null @@ -1,31 +0,0 @@ -package co.elastic.apm.android.test.attributes.common; - -import android.os.Build; - -import org.robolectric.util.ReflectionHelpers; - -import co.elastic.apm.android.sdk.ElasticApmConfiguration; -import co.elastic.apm.android.test.testutils.MainApp; -import co.elastic.apm.android.test.testutils.base.BaseRobolectricTestApplication; -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.sdk.resources.Resource; - -public class ResourcesApp extends BaseRobolectricTestApplication { - - public static final String RUNTIME_VERSION = "0.0.0"; - public static final String DEVICE_MODEL_NAME = "Universe E10"; - public static final String DEVICE_MANUFACTURER = "Droidlastic"; - public static final String RESOURCE_KEY = "global.key"; - public static final String RESOURCE_VALUE = "global.value"; - - @Override - public void onCreate() { - super.onCreate(); - ReflectionHelpers.setStaticField(Build.class, "MODEL", DEVICE_MODEL_NAME); - ReflectionHelpers.setStaticField(Build.class, "MANUFACTURER", DEVICE_MANUFACTURER); - System.setProperty("java.vm.version", RUNTIME_VERSION); - initializeAgentWithCustomConfig(ElasticApmConfiguration.builder() - .setResource(Resource.create(Attributes.builder().put(RESOURCE_KEY, RESOURCE_VALUE).build())) - .build()); - } -} diff --git a/android-test/app/src/test/java/co/elastic/apm/android/test/attributes/logs/GlobalAttributeTest.java b/android-test/app/src/test/java/co/elastic/apm/android/test/attributes/logs/GlobalAttributeTest.java deleted file mode 100644 index abf8a13be..000000000 --- a/android-test/app/src/test/java/co/elastic/apm/android/test/attributes/logs/GlobalAttributeTest.java +++ /dev/null @@ -1,56 +0,0 @@ -package co.elastic.apm.android.test.attributes.logs; - -import org.junit.Test; -import org.robolectric.annotation.Config; - -import co.elastic.apm.android.test.attributes.traces.common.AppsWithConnectivity; -import co.elastic.apm.android.test.common.logs.Logs; -import co.elastic.apm.android.test.testutils.base.BaseRobolectricTest; -import io.opentelemetry.sdk.logs.data.LogRecordData; - -public class GlobalAttributeTest extends BaseRobolectricTest { - - @Test - public void whenALogIsCreated_verifyItHasSessionIdAsParam() { - LogRecordData log = captureLog(); - - Logs.verifyRecord(log) - .hasAttribute("session.id"); - } - - @Config(application = AppsWithConnectivity.WithWifi.class) - @Test - public void whenALogIsCreated_andThereIsWifiConnectivity_verifyItHasWifiConnectivityParam() { - LogRecordData log = captureLog(); - - Logs.verifyRecord(log) - .hasAttribute("network.connection.type", "wifi"); - } - - @Config(application = AppsWithConnectivity.WithCellular.class) - @Test - public void whenALogIsCreated_andThereIsCellularConnectivity_verifyItHasCellularConnectivityParam() { - LogRecordData log = captureLog(); - - Logs.verifyRecord(log) - .hasAttribute("network.connection.type", "cell"); - } - - @Config(application = AppsWithConnectivity.WithCellularAndSubtype.class) - @Test - public void whenALogIsCreated_andThereIsCellularConnectivityWithSubtype_verifyItHasCellularConnectivityParam() { - LogRecordData log = captureLog(); - - Logs.verifyRecord(log) - .hasAttribute("network.connection.type", "cell") - .hasAttribute("network.connection.subtype", "EDGE"); - } - - private LogRecordData captureLog() { - LogAttrHost host = new LogAttrHost(); - - host.methodWithLog(); - - return getRecordedLog(); - } -} diff --git a/android-test/app/src/test/java/co/elastic/apm/android/test/attributes/logs/ResourcesTest.java b/android-test/app/src/test/java/co/elastic/apm/android/test/attributes/logs/ResourcesTest.java deleted file mode 100644 index 703511dc1..000000000 --- a/android-test/app/src/test/java/co/elastic/apm/android/test/attributes/logs/ResourcesTest.java +++ /dev/null @@ -1,148 +0,0 @@ -package co.elastic.apm.android.test.attributes.logs; - -import static co.elastic.apm.android.test.attributes.common.ResourcesApp.DEVICE_MANUFACTURER; -import static co.elastic.apm.android.test.attributes.common.ResourcesApp.DEVICE_MODEL_NAME; -import static co.elastic.apm.android.test.attributes.common.ResourcesApp.RESOURCE_KEY; -import static co.elastic.apm.android.test.attributes.common.ResourcesApp.RESOURCE_VALUE; -import static co.elastic.apm.android.test.attributes.common.ResourcesApp.RUNTIME_VERSION; - -import org.junit.Test; -import org.robolectric.annotation.Config; - -import co.elastic.apm.android.test.BuildConfig; -import co.elastic.apm.android.test.attributes.common.ResourcesApp; -import co.elastic.apm.android.test.common.logs.Logs; -import co.elastic.apm.android.test.testutils.base.BaseRobolectricTest; -import io.opentelemetry.sdk.logs.data.LogRecordData; - -@Config(application = ResourcesApp.class) -public class ResourcesTest extends BaseRobolectricTest { - - @Test - public void whenALogIsCreated_serviceNameIsSet() { - LogRecordData log = captureLog(); - - Logs.verifyRecord(log) - .hasResource("service.name", "my-app"); - } - - @Test - public void whenALogIsCreated_itHasProvidedResources() { - LogRecordData log = captureLog(); - - Logs.verifyRecord(log) - .hasResource(RESOURCE_KEY, RESOURCE_VALUE); - } - - @Test - public void whenALogIsCreated_serviceVersionIsSet() { - LogRecordData log = captureLog(); - - Logs.verifyRecord(log) - .hasResource("service.version", "1.0"); - } - - @Test - public void whenALogIsCreated_serviceBuildIsSet() { - LogRecordData log = captureLog(); - - Logs.verifyRecord(log) - .hasResource("service.build", 5); - } - - @Test - public void whenALogIsCreated_agentNameIsSet() { - LogRecordData log = captureLog(); - - Logs.verifyRecord(log) - .hasResource("telemetry.sdk.name", "android"); - } - - @Test - public void whenALogIsCreated_agentVersionIsSet() { - LogRecordData log = captureLog(); - - Logs.verifyRecord(log) - .hasResource("telemetry.sdk.version", System.getProperty("agentVersion")); - } - - @Test - public void whenALogIsCreated_osDescriptionIsSet() { - LogRecordData log = captureLog(); - - Logs.verifyRecord(log) - .hasResource("os.description", "Android 14, API level 34, BUILD unknown"); - } - - @Test - public void whenALogIsCreated_osNameIsSet() { - LogRecordData log = captureLog(); - - Logs.verifyRecord(log) - .hasResource("os.name", "Android"); - } - - @Test - public void whenALogIsCreated_osVersionSet() { - LogRecordData log = captureLog(); - - Logs.verifyRecord(log) - .hasResource("os.version", "14"); - } - - @Test - public void whenALogIsCreated_deploymentEnvironmentIsSet() { - LogRecordData log = captureLog(); - - Logs.verifyRecord(log) - .hasResource("deployment.environment", (BuildConfig.DEBUG) ? "debug" : "release"); - } - - @Test - public void whenALogIsCreated_deviceIdIsSet() { - LogRecordData log = captureLog(); - - Logs.verifyRecord(log) - .hasResource("device.id"); - } - - @Test - public void whenALogIsCreated_deviceModelIdIsSet() { - LogRecordData log = captureLog(); - - Logs.verifyRecord(log) - .hasResource("device.model.identifier", DEVICE_MODEL_NAME); - } - - @Test - public void whenALogIsCreated_deviceModelManufacturerIsSet() { - LogRecordData log = captureLog(); - - Logs.verifyRecord(log) - .hasResource("device.manufacturer", DEVICE_MANUFACTURER); - } - - @Test - public void whenALogIsCreated_processRuntimeNameIsSet() { - LogRecordData log = captureLog(); - - Logs.verifyRecord(log) - .hasResource("process.runtime.name", "Android Runtime"); - } - - @Test - public void whenALogIsCreated_processRuntimeVersionIsSet() { - LogRecordData log = captureLog(); - - Logs.verifyRecord(log) - .hasResource("process.runtime.version", RUNTIME_VERSION); - } - - private LogRecordData captureLog() { - LogAttrHost host = new LogAttrHost(); - - host.methodWithLog(); - - return getRecordedLog(); - } -} diff --git a/android-test/app/src/test/java/co/elastic/apm/android/test/attributes/metrics/ResourcesTest.java b/android-test/app/src/test/java/co/elastic/apm/android/test/attributes/metrics/ResourcesTest.java deleted file mode 100644 index ea6493716..000000000 --- a/android-test/app/src/test/java/co/elastic/apm/android/test/attributes/metrics/ResourcesTest.java +++ /dev/null @@ -1,149 +0,0 @@ -package co.elastic.apm.android.test.attributes.metrics; - -import static co.elastic.apm.android.test.attributes.common.ResourcesApp.DEVICE_MANUFACTURER; -import static co.elastic.apm.android.test.attributes.common.ResourcesApp.DEVICE_MODEL_NAME; -import static co.elastic.apm.android.test.attributes.common.ResourcesApp.RESOURCE_KEY; -import static co.elastic.apm.android.test.attributes.common.ResourcesApp.RESOURCE_VALUE; -import static co.elastic.apm.android.test.attributes.common.ResourcesApp.RUNTIME_VERSION; - -import org.junit.Test; -import org.robolectric.annotation.Config; - -import co.elastic.apm.android.test.BuildConfig; -import co.elastic.apm.android.test.attributes.common.ResourcesApp; -import co.elastic.apm.android.test.common.metrics.Metrics; -import co.elastic.apm.android.test.testutils.base.BaseRobolectricTest; -import io.opentelemetry.sdk.metrics.data.MetricData; - -@Config(application = ResourcesApp.class) -public class ResourcesTest extends BaseRobolectricTest { - - @Test - public void whenAMetricIsCreated_serviceNameIsSet() { - MetricData metric = captureMetric(); - - Metrics.verify(metric) - .hasResource("service.name", "my-app"); - } - - @Test - public void whenAMetricIsCreated_itHasProvidedResources() { - MetricData metric = captureMetric(); - - Metrics.verify(metric) - .hasResource(RESOURCE_KEY, RESOURCE_VALUE); - } - - @Test - public void whenAMetricIsCreated_serviceVersionIsSet() { - MetricData metric = captureMetric(); - - Metrics.verify(metric) - .hasResource("service.version", "1.0"); - } - - @Test - public void whenAMetricIsCreated_serviceBuildIsSet() { - MetricData metric = captureMetric(); - - Metrics.verify(metric) - .hasResource("service.build", 5); - } - - @Test - public void whenAMetricIsCreated_agentNameIsSet() { - MetricData metric = captureMetric(); - - Metrics.verify(metric) - .hasResource("telemetry.sdk.name", "android"); - } - - @Test - public void whenAMetricIsCreated_agentVersionIsSet() { - MetricData metric = captureMetric(); - - Metrics.verify(metric) - .hasResource("telemetry.sdk.version", System.getProperty("agentVersion")); - } - - @Test - public void whenAMetricIsCreated_osDescriptionIsSet() { - MetricData metric = captureMetric(); - - Metrics.verify(metric) - .hasResource("os.description", "Android 14, API level 34, BUILD unknown"); - } - - @Test - public void whenAMetricIsCreated_osNameIsSet() { - MetricData metric = captureMetric(); - - Metrics.verify(metric) - .hasResource("os.name", "Android"); - } - - @Test - public void whenAMetricIsCreated_osVersionSet() { - MetricData metric = captureMetric(); - - Metrics.verify(metric) - .hasResource("os.version", "14"); - } - - @Test - public void whenAMetricIsCreated_deploymentEnvironmentIsSet() { - MetricData metric = captureMetric(); - - Metrics.verify(metric) - .hasResource("deployment.environment", (BuildConfig.DEBUG) ? "debug" : "release"); - } - - @Test - public void whenAMetricIsCreated_deviceIdIsSet() { - MetricData metric = captureMetric(); - - Metrics.verify(metric) - .hasResource("device.id"); - } - - @Test - public void whenAMetricIsCreated_deviceModelIdIsSet() { - MetricData metric = captureMetric(); - - Metrics.verify(metric) - .hasResource("device.model.identifier", DEVICE_MODEL_NAME); - } - - @Test - public void whenAMetricIsCreated_deviceModelManufacturerIsSet() { - MetricData metric = captureMetric(); - - Metrics.verify(metric) - .hasResource("device.manufacturer", DEVICE_MANUFACTURER); - } - - @Test - public void whenAMetricIsCreated_processRuntimeNameIsSet() { - MetricData metric = captureMetric(); - - Metrics.verify(metric) - .hasResource("process.runtime.name", "Android Runtime"); - } - - @Test - public void whenAMetricIsCreated_processRuntimeVersionIsSet() { - MetricData metric = captureMetric(); - - Metrics.verify(metric) - .hasResource("process.runtime.version", RUNTIME_VERSION); - } - - private MetricData captureMetric() { - MetricAttrHost host = new MetricAttrHost(); - - host.methodWithCounter(); - flushMetrics(); - - return getRecordedMetric(); - } -} diff --git a/android-test/app/src/test/java/co/elastic/apm/android/test/attributes/traces/GlobalAttributeTest.java b/android-test/app/src/test/java/co/elastic/apm/android/test/attributes/traces/GlobalAttributeTest.java deleted file mode 100644 index b651b4179..000000000 --- a/android-test/app/src/test/java/co/elastic/apm/android/test/attributes/traces/GlobalAttributeTest.java +++ /dev/null @@ -1,101 +0,0 @@ -package co.elastic.apm.android.test.attributes.traces; - -import android.content.Context; -import android.telephony.TelephonyManager; - -import org.junit.Test; -import org.robolectric.Shadows; -import org.robolectric.annotation.Config; -import org.robolectric.shadows.ShadowTelephonyManager; - -import java.util.List; - -import co.elastic.apm.android.test.attributes.traces.common.AppsWithConnectivity; -import co.elastic.apm.android.test.common.spans.Spans; -import co.elastic.apm.android.test.testutils.MainApp; -import co.elastic.apm.android.test.testutils.base.BaseRobolectricTest; -import io.opentelemetry.sdk.trace.data.SpanData; - -public class GlobalAttributeTest extends BaseRobolectricTest { - - @Test - public void whenASpanIsCreated_verifyItHasSessionIdAsParam() { - SpanData customSpan = getSpanData(); - - Spans.verify(customSpan) - .hasAttribute("session.id"); - } - - @Test - public void whenASpanIsCreated_verifyItHasTypeMobileAsParam() { - SpanData customSpan = getSpanData(); - - Spans.verify(customSpan) - .hasAttribute("type", "mobile"); - } - - @Config(application = AppWithCarrierInfo.class) - @Test - public void whenASpanIsCreated_verifyItHasCarrierInfoParams() { - SpanData customSpan = getSpanData(); - - Spans.verify(customSpan) - .hasAttribute("network.carrier.name", AppWithCarrierInfo.SIM_OPERATOR_NAME) - .hasAttribute("network.carrier.mcc", "123") - .hasAttribute("network.carrier.mnc", "456") - .hasAttribute("network.carrier.icc", AppWithCarrierInfo.SIM_COUNTRY_ISO); - } - - @Config(application = AppsWithConnectivity.WithWifi.class) - @Test - public void whenASpanIsCreated_andThereIsAWifiConnection_verifyItHasWifiConnectivityParams() { - SpanData span = getSpanData(); - - Spans.verify(span) - .hasAttribute("network.connection.type", "wifi"); - } - - @Config(application = AppsWithConnectivity.WithCellular.class) - @Test - public void whenASpanIsCreated_andThereIsAMobileConnection_verifyItHasMobileConnectivityParams() { - SpanData span = getSpanData(); - - Spans.verify(span) - .hasAttribute("network.connection.type", "cell"); - } - - @Config(application = AppsWithConnectivity.WithCellularAndSubtype.class) - @Test - public void whenASpanIsCreated_andThereIsAMobileConnectionWithSubtype_verifyItHasMobileConnectivityParams() { - SpanData span = getSpanData(); - - Spans.verify(span) - .hasAttribute("network.connection.type", "cell") - .hasAttribute("network.connection.subtype", "EDGE"); - } - - private SpanData getSpanData() { - SpanAttrHost host = new SpanAttrHost(); - - host.methodWithSpan(); - - List spans = getRecordedSpans(1); - return spans.get(0); - } - - private static class AppWithCarrierInfo extends MainApp { - private static final String SIM_OPERATOR = "123456"; - private static final String SIM_OPERATOR_NAME = "elasticphone"; - private static final String SIM_COUNTRY_ISO = "us"; - - @Override - public void onCreate() { - ShadowTelephonyManager shadowTelephonyManager = Shadows.shadowOf((TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE)); - shadowTelephonyManager.setSimOperator(SIM_OPERATOR); - shadowTelephonyManager.setSimState(TelephonyManager.SIM_STATE_READY); - shadowTelephonyManager.setSimOperatorName(SIM_OPERATOR_NAME); - shadowTelephonyManager.setSimCountryIso(SIM_COUNTRY_ISO); - super.onCreate(); - } - } -} diff --git a/android-test/app/src/test/java/co/elastic/apm/android/test/attributes/traces/ResourcesTest.java b/android-test/app/src/test/java/co/elastic/apm/android/test/attributes/traces/ResourcesTest.java deleted file mode 100644 index cca65cab8..000000000 --- a/android-test/app/src/test/java/co/elastic/apm/android/test/attributes/traces/ResourcesTest.java +++ /dev/null @@ -1,168 +0,0 @@ -package co.elastic.apm.android.test.attributes.traces; - -import static co.elastic.apm.android.test.attributes.common.ResourcesApp.DEVICE_MANUFACTURER; -import static co.elastic.apm.android.test.attributes.common.ResourcesApp.DEVICE_MODEL_NAME; -import static co.elastic.apm.android.test.attributes.common.ResourcesApp.RESOURCE_KEY; -import static co.elastic.apm.android.test.attributes.common.ResourcesApp.RESOURCE_VALUE; -import static co.elastic.apm.android.test.attributes.common.ResourcesApp.RUNTIME_VERSION; - -import org.junit.Test; -import org.robolectric.annotation.Config; - -import co.elastic.apm.android.sdk.ElasticApmConfiguration; -import co.elastic.apm.android.test.BuildConfig; -import co.elastic.apm.android.test.attributes.common.ResourcesApp; -import co.elastic.apm.android.test.common.spans.Spans; -import co.elastic.apm.android.test.testutils.base.BaseRobolectricTest; -import co.elastic.apm.android.test.testutils.base.BaseRobolectricTestApplication; -import io.opentelemetry.sdk.trace.data.SpanData; - -@Config(application = ResourcesApp.class) -public class ResourcesTest extends BaseRobolectricTest { - - @Test - public void whenASpanIsCreated_serviceNameIsSet() { - SpanData customSpan = captureSpan(); - - Spans.verify(customSpan) - .hasResource("service.name", "my-app"); - } - - @Test - public void whenASpanIsCreated_itHasProvidedResources() { - SpanData customSpan = captureSpan(); - - Spans.verify(customSpan) - .hasResource(RESOURCE_KEY, RESOURCE_VALUE); - } - - @Test - public void whenASpanIsCreated_serviceVersionIsSet() { - SpanData customSpan = captureSpan(); - - Spans.verify(customSpan) - .hasResource("service.version", "1.0"); - } - - @Test - public void whenASpanIsCreated_serviceBuildIsSet() { - SpanData customSpan = captureSpan(); - - Spans.verify(customSpan) - .hasResource("service.build", 5); - } - - @Test - public void whenASpanIsCreated_agentNameIsSet() { - SpanData customSpan = captureSpan(); - - Spans.verify(customSpan) - .hasResource("telemetry.sdk.name", "android"); - } - - @Test - public void whenASpanIsCreated_agentVersionIsSet() { - SpanData customSpan = captureSpan(); - - Spans.verify(customSpan) - .hasResource("telemetry.sdk.version", System.getProperty("agentVersion")); - } - - @Test - public void whenASpanIsCreated_osDescriptionIsSet() { - SpanData customSpan = captureSpan(); - - Spans.verify(customSpan) - .hasResource("os.description", "Android 14, API level 34, BUILD unknown"); - } - - @Test - public void whenASpanIsCreated_osNameIsSet() { - SpanData customSpan = captureSpan(); - - Spans.verify(customSpan) - .hasResource("os.name", "Android"); - } - - @Test - public void whenASpanIsCreated_osVersionSet() { - SpanData customSpan = captureSpan(); - - Spans.verify(customSpan) - .hasResource("os.version", "14"); - } - - @Test - public void whenASpanIsCreated_deploymentEnvironmentIsSet() { - SpanData customSpan = captureSpan(); - - Spans.verify(customSpan) - .hasResource("deployment.environment", (BuildConfig.DEBUG) ? "debug" : "release"); - } - - @Test - public void whenASpanIsCreated_deviceIdIsSet() { - SpanData customSpan = captureSpan(); - - Spans.verify(customSpan) - .hasResource("device.id"); - } - - @Test - public void whenASpanIsCreated_deviceModelIdIsSet() { - SpanData customSpan = captureSpan(); - - Spans.verify(customSpan) - .hasResource("device.model.identifier", DEVICE_MODEL_NAME); - } - - @Test - public void whenASpanIsCreated_deviceModelManufacturerIsSet() { - SpanData customSpan = captureSpan(); - - Spans.verify(customSpan) - .hasResource("device.manufacturer", DEVICE_MANUFACTURER); - } - - @Test - public void whenASpanIsCreated_processRuntimeNameIsSet() { - SpanData customSpan = captureSpan(); - - Spans.verify(customSpan) - .hasResource("process.runtime.name", "Android Runtime"); - } - - @Test - public void whenASpanIsCreated_processRuntimeVersionIsSet() { - SpanData customSpan = captureSpan(); - - Spans.verify(customSpan) - .hasResource("process.runtime.version", RUNTIME_VERSION); - } - - @Config(application = CustomDeploymentEnvApp.class) - @Test - public void withProvidedDeploymentEnv_whenASpanIsCreated_providedDeploymentEnvironmentIsSet() { - SpanData customSpan = captureSpan(); - - Spans.verify(customSpan) - .hasResource("deployment.environment", "some-deployment-env"); - } - - private static class CustomDeploymentEnvApp extends BaseRobolectricTestApplication { - @Override - public void onCreate() { - initializeAgentWithCustomConfig(ElasticApmConfiguration.builder() - .setDeploymentEnvironment("some-deployment-env") - .build()); - } - } - - private SpanData captureSpan() { - SpanAttrHost host = new SpanAttrHost(); - - host.methodWithSpan(); - - return getRecordedSpan(); - } -} \ No newline at end of file diff --git a/android-test/app/src/test/java/co/elastic/apm/android/test/attributes/traces/common/AppsWithConnectivity.java b/android-test/app/src/test/java/co/elastic/apm/android/test/attributes/traces/common/AppsWithConnectivity.java deleted file mode 100644 index bfbfaeaeb..000000000 --- a/android-test/app/src/test/java/co/elastic/apm/android/test/attributes/traces/common/AppsWithConnectivity.java +++ /dev/null @@ -1,71 +0,0 @@ -package co.elastic.apm.android.test.attributes.traces.common; - -import static org.mockito.Mockito.doReturn; - -import android.Manifest; -import android.content.Context; -import android.net.ConnectivityManager; -import android.net.Network; -import android.net.NetworkCapabilities; -import android.telephony.TelephonyManager; - -import org.mockito.Mockito; -import org.robolectric.Shadows; -import org.robolectric.shadows.ShadowApplication; -import org.robolectric.shadows.ShadowConnectivityManager; -import org.robolectric.shadows.ShadowTelephonyManager; - -import java.util.ArrayList; -import java.util.List; - -import co.elastic.apm.android.test.testutils.MainApp; - -public final class AppsWithConnectivity { - - public static class WithWifi extends MainApp { - @Override - public void onCreate() { - super.onCreate(); - ShadowConnectivityManager shadowConnectivityManager = Shadows.shadowOf((ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE)); - List callbacks = new ArrayList<>(shadowConnectivityManager.getNetworkCallbacks()); - ConnectivityManager.NetworkCallback defaultNetworkCallback = callbacks.get(0); - - NetworkCapabilities capabilities = Mockito.mock(NetworkCapabilities.class); - doReturn(true).when(capabilities).hasTransport(NetworkCapabilities.TRANSPORT_WIFI); - defaultNetworkCallback.onCapabilitiesChanged(Mockito.mock(Network.class), capabilities); - } - } - - public static class WithCellular extends MainApp { - @Override - public void onCreate() { - super.onCreate(); - ShadowConnectivityManager shadowConnectivityManager = Shadows.shadowOf((ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE)); - List callbacks = new ArrayList<>(shadowConnectivityManager.getNetworkCallbacks()); - ConnectivityManager.NetworkCallback defaultNetworkCallback = callbacks.get(0); - - NetworkCapabilities capabilities = Mockito.mock(NetworkCapabilities.class); - doReturn(true).when(capabilities).hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR); - defaultNetworkCallback.onCapabilitiesChanged(Mockito.mock(Network.class), capabilities); - } - } - - public static class WithCellularAndSubtype extends MainApp { - @Override - public void onCreate() { - super.onCreate(); - ShadowConnectivityManager shadowConnectivityManager = Shadows.shadowOf((ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE)); - ShadowTelephonyManager shadowTelephonyManager = Shadows.shadowOf((TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE)); - ShadowApplication shadowContext = Shadows.shadowOf(this); - shadowContext.grantPermissions(Manifest.permission.READ_PHONE_STATE); - List callbacks = new ArrayList<>(shadowConnectivityManager.getNetworkCallbacks()); - ConnectivityManager.NetworkCallback defaultNetworkCallback = callbacks.get(0); - - NetworkCapabilities capabilities = Mockito.mock(NetworkCapabilities.class); - doReturn(true).when(capabilities).hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR); - shadowTelephonyManager.setDataNetworkType(TelephonyManager.NETWORK_TYPE_EDGE); - - defaultNetworkCallback.onCapabilitiesChanged(Mockito.mock(Network.class), capabilities); - } - } -} diff --git a/android-test/app/src/test/java/co/elastic/apm/android/test/opentelemetry/common/AppUseCases.java b/android-test/app/src/test/java/co/elastic/apm/android/test/centralconfigs/common/AppUseCases.java similarity index 90% rename from android-test/app/src/test/java/co/elastic/apm/android/test/opentelemetry/common/AppUseCases.java rename to android-test/app/src/test/java/co/elastic/apm/android/test/centralconfigs/common/AppUseCases.java index 8e581dcc7..3d77b51c9 100644 --- a/android-test/app/src/test/java/co/elastic/apm/android/test/opentelemetry/common/AppUseCases.java +++ b/android-test/app/src/test/java/co/elastic/apm/android/test/centralconfigs/common/AppUseCases.java @@ -1,4 +1,4 @@ -package co.elastic.apm.android.test.opentelemetry.common; +package co.elastic.apm.android.test.centralconfigs.common; import co.elastic.apm.android.sdk.ElasticApmConfiguration; import co.elastic.apm.android.test.testutils.base.BaseRobolectricTestApplication; diff --git a/android-test/app/src/test/java/co/elastic/apm/android/test/opentelemetry/logs/OpenTelemetryLogRecordsTest.java b/android-test/app/src/test/java/co/elastic/apm/android/test/centralconfigs/logs/OpenTelemetryLogRecordsTest.java similarity index 91% rename from android-test/app/src/test/java/co/elastic/apm/android/test/opentelemetry/logs/OpenTelemetryLogRecordsTest.java rename to android-test/app/src/test/java/co/elastic/apm/android/test/centralconfigs/logs/OpenTelemetryLogRecordsTest.java index 112c7556f..26ff9860b 100644 --- a/android-test/app/src/test/java/co/elastic/apm/android/test/opentelemetry/logs/OpenTelemetryLogRecordsTest.java +++ b/android-test/app/src/test/java/co/elastic/apm/android/test/centralconfigs/logs/OpenTelemetryLogRecordsTest.java @@ -1,4 +1,4 @@ -package co.elastic.apm.android.test.opentelemetry.logs; +package co.elastic.apm.android.test.centralconfigs.logs; import static org.mockito.Mockito.doReturn; @@ -8,7 +8,7 @@ import co.elastic.apm.android.sdk.internal.configuration.Configurations; import co.elastic.apm.android.sdk.internal.configuration.impl.AllInstrumentationConfiguration; import co.elastic.apm.android.sdk.logs.ElasticLoggers; -import co.elastic.apm.android.test.opentelemetry.common.AppUseCases; +import co.elastic.apm.android.test.centralconfigs.common.AppUseCases; import co.elastic.apm.android.test.testutils.base.BaseRobolectricTest; import io.opentelemetry.api.logs.Logger; diff --git a/android-test/app/src/test/java/co/elastic/apm/android/test/opentelemetry/metrics/OpenTelemetryMetricsTest.java b/android-test/app/src/test/java/co/elastic/apm/android/test/centralconfigs/metrics/OpenTelemetryMetricsTest.java similarity index 91% rename from android-test/app/src/test/java/co/elastic/apm/android/test/opentelemetry/metrics/OpenTelemetryMetricsTest.java rename to android-test/app/src/test/java/co/elastic/apm/android/test/centralconfigs/metrics/OpenTelemetryMetricsTest.java index e777161b6..a2491f03e 100644 --- a/android-test/app/src/test/java/co/elastic/apm/android/test/opentelemetry/metrics/OpenTelemetryMetricsTest.java +++ b/android-test/app/src/test/java/co/elastic/apm/android/test/centralconfigs/metrics/OpenTelemetryMetricsTest.java @@ -1,4 +1,4 @@ -package co.elastic.apm.android.test.opentelemetry.metrics; +package co.elastic.apm.android.test.centralconfigs.metrics; import static org.mockito.Mockito.doReturn; @@ -8,7 +8,7 @@ import co.elastic.apm.android.sdk.internal.configuration.Configurations; import co.elastic.apm.android.sdk.internal.configuration.impl.AllInstrumentationConfiguration; import co.elastic.apm.android.sdk.metrics.ElasticMeters; -import co.elastic.apm.android.test.opentelemetry.common.AppUseCases; +import co.elastic.apm.android.test.centralconfigs.common.AppUseCases; import co.elastic.apm.android.test.testutils.base.BaseRobolectricTest; import io.opentelemetry.api.metrics.Meter; diff --git a/android-test/app/src/test/java/co/elastic/apm/android/test/opentelemetry/traces/OpenTelemetryTracesTest.java b/android-test/app/src/test/java/co/elastic/apm/android/test/centralconfigs/traces/OpenTelemetryTracesTest.java similarity index 84% rename from android-test/app/src/test/java/co/elastic/apm/android/test/opentelemetry/traces/OpenTelemetryTracesTest.java rename to android-test/app/src/test/java/co/elastic/apm/android/test/centralconfigs/traces/OpenTelemetryTracesTest.java index 3ac6255ca..1c3af462b 100644 --- a/android-test/app/src/test/java/co/elastic/apm/android/test/opentelemetry/traces/OpenTelemetryTracesTest.java +++ b/android-test/app/src/test/java/co/elastic/apm/android/test/centralconfigs/traces/OpenTelemetryTracesTest.java @@ -1,17 +1,15 @@ -package co.elastic.apm.android.test.opentelemetry.traces; +package co.elastic.apm.android.test.centralconfigs.traces; import static org.mockito.Mockito.doReturn; import org.junit.Test; import org.robolectric.annotation.Config; -import co.elastic.apm.android.sdk.ElasticApmConfiguration; import co.elastic.apm.android.sdk.internal.configuration.Configurations; import co.elastic.apm.android.sdk.internal.configuration.impl.AllInstrumentationConfiguration; import co.elastic.apm.android.sdk.traces.ElasticTracers; -import co.elastic.apm.android.test.opentelemetry.common.AppUseCases; +import co.elastic.apm.android.test.centralconfigs.common.AppUseCases; import co.elastic.apm.android.test.testutils.base.BaseRobolectricTest; -import co.elastic.apm.android.test.testutils.base.BaseRobolectricTestApplication; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.Tracer; diff --git a/android-test/app/src/test/java/co/elastic/apm/android/test/testutils/base/BaseRobolectricTestApplication.java b/android-test/app/src/test/java/co/elastic/apm/android/test/testutils/base/BaseRobolectricTestApplication.java index ef0b78578..543a4004e 100644 --- a/android-test/app/src/test/java/co/elastic/apm/android/test/testutils/base/BaseRobolectricTestApplication.java +++ b/android-test/app/src/test/java/co/elastic/apm/android/test/testutils/base/BaseRobolectricTestApplication.java @@ -203,7 +203,11 @@ public AgentDependenciesInjector intercept(AgentDependenciesInjector agentDepend public List provideConfigurations() { List spies = new ArrayList<>(); for (Configuration configuration : configurations) { - spies.add(spy(configuration)); + try { + spies.add(spy(configuration)); + } catch (IllegalArgumentException ignored) { + spies.add(configuration); + } } return spies; } diff --git a/android-test/build.gradle b/android-test/build.gradle index 9a565dd0d..082d8767c 100644 --- a/android-test/build.gradle +++ b/android-test/build.gradle @@ -6,16 +6,7 @@ propertiesFile.withInputStream { ext { jvmCompatibility = JavaVersion.VERSION_17 - junit_version = '4.13.2' - espresso_version = '3.5.1' - mockito_version = '4.9.0' - agp_version = '8.3.0' - mockwebserver_version = '4.10.0' androidCompileSdk = 34 androidMinSdk = 26 - agent_version = agentProperties["version"] -} - -task clean(type: Delete) { - delete rootProject.buildDir + agentVersion = agentProperties["version"] } \ No newline at end of file diff --git a/android-test/gradle/libs.versions.toml b/android-test/gradle/libs.versions.toml new file mode 100644 index 000000000..73e05d0e5 --- /dev/null +++ b/android-test/gradle/libs.versions.toml @@ -0,0 +1,15 @@ +[versions] +espresso = "3.5.1" + +[libraries] +openTelemetry-exporter = "io.opentelemetry:opentelemetry-exporter-otlp:1.28.0" +junit = "junit:junit:4.13.2" +espresso-idlingResource = { module = "androidx.test.espresso:espresso-idling-resource", version.ref = "espresso" } +espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "espresso" } +espresso-contrib = { module = "androidx.test.espresso:espresso-contrib", version.ref = "espresso" } +mockito = "org.mockito:mockito-core:5.14.2" +mockWebServer = "com.squareup.okhttp3:mockwebserver:4.12.0" +fragmentTesting = "androidx.fragment:fragment-testing:1.6.1" +appCompat = "androidx.appcompat:appcompat:1.5.1" +coreLib = "com.android.tools:desugar_jdk_libs:2.0.4" +robolectric = "org.robolectric:robolectric:4.12.1" \ No newline at end of file diff --git a/android-test/plugin-test/build.gradle b/android-test/plugin-test/build.gradle index 5e68b1c32..3399d3606 100644 --- a/android-test/plugin-test/build.gradle +++ b/android-test/plugin-test/build.gradle @@ -8,8 +8,8 @@ java { } dependencies { - implementation "co.elastic.apm:android-plugin:$agent_version" - testImplementation "co.elastic.apm:android-common:$agent_version" - testImplementation "junit:junit:$junit_version" + implementation "co.elastic.apm:android-plugin" + testImplementation "co.elastic.apm:android-common" + testImplementation libs.junit testImplementation "uk.org.webcompere:system-stubs-junit4:2.1.6" } \ No newline at end of file diff --git a/android-test/settings.gradle b/android-test/settings.gradle index 3bdc2d0a2..b839bc3bc 100644 --- a/android-test/settings.gradle +++ b/android-test/settings.gradle @@ -1,19 +1,23 @@ pluginManagement { repositories { - mavenLocal() gradlePluginPortal() mavenCentral() google() } } dependencyResolutionManagement { + versionCatalogs { + create("rootLibs") { + from(files("../gradle/libs.versions.toml")) + } + } repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { - mavenLocal() mavenCentral() google() } } +includeBuild '..' include ':app' include ':android-test-common' include ':plugin-test' \ No newline at end of file diff --git a/build-tools/build.gradle b/build-tools/build.gradle index 2d72953dd..1e1f91771 100644 --- a/build-tools/build.gradle +++ b/build-tools/build.gradle @@ -17,7 +17,7 @@ dependencies { implementation libs.gradle.publish.plugin implementation libs.gradle.shadow.plugin implementation "com.android.tools.build:gradle:${properties["androidGradlePlugin_version"]}" - testImplementation libs.junit + testImplementation libs.junit4 } gradlePlugin { diff --git a/build.gradle b/build.gradle index c18329891..648ab7b2a 100644 --- a/build.gradle +++ b/build.gradle @@ -7,5 +7,5 @@ plugins { ext { jvmCompatibility = JavaVersion.VERSION_11 androidMinSdk = 24 - androidCompileSdk = 32 + androidCompileSdk = 34 } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9a40898e0..9014c6d33 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,11 +4,13 @@ opentelemetry = "1.40.0" opentelemetry-alpha = "1.40.0-alpha" opentelemetry-semconv = "1.26.0-alpha" opentelemetry-contrib = "1.37.0-alpha" -mockito = "4.11.0" +mockito = "5.14.2" byteBuddy = "1.14.18" okhttp = "4.12.0" kotlin = "1.9.23" google-autoService = "1.1.1" +android = "8.2.0" +junit5 = "5.11.3" [libraries] opentelemetry-sdk = { module = "io.opentelemetry:opentelemetry-sdk", version.ref = "opentelemetry" } @@ -35,11 +37,16 @@ opentelemetry-exporter-otlp = { module = "io.opentelemetry:opentelemetry-exporte kotlin-coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4" #Test tools -mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockito" } -mockito-inline = { module = "org.mockito:mockito-inline", version.ref = "mockito" } -junit = "junit:junit:4.13.2" +mockito = { module = "org.mockito:mockito-core", version.ref = "mockito" } +junit4 = "junit:junit:4.13.2" +junit5 = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit5" } +junit5-vintage = { module = "org.junit.vintage:junit-vintage-engine", version.ref = "junit5" } mockwebserver = "com.squareup.okhttp3:mockwebserver:4.12.0" opentelemetry-testing = { module = "io.opentelemetry:opentelemetry-sdk-testing", version.ref = "opentelemetry" } +robolectric = "org.robolectric:robolectric:4.13" +assertj = "org.assertj:assertj-core:3.26.3" +coreLib = "com.android.tools:desugar_jdk_libs:2.0.4" +mockk = "io.mockk:mockk:1.13.13" #Compilation tools apache-commons-text = "org.apache.commons:commons-text:1.10.0" @@ -55,8 +62,11 @@ google-autoService-annotations = { module = "com.google.auto.service:auto-servic byteBuddy-plugin = { module = "net.bytebuddy:byte-buddy-gradle-plugin", version.ref = "byteBuddy" } [bundles] -mocking = ["mockito-core", "mockito-inline"] +mocking = ["mockito", "mockk"] +junit = ["junit4", "junit5"] [plugins] +androidApp = { id = "com.android.application", version.ref = "android" } +androidLib = { id = "com.android.library", version.ref = "android" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } \ No newline at end of file