From b888af2772a7fdcc3ab001d9e16c8e665186b6e3 Mon Sep 17 00:00:00 2001 From: Timur Valeev Date: Mon, 16 Dec 2024 20:19:56 +0000 Subject: [PATCH 1/9] RUM-6286: replacing `joinToString` when it possible, refactor a bit --- dd-sdk-android-core/api/apiSurface | 2 + .../api/dd-sdk-android-core.api | 5 ++ .../datadog/android/core/StringBuilderExt.kt | 27 ++++++++ .../internal/data/upload/CurlInterceptor.kt | 18 ++---- .../error/internal/DatadogExceptionHandler.kt | 5 +- .../android/core/StringBuilderExtKtTest.kt | 61 ++++++++++++++++++ .../datadog/android/utils/PerformanceTest.kt | 64 +++++++++++++++++++ .../rum/internal/DatadogLateCrashReporter.kt | 2 + .../android/rum/internal/LateCrashReporter.kt | 2 + .../rum/internal/anr/AndroidTraceParser.kt | 7 +- .../rum/internal/net/RumRequestFactory.kt | 20 +++--- 11 files changed, 184 insertions(+), 29 deletions(-) create mode 100644 dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/StringBuilderExt.kt create mode 100644 dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/StringBuilderExtKtTest.kt create mode 100644 dd-sdk-android-core/src/test/kotlin/com/datadog/android/utils/PerformanceTest.kt diff --git a/dd-sdk-android-core/api/apiSurface b/dd-sdk-android-core/api/apiSurface index d5b414c3a4..8d71195036 100644 --- a/dd-sdk-android-core/api/apiSurface +++ b/dd-sdk-android-core/api/apiSurface @@ -190,6 +190,8 @@ class com.datadog.android.core.SdkReference fun get(): com.datadog.android.api.SdkCore? fun allowThreadDiskReads(() -> T): T fun allowThreadDiskWrites(() -> T): T +fun StringBuilder.appendIfNotEmpty(String) +fun StringBuilder.appendIfNotEmpty(Char) enum com.datadog.android.core.configuration.BackPressureMitigation - DROP_OLDEST - IGNORE_NEWEST diff --git a/dd-sdk-android-core/api/dd-sdk-android-core.api b/dd-sdk-android-core/api/dd-sdk-android-core.api index 0e0ac78ac3..c6a9155c45 100644 --- a/dd-sdk-android-core/api/dd-sdk-android-core.api +++ b/dd-sdk-android-core/api/dd-sdk-android-core.api @@ -543,6 +543,11 @@ public final class com/datadog/android/core/StrictModeExtKt { public static final fun allowThreadDiskWrites (Lkotlin/jvm/functions/Function0;)Ljava/lang/Object; } +public final class com/datadog/android/core/StringBuilderExtKt { + public static final fun appendIfNotEmpty (Ljava/lang/StringBuilder;C)Ljava/lang/StringBuilder; + public static final fun appendIfNotEmpty (Ljava/lang/StringBuilder;Ljava/lang/String;)Ljava/lang/StringBuilder; +} + public final class com/datadog/android/core/configuration/BackPressureMitigation : java/lang/Enum { public static final field DROP_OLDEST Lcom/datadog/android/core/configuration/BackPressureMitigation; public static final field IGNORE_NEWEST Lcom/datadog/android/core/configuration/BackPressureMitigation; diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/StringBuilderExt.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/StringBuilderExt.kt new file mode 100644 index 0000000000..64e4d76f5e --- /dev/null +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/StringBuilderExt.kt @@ -0,0 +1,27 @@ +/* + * 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.core + +/** + * This utility function helps replace joinToString calls with more efficient StringBuilder's methods calls. + * In case when content should be separated with some separator it's handy to add it in front of new string only + * if buffer already contain some data + * @param str - string that should be added into buffer only if it already contains some data + */ +fun StringBuilder.appendIfNotEmpty(str: String) = apply { + if (isNotEmpty()) append(str) +} + +/** + * This utility function helps replace joinToString calls with more efficient StringBuilder's methods calls. + * In case when content should be separated with some separator it's handy to add it in front of new string only + * if buffer already contain some data + * @param char - char that should be added into buffer only if it already contains some data + */ +fun StringBuilder.appendIfNotEmpty(char: Char) = apply { + if (isNotEmpty()) append(char) +} diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/data/upload/CurlInterceptor.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/data/upload/CurlInterceptor.kt index 09e7d0d9e1..91244a72db 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/data/upload/CurlInterceptor.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/data/upload/CurlInterceptor.kt @@ -67,25 +67,21 @@ internal class CurlInterceptor( printBody = printBody ) - fun toCommand(): String { - val parts = mutableListOf() - parts.add("curl") - parts.add(FORMAT_METHOD.format(Locale.US, method.uppercase(Locale.US))) - + fun toCommand(): String = buildString { + append("curl").append(' ') + append(FORMAT_METHOD.format(Locale.US, method.uppercase(Locale.US))).append(' ') headers.forEach { (key, values) -> values.forEach { value -> - parts.add(FORMAT_HEADER.format(Locale.US, key, value)) + append(FORMAT_HEADER.format(Locale.US, key, value)).append(' ') } } if (contentType != null && !headers.containsKey(CONTENT_TYPE)) { - parts.add(FORMAT_HEADER.format(Locale.US, CONTENT_TYPE, contentType)) + append(FORMAT_HEADER.format(Locale.US, CONTENT_TYPE, contentType)).append(' ') } - requestBody?.let { parts.addAll(it.toParts()) } - parts.add(FORMAT_URL.format(Locale.US, url)) - - return parts.joinToString(" ") + requestBody?.toParts()?.forEach { append(it).append(' ') } + append(FORMAT_URL.format(Locale.US, url)) } private fun RequestBody.toParts(): List { diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/error/internal/DatadogExceptionHandler.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/error/internal/DatadogExceptionHandler.kt index 6996c6c87f..89d28dda4c 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/error/internal/DatadogExceptionHandler.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/error/internal/DatadogExceptionHandler.kt @@ -133,12 +133,11 @@ internal class DatadogExceptionHandler( ) + safeGetAllStacktraces() .filterKeys { it != crashedThread } .filterValues { it.isNotEmpty() } - .map { - val thread = it.key + .map { (thread, stackTrace) -> ThreadDump( name = thread.name, state = thread.state.asString(), - stack = it.value.loggableStackTrace(), + stack = stackTrace.loggableStackTrace(), crashed = false ) } diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/StringBuilderExtKtTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/StringBuilderExtKtTest.kt new file mode 100644 index 0000000000..181b4e1346 --- /dev/null +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/StringBuilderExtKtTest.kt @@ -0,0 +1,61 @@ +package com.datadog.android.core + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class StringBuilderExtKtTest { + + @Test + fun `M add char W addIfNotEmpty {buffer is not empty}`() { + // Given + val initialData = "something inside buffer already" + val buffer = StringBuilder(initialData) + + // When + buffer.appendIfNotEmpty(' ') + + // Then + + assertThat(buffer.toString()).isEqualTo("$initialData ") + } + + @Test + fun `M add str W addIfNotEmpty {buffer is not empty}`() { + // Given + val initialData = "something inside buffer already" + val buffer = StringBuilder(initialData) + + // When + buffer.appendIfNotEmpty(" ") + + // Then + + assertThat(buffer.toString()).isEqualTo("$initialData ") + } + + @Test + fun `M not add any char W addIfNotEmpty {buffer is empty}`() { + // Given + val buffer = StringBuilder() + + // When + buffer.appendIfNotEmpty(' ') + + // Then + + assertThat(buffer.toString()).isEqualTo("") + } + + @Test + fun `M not add any str W addIfNotEmpty {buffer is empty}`() { + // Given + val buffer = StringBuilder() + + // When + buffer.appendIfNotEmpty(" ") + + // Then + + assertThat(buffer.toString()).isEqualTo("") + } +} diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/utils/PerformanceTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/utils/PerformanceTest.kt new file mode 100644 index 0000000000..eaf84b4f70 --- /dev/null +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/utils/PerformanceTest.kt @@ -0,0 +1,64 @@ +/* + * 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.utils + +import com.datadog.android.utils.forge.Configurator +import fr.xgouchet.elmyr.Forge +import fr.xgouchet.elmyr.junit5.ForgeConfiguration +import fr.xgouchet.elmyr.junit5.ForgeExtension +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.api.extension.Extensions +import kotlin.system.measureNanoTime + +private const val ITEMS_TO_JOINT = 10_000 +private const val REPETITION_COUNT = 10_000 + +@Extensions( + ExtendWith(ForgeExtension::class) +) +@ForgeConfiguration(Configurator::class) +internal class PerformanceTest { + + private fun List.mean() = sum().toDouble() / size + + @Test + fun `M be faster than joinToString W buildString`(forge: Forge) { + val itemsForJoin = forge.aList(ITEMS_TO_JOINT) { forge.aString() } + val jointToStringResults = mutableListOf() + val builderResults = mutableListOf() + + var jointToStringResult = "" + var builderResult = "" + + for (i in 0..REPETITION_COUNT) { + jointToStringResults.add( + measureNanoTime { + val jointToStringContainer = mutableListOf() + for (item in itemsForJoin) { + jointToStringContainer.add(item) + } + jointToStringResult = jointToStringContainer.joinToString(separator = " ") { it } + } + ) + + builderResults.add( + measureNanoTime { + builderResult = buildString { + itemsForJoin.forEachIndexed { i, item -> + if (i > 0) append(" ") + append(item) + } + } + } + ) + } + + assertThat(builderResult).isEqualTo(jointToStringResult) // same result + assertThat(builderResults.mean()).isLessThan(jointToStringResults.mean()) + } +} diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/DatadogLateCrashReporter.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/DatadogLateCrashReporter.kt index ac1cf8021b..4821c6554b 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/DatadogLateCrashReporter.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/DatadogLateCrashReporter.kt @@ -9,6 +9,7 @@ package com.datadog.android.rum.internal import android.app.ApplicationExitInfo import android.os.Build import androidx.annotation.RequiresApi +import androidx.annotation.WorkerThread import com.datadog.android.api.InternalLogger import com.datadog.android.api.context.DatadogContext import com.datadog.android.api.feature.Feature @@ -97,6 +98,7 @@ internal class DatadogLateCrashReporter( } } + @WorkerThread @RequiresApi(Build.VERSION_CODES.R) override fun handleAnrCrash( anrExitInfo: ApplicationExitInfo, diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/LateCrashReporter.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/LateCrashReporter.kt index 243dc74eba..d767b2139d 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/LateCrashReporter.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/LateCrashReporter.kt @@ -9,6 +9,7 @@ package com.datadog.android.rum.internal import android.app.ApplicationExitInfo import android.os.Build import androidx.annotation.RequiresApi +import androidx.annotation.WorkerThread import com.datadog.android.api.storage.DataWriter import com.google.gson.JsonObject @@ -16,6 +17,7 @@ internal interface LateCrashReporter { fun handleNdkCrashEvent(event: Map<*, *>, rumWriter: DataWriter) + @WorkerThread @RequiresApi(Build.VERSION_CODES.R) fun handleAnrCrash( anrExitInfo: ApplicationExitInfo, diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/anr/AndroidTraceParser.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/anr/AndroidTraceParser.kt index dba025d225..58f7dcfc22 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/anr/AndroidTraceParser.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/anr/AndroidTraceParser.kt @@ -7,6 +7,7 @@ package com.datadog.android.rum.internal.anr import com.datadog.android.api.InternalLogger +import com.datadog.android.core.appendIfNotEmpty import com.datadog.android.core.feature.event.ThreadDump import java.io.IOException import java.io.InputStream @@ -33,7 +34,7 @@ internal class AndroidTraceParser( val threadDumps = mutableListOf() var isInThreadStackBlock = false - val currentThreadStack = mutableListOf() + val currentThreadStack = StringBuilder() var currentThreadName: String? = null var currentThreadState: String? = null @@ -45,7 +46,7 @@ internal class AndroidTraceParser( threadDumps += ThreadDump( name = currentThreadName, state = convertThreadState(currentThreadState.orEmpty()), - stack = currentThreadStack.joinToString("\n"), + stack = currentThreadStack.toString(), crashed = currentThreadName == "main" ) } @@ -73,7 +74,7 @@ internal class AndroidTraceParser( // - locked <0x0dd89f49> (a okhttp3.internal.concurrent.TaskRunner) // we want to skip them for now // also we want to skip any non-stack lines in the thread info block - currentThreadStack += line + currentThreadStack.appendIfNotEmpty('\n').append(line) } } diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/net/RumRequestFactory.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/net/RumRequestFactory.kt index 6277573bc9..2ab2fbd30d 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/net/RumRequestFactory.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/net/RumRequestFactory.kt @@ -108,24 +108,20 @@ internal class RumRequestFactory( env: String, variant: String, executionContext: RequestExecutionContext - ): String { - val elements = mutableListOf( - "${RumAttributes.SERVICE_NAME}:$serviceName", - "${RumAttributes.APPLICATION_VERSION}:$version", - "${RumAttributes.SDK_VERSION}:$sdkVersion", - "${RumAttributes.ENV}:$env" - ) + ) = buildString { + append("${RumAttributes.SERVICE_NAME}:$serviceName").append(",") + .append("${RumAttributes.APPLICATION_VERSION}:$version").append(",") + .append("${RumAttributes.SDK_VERSION}:$sdkVersion").append(",") + .append("${RumAttributes.ENV}:$env") if (variant.isNotEmpty()) { - elements.add("${RumAttributes.VARIANT}:$variant") + append(",").append("${RumAttributes.VARIANT}:$variant") } if (executionContext.previousResponseCode != null) { // we had a previous failure - elements.add("${RETRY_COUNT_KEY}:${executionContext.attemptNumber}") - elements.add("${LAST_FAILURE_STATUS_KEY}:${executionContext.previousResponseCode}") + append(",").append("${RETRY_COUNT_KEY}:${executionContext.attemptNumber}") + append(",").append("${LAST_FAILURE_STATUS_KEY}:${executionContext.previousResponseCode}") } - - return elements.joinToString(",") } @Suppress("TooGenericExceptionCaught") From 6e5c73f01368895e3905ead99d581caa7e596d5f Mon Sep 17 00:00:00 2001 From: Timur Valeev Date: Tue, 17 Dec 2024 11:03:38 +0000 Subject: [PATCH 2/9] RUM-6286: Adding StringBuilder.clear() to whitelist --- detekt_custom.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/detekt_custom.yml b/detekt_custom.yml index 5ae85b0ace..fbb2885a12 100644 --- a/detekt_custom.yml +++ b/detekt_custom.yml @@ -1265,6 +1265,7 @@ datadog: - "kotlin.text.StringBuilder()" - "kotlin.text.buildString(kotlin.Function1)" - "kotlin.text.buildString(kotlin.Int, kotlin.Function1)" + - "java.lang.StringBuilder.clear()" # endregion # region Kotlin Misc - "kotlin.IllegalArgumentException(kotlin.String?)" From 3240dc601362ecf91a65167e3f7aa5156db3c123 Mon Sep 17 00:00:00 2001 From: Timur Valeev Date: Tue, 17 Dec 2024 12:09:38 +0000 Subject: [PATCH 3/9] RUM-6286: post-review fixes: using property-base approach in string builder tests --- .../android/core/StringBuilderExtKtTest.kt | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/StringBuilderExtKtTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/StringBuilderExtKtTest.kt index 181b4e1346..6e0b81b1ea 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/StringBuilderExtKtTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/StringBuilderExtKtTest.kt @@ -1,36 +1,39 @@ package com.datadog.android.core +import fr.xgouchet.elmyr.annotation.StringForgery import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test class StringBuilderExtKtTest { @Test - fun `M add char W addIfNotEmpty {buffer is not empty}`() { + fun `M add char W addIfNotEmpty {buffer is not empty}`( + @StringForgery(regex = ".+") initialContent: String + ) { // Given - val initialData = "something inside buffer already" - val buffer = StringBuilder(initialData) + val buffer = StringBuilder(initialContent) // When buffer.appendIfNotEmpty(' ') // Then - assertThat(buffer.toString()).isEqualTo("$initialData ") + assertThat(buffer.toString()).isEqualTo("$initialContent ") } @Test - fun `M add str W addIfNotEmpty {buffer is not empty}`() { + fun `M add str W addIfNotEmpty {buffer is not empty}`( + @StringForgery(regex = ".+") initialContent: String + ) { // Given - val initialData = "something inside buffer already" - val buffer = StringBuilder(initialData) + val buffer = StringBuilder(initialContent) // When buffer.appendIfNotEmpty(" ") // Then - assertThat(buffer.toString()).isEqualTo("$initialData ") + assertThat(buffer.toString()).isEqualTo("$initialContent ") } @Test From ccb2ae3aae7680b6646440ccf99bad18993e1734 Mon Sep 17 00:00:00 2001 From: Timur Valeev Date: Tue, 17 Dec 2024 12:10:02 +0000 Subject: [PATCH 4/9] RUM-6286: post-review fixes: using p95 percentile to compare performance --- ...tToStringVsStringBuilderPerformanceTest.kt | 111 ++++++++++++++++++ .../datadog/android/utils/PerformanceTest.kt | 64 ---------- 2 files changed, 111 insertions(+), 64 deletions(-) create mode 100644 dd-sdk-android-core/src/test/kotlin/com/datadog/android/utils/JointToStringVsStringBuilderPerformanceTest.kt delete mode 100644 dd-sdk-android-core/src/test/kotlin/com/datadog/android/utils/PerformanceTest.kt diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/utils/JointToStringVsStringBuilderPerformanceTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/utils/JointToStringVsStringBuilderPerformanceTest.kt new file mode 100644 index 0000000000..cad174c375 --- /dev/null +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/utils/JointToStringVsStringBuilderPerformanceTest.kt @@ -0,0 +1,111 @@ +/* + * 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.utils + +import com.datadog.android.core.appendIfNotEmpty +import com.datadog.android.utils.forge.Configurator +import fr.xgouchet.elmyr.Forge +import fr.xgouchet.elmyr.junit5.ForgeConfiguration +import fr.xgouchet.elmyr.junit5.ForgeExtension +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.api.extension.Extensions +import kotlin.math.pow +import kotlin.math.round +import kotlin.math.sqrt +import kotlin.system.measureNanoTime + +private const val ITEMS_TO_JOINT = 10_000 +private const val REPETITION_COUNT = 10_000 + +@Extensions( + ExtendWith(ForgeExtension::class) +) +@ForgeConfiguration(value = Configurator::class, seed = 0x50c72968d123L) +internal class JointToStringVsStringBuilderPerformanceTest { + + @Test + fun `M be faster than joinToString W buildString`(forge: Forge) { + val itemsForJoin = forge.aList(ITEMS_TO_JOINT) { forge.aString() } + val jointToStringExecutionTime = mutableListOf() + val builderResultsExecutionTime = mutableListOf() + + var jointToStringResult: String + var builderResult: String + + for (i in 0..REPETITION_COUNT) { + jointToStringExecutionTime.add( + measureNanoTime { + val jointToStringContainer = mutableListOf() + for (item in itemsForJoin) { + jointToStringContainer.add(item) + } + jointToStringResult = jointToStringContainer.joinToString(separator = " ") { it } + } + ) + + builderResultsExecutionTime.add( + measureNanoTime { + builderResult = buildString { + itemsForJoin.forEach { item -> appendIfNotEmpty(' ').append(item) } + } + } + ) + + assertThat(builderResult).isEqualTo(jointToStringResult) // same result + } + + val statisticsReport = ( + "buildString:\n" + + " mean = ${builderResultsExecutionTime.mean}\n" + + " std = ${builderResultsExecutionTime.std}\n" + + " cv = ${"%.2f".format(builderResultsExecutionTime.cv)}%\n" + + " p50 = ${builderResultsExecutionTime.percentile(50)}\n" + + " p90 = ${builderResultsExecutionTime.percentile(90)}\n" + + " p95 = ${builderResultsExecutionTime.percentile(95)}\n" + + " p99 = ${builderResultsExecutionTime.percentile(99)}\n" + + "\n" + + "joinToString:\n" + + " mean = ${jointToStringExecutionTime.mean}\n" + + " std = ${jointToStringExecutionTime.std}\n" + + " cv = ${"%.2f".format(jointToStringExecutionTime.cv)}%\n" + + " p50 = ${jointToStringExecutionTime.percentile(50)},\n" + + " p90 = ${jointToStringExecutionTime.percentile(90)},\n" + + " p95 = ${jointToStringExecutionTime.percentile(95)},\n" + + " p99 = ${jointToStringExecutionTime.percentile(99)}\n" + ) + + println(statisticsReport) + + assertThat( + builderResultsExecutionTime.percentile(95) + ).isLessThan( + jointToStringExecutionTime.percentile(95) + ) + } + + companion object { + private val List.mean + get() = (sum().toDouble() / size) + + private val List.std: Double + get() { + val m = mean + return sqrt( + sumOf { (it - m).pow(2.0) } / size + ) + } + + private val List.cv: Double + get() = std / mean * 100.0 + + private fun List.percentile(k: Int): Long { + val p = (k / 100.0) * (size + 1) + return sorted()[round(p).toInt()] + } + } +} diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/utils/PerformanceTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/utils/PerformanceTest.kt deleted file mode 100644 index eaf84b4f70..0000000000 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/utils/PerformanceTest.kt +++ /dev/null @@ -1,64 +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.android.utils - -import com.datadog.android.utils.forge.Configurator -import fr.xgouchet.elmyr.Forge -import fr.xgouchet.elmyr.junit5.ForgeConfiguration -import fr.xgouchet.elmyr.junit5.ForgeExtension -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.extension.Extensions -import kotlin.system.measureNanoTime - -private const val ITEMS_TO_JOINT = 10_000 -private const val REPETITION_COUNT = 10_000 - -@Extensions( - ExtendWith(ForgeExtension::class) -) -@ForgeConfiguration(Configurator::class) -internal class PerformanceTest { - - private fun List.mean() = sum().toDouble() / size - - @Test - fun `M be faster than joinToString W buildString`(forge: Forge) { - val itemsForJoin = forge.aList(ITEMS_TO_JOINT) { forge.aString() } - val jointToStringResults = mutableListOf() - val builderResults = mutableListOf() - - var jointToStringResult = "" - var builderResult = "" - - for (i in 0..REPETITION_COUNT) { - jointToStringResults.add( - measureNanoTime { - val jointToStringContainer = mutableListOf() - for (item in itemsForJoin) { - jointToStringContainer.add(item) - } - jointToStringResult = jointToStringContainer.joinToString(separator = " ") { it } - } - ) - - builderResults.add( - measureNanoTime { - builderResult = buildString { - itemsForJoin.forEachIndexed { i, item -> - if (i > 0) append(" ") - append(item) - } - } - } - ) - } - - assertThat(builderResult).isEqualTo(jointToStringResult) // same result - assertThat(builderResults.mean()).isLessThan(jointToStringResults.mean()) - } -} From f56fefbf6c778d0fb57588b52fb96eb43eb97d9b Mon Sep 17 00:00:00 2001 From: Timur Valeev Date: Tue, 17 Dec 2024 12:19:52 +0000 Subject: [PATCH 5/9] RUM-6286: post-review fixes: using p95 percentile to compare performance --- ...tToStringVsStringBuilderPerformanceTest.kt | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/utils/JointToStringVsStringBuilderPerformanceTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/utils/JointToStringVsStringBuilderPerformanceTest.kt index cad174c375..481a3bcd77 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/utils/JointToStringVsStringBuilderPerformanceTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/utils/JointToStringVsStringBuilderPerformanceTest.kt @@ -32,12 +32,12 @@ internal class JointToStringVsStringBuilderPerformanceTest { fun `M be faster than joinToString W buildString`(forge: Forge) { val itemsForJoin = forge.aList(ITEMS_TO_JOINT) { forge.aString() } val jointToStringExecutionTime = mutableListOf() - val builderResultsExecutionTime = mutableListOf() + val buildStringExecutionTime = mutableListOf() var jointToStringResult: String var builderResult: String - for (i in 0..REPETITION_COUNT) { + repeat(REPETITION_COUNT) { jointToStringExecutionTime.add( measureNanoTime { val jointToStringContainer = mutableListOf() @@ -48,7 +48,7 @@ internal class JointToStringVsStringBuilderPerformanceTest { } ) - builderResultsExecutionTime.add( + buildStringExecutionTime.add( measureNanoTime { builderResult = buildString { itemsForJoin.forEach { item -> appendIfNotEmpty(' ').append(item) } @@ -61,13 +61,13 @@ internal class JointToStringVsStringBuilderPerformanceTest { val statisticsReport = ( "buildString:\n" + - " mean = ${builderResultsExecutionTime.mean}\n" + - " std = ${builderResultsExecutionTime.std}\n" + - " cv = ${"%.2f".format(builderResultsExecutionTime.cv)}%\n" + - " p50 = ${builderResultsExecutionTime.percentile(50)}\n" + - " p90 = ${builderResultsExecutionTime.percentile(90)}\n" + - " p95 = ${builderResultsExecutionTime.percentile(95)}\n" + - " p99 = ${builderResultsExecutionTime.percentile(99)}\n" + + " mean = ${buildStringExecutionTime.mean}\n" + + " std = ${buildStringExecutionTime.std}\n" + + " cv = ${"%.2f".format(buildStringExecutionTime.cv)}%\n" + + " p50 = ${buildStringExecutionTime.percentile(50)}\n" + + " p90 = ${buildStringExecutionTime.percentile(90)}\n" + + " p95 = ${buildStringExecutionTime.percentile(95)}\n" + + " p99 = ${buildStringExecutionTime.percentile(99)}\n" + "\n" + "joinToString:\n" + " mean = ${jointToStringExecutionTime.mean}\n" + @@ -82,7 +82,7 @@ internal class JointToStringVsStringBuilderPerformanceTest { println(statisticsReport) assertThat( - builderResultsExecutionTime.percentile(95) + buildStringExecutionTime.percentile(95) ).isLessThan( jointToStringExecutionTime.percentile(95) ) From b7a8a442f1eb92f22961dce00190254dea5a0f51 Mon Sep 17 00:00:00 2001 From: Timur Valeev Date: Tue, 17 Dec 2024 13:38:26 +0000 Subject: [PATCH 6/9] RUM-6286: reduce flakiness, fix configuration --- .../com/datadog/android/core/StringBuilderExtKtTest.kt | 8 +++++++- .../utils/JointToStringVsStringBuilderPerformanceTest.kt | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/StringBuilderExtKtTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/StringBuilderExtKtTest.kt index 6e0b81b1ea..f0cb5979cd 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/StringBuilderExtKtTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/StringBuilderExtKtTest.kt @@ -1,10 +1,16 @@ package com.datadog.android.core import fr.xgouchet.elmyr.annotation.StringForgery +import fr.xgouchet.elmyr.junit5.ForgeExtension import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.api.extension.Extensions -class StringBuilderExtKtTest { +@Extensions( + ExtendWith(ForgeExtension::class) +) +internal class StringBuilderExtKtTest { @Test fun `M add char W addIfNotEmpty {buffer is not empty}`( diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/utils/JointToStringVsStringBuilderPerformanceTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/utils/JointToStringVsStringBuilderPerformanceTest.kt index 481a3bcd77..cf1e1303a1 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/utils/JointToStringVsStringBuilderPerformanceTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/utils/JointToStringVsStringBuilderPerformanceTest.kt @@ -82,9 +82,9 @@ internal class JointToStringVsStringBuilderPerformanceTest { println(statisticsReport) assertThat( - buildStringExecutionTime.percentile(95) + buildStringExecutionTime.percentile(90) ).isLessThan( - jointToStringExecutionTime.percentile(95) + jointToStringExecutionTime.percentile(90) ) } From d6a6b62b34ed9e29320e0b16bf189873255d8bcc Mon Sep 17 00:00:00 2001 From: Timur Valeev Date: Wed, 18 Dec 2024 10:20:43 +0000 Subject: [PATCH 7/9] RUM-6286: post-review fixes - moving StringBuilderExt into internal module --- dd-sdk-android-core/api/apiSurface | 2 -- .../api/dd-sdk-android-core.api | 5 --- .../datadog/android/core/StringBuilderExt.kt | 27 --------------- ...tToStringVsStringBuilderPerformanceTest.kt | 34 +++++++++---------- dd-sdk-android-internal/api/apiSurface | 2 ++ .../api/dd-sdk-android-internal.api | 5 +++ .../internal/utils/StringBuilderExt.kt | 27 +++++++++++++++ .../internal/utils}/StringBuilderExtKtTest.kt | 12 ++++--- detekt_custom.yml | 2 +- .../rum/internal/anr/AndroidTraceParser.kt | 2 +- 10 files changed, 61 insertions(+), 57 deletions(-) delete mode 100644 dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/StringBuilderExt.kt create mode 100644 dd-sdk-android-internal/src/main/java/com/datadog/android/internal/utils/StringBuilderExt.kt rename {dd-sdk-android-core/src/test/kotlin/com/datadog/android/core => dd-sdk-android-internal/src/test/java/com/datadog/internal/utils}/StringBuilderExtKtTest.kt (82%) diff --git a/dd-sdk-android-core/api/apiSurface b/dd-sdk-android-core/api/apiSurface index 8d71195036..d5b414c3a4 100644 --- a/dd-sdk-android-core/api/apiSurface +++ b/dd-sdk-android-core/api/apiSurface @@ -190,8 +190,6 @@ class com.datadog.android.core.SdkReference fun get(): com.datadog.android.api.SdkCore? fun allowThreadDiskReads(() -> T): T fun allowThreadDiskWrites(() -> T): T -fun StringBuilder.appendIfNotEmpty(String) -fun StringBuilder.appendIfNotEmpty(Char) enum com.datadog.android.core.configuration.BackPressureMitigation - DROP_OLDEST - IGNORE_NEWEST diff --git a/dd-sdk-android-core/api/dd-sdk-android-core.api b/dd-sdk-android-core/api/dd-sdk-android-core.api index c6a9155c45..0e0ac78ac3 100644 --- a/dd-sdk-android-core/api/dd-sdk-android-core.api +++ b/dd-sdk-android-core/api/dd-sdk-android-core.api @@ -543,11 +543,6 @@ public final class com/datadog/android/core/StrictModeExtKt { public static final fun allowThreadDiskWrites (Lkotlin/jvm/functions/Function0;)Ljava/lang/Object; } -public final class com/datadog/android/core/StringBuilderExtKt { - public static final fun appendIfNotEmpty (Ljava/lang/StringBuilder;C)Ljava/lang/StringBuilder; - public static final fun appendIfNotEmpty (Ljava/lang/StringBuilder;Ljava/lang/String;)Ljava/lang/StringBuilder; -} - public final class com/datadog/android/core/configuration/BackPressureMitigation : java/lang/Enum { public static final field DROP_OLDEST Lcom/datadog/android/core/configuration/BackPressureMitigation; public static final field IGNORE_NEWEST Lcom/datadog/android/core/configuration/BackPressureMitigation; diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/StringBuilderExt.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/StringBuilderExt.kt deleted file mode 100644 index 64e4d76f5e..0000000000 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/StringBuilderExt.kt +++ /dev/null @@ -1,27 +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.android.core - -/** - * This utility function helps replace joinToString calls with more efficient StringBuilder's methods calls. - * In case when content should be separated with some separator it's handy to add it in front of new string only - * if buffer already contain some data - * @param str - string that should be added into buffer only if it already contains some data - */ -fun StringBuilder.appendIfNotEmpty(str: String) = apply { - if (isNotEmpty()) append(str) -} - -/** - * This utility function helps replace joinToString calls with more efficient StringBuilder's methods calls. - * In case when content should be separated with some separator it's handy to add it in front of new string only - * if buffer already contain some data - * @param char - char that should be added into buffer only if it already contains some data - */ -fun StringBuilder.appendIfNotEmpty(char: Char) = apply { - if (isNotEmpty()) append(char) -} diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/utils/JointToStringVsStringBuilderPerformanceTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/utils/JointToStringVsStringBuilderPerformanceTest.kt index cf1e1303a1..ea6583a03b 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/utils/JointToStringVsStringBuilderPerformanceTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/utils/JointToStringVsStringBuilderPerformanceTest.kt @@ -5,7 +5,7 @@ */ package com.datadog.android.utils -import com.datadog.android.core.appendIfNotEmpty +import com.datadog.android.internal.utils.appendIfNotEmpty import com.datadog.android.utils.forge.Configurator import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.junit5.ForgeConfiguration @@ -19,9 +19,6 @@ import kotlin.math.round import kotlin.math.sqrt import kotlin.system.measureNanoTime -private const val ITEMS_TO_JOINT = 10_000 -private const val REPETITION_COUNT = 10_000 - @Extensions( ExtendWith(ForgeExtension::class) ) @@ -30,15 +27,15 @@ internal class JointToStringVsStringBuilderPerformanceTest { @Test fun `M be faster than joinToString W buildString`(forge: Forge) { - val itemsForJoin = forge.aList(ITEMS_TO_JOINT) { forge.aString() } - val jointToStringExecutionTime = mutableListOf() + val itemsForJoin = forge.aList(ITEMS_TO_JOIN) { forge.aString() } + val joinToStringExecutionTime = mutableListOf() val buildStringExecutionTime = mutableListOf() var jointToStringResult: String var builderResult: String repeat(REPETITION_COUNT) { - jointToStringExecutionTime.add( + joinToStringExecutionTime.add( measureNanoTime { val jointToStringContainer = mutableListOf() for (item in itemsForJoin) { @@ -70,25 +67,28 @@ internal class JointToStringVsStringBuilderPerformanceTest { " p99 = ${buildStringExecutionTime.percentile(99)}\n" + "\n" + "joinToString:\n" + - " mean = ${jointToStringExecutionTime.mean}\n" + - " std = ${jointToStringExecutionTime.std}\n" + - " cv = ${"%.2f".format(jointToStringExecutionTime.cv)}%\n" + - " p50 = ${jointToStringExecutionTime.percentile(50)},\n" + - " p90 = ${jointToStringExecutionTime.percentile(90)},\n" + - " p95 = ${jointToStringExecutionTime.percentile(95)},\n" + - " p99 = ${jointToStringExecutionTime.percentile(99)}\n" + " mean = ${joinToStringExecutionTime.mean}\n" + + " std = ${joinToStringExecutionTime.std}\n" + + " cv = ${"%.2f".format(joinToStringExecutionTime.cv)}%\n" + + " p50 = ${joinToStringExecutionTime.percentile(50)},\n" + + " p90 = ${joinToStringExecutionTime.percentile(90)},\n" + + " p95 = ${joinToStringExecutionTime.percentile(95)},\n" + + " p99 = ${joinToStringExecutionTime.percentile(99)}\n" ) - println(statisticsReport) - assertThat( buildStringExecutionTime.percentile(90) + ).withFailMessage( + statisticsReport ).isLessThan( - jointToStringExecutionTime.percentile(90) + joinToStringExecutionTime.percentile(90) ) } companion object { + private const val ITEMS_TO_JOIN = 10_000 + private const val REPETITION_COUNT = 10_000 + private val List.mean get() = (sum().toDouble() / size) diff --git a/dd-sdk-android-internal/api/apiSurface b/dd-sdk-android-internal/api/apiSurface index 8f42e63ef2..8a1d378cd6 100644 --- a/dd-sdk-android-internal/api/apiSurface +++ b/dd-sdk-android-internal/api/apiSurface @@ -38,6 +38,8 @@ object com.datadog.android.internal.utils.ImageViewUtils fun resolveContentRectWithScaling(android.widget.ImageView, android.graphics.drawable.Drawable, android.widget.ImageView.ScaleType? = null): android.graphics.Rect fun Int.densityNormalized(Float): Int fun Long.densityNormalized(Float): Long +fun StringBuilder.appendIfNotEmpty(String) +fun StringBuilder.appendIfNotEmpty(Char) fun Throwable.loggableStackTrace(): String annotation com.datadog.tools.annotation.NoOpImplementation constructor(Boolean = false) diff --git a/dd-sdk-android-internal/api/dd-sdk-android-internal.api b/dd-sdk-android-internal/api/dd-sdk-android-internal.api index 1b50c65949..a8a3271696 100644 --- a/dd-sdk-android-internal/api/dd-sdk-android-internal.api +++ b/dd-sdk-android-internal/api/dd-sdk-android-internal.api @@ -130,6 +130,11 @@ public final class com/datadog/android/internal/utils/LongExtKt { public static final fun densityNormalized (JF)J } +public final class com/datadog/android/internal/utils/StringBuilderExtKt { + public static final fun appendIfNotEmpty (Ljava/lang/StringBuilder;C)Ljava/lang/StringBuilder; + public static final fun appendIfNotEmpty (Ljava/lang/StringBuilder;Ljava/lang/String;)Ljava/lang/StringBuilder; +} + public final class com/datadog/android/internal/utils/ThrowableExtKt { public static final fun loggableStackTrace (Ljava/lang/Throwable;)Ljava/lang/String; } diff --git a/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/utils/StringBuilderExt.kt b/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/utils/StringBuilderExt.kt new file mode 100644 index 0000000000..1c57cbe6b9 --- /dev/null +++ b/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/utils/StringBuilderExt.kt @@ -0,0 +1,27 @@ +/* + * 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.internal.utils + +/** + * This utility function helps to replace [joinToString] calls with more efficient [StringBuilder] methods calls. + * In case when content should be separated with some separator, it's handy to add it in front of a new string only + * if buffer already contains some data. + * @param str string that should be added to the buffer only if it already contains some data. + */ +fun StringBuilder.appendIfNotEmpty(str: String) = apply { + if (isNotEmpty()) append(str) +} + +/** + * This utility function helps to replace [joinToString] calls with more efficient [StringBuilder] methods calls. + * In case when content should be separated with some separator, it's handy to add it in front of a new string only + * if buffer already contains some data. + * @param char char that should be added to the buffer only if it already contains some data. + */ +fun StringBuilder.appendIfNotEmpty(char: Char) = apply { + if (isNotEmpty()) append(char) +} diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/StringBuilderExtKtTest.kt b/dd-sdk-android-internal/src/test/java/com/datadog/internal/utils/StringBuilderExtKtTest.kt similarity index 82% rename from dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/StringBuilderExtKtTest.kt rename to dd-sdk-android-internal/src/test/java/com/datadog/internal/utils/StringBuilderExtKtTest.kt index f0cb5979cd..8817047753 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/StringBuilderExtKtTest.kt +++ b/dd-sdk-android-internal/src/test/java/com/datadog/internal/utils/StringBuilderExtKtTest.kt @@ -1,5 +1,12 @@ -package com.datadog.android.core +/* + * 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.internal.utils + +import com.datadog.android.internal.utils.appendIfNotEmpty import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.junit5.ForgeExtension import org.assertj.core.api.Assertions.assertThat @@ -38,7 +45,6 @@ internal class StringBuilderExtKtTest { buffer.appendIfNotEmpty(" ") // Then - assertThat(buffer.toString()).isEqualTo("$initialContent ") } @@ -51,7 +57,6 @@ internal class StringBuilderExtKtTest { buffer.appendIfNotEmpty(' ') // Then - assertThat(buffer.toString()).isEqualTo("") } @@ -64,7 +69,6 @@ internal class StringBuilderExtKtTest { buffer.appendIfNotEmpty(" ") // Then - assertThat(buffer.toString()).isEqualTo("") } } diff --git a/detekt_custom.yml b/detekt_custom.yml index fbb2885a12..988918490f 100644 --- a/detekt_custom.yml +++ b/detekt_custom.yml @@ -780,6 +780,7 @@ datadog: - "java.lang.StringBuilder.append(kotlin.Char)" - "java.lang.StringBuilder.append(kotlin.CharArray?)" - "java.lang.StringBuilder.append(kotlin.String?)" + - "java.lang.StringBuilder.clear()" - "java.lang.StringBuilder.constructor()" - "java.math.BigInteger.toHexString()" - "java.math.BigInteger.toLong()" @@ -1265,7 +1266,6 @@ datadog: - "kotlin.text.StringBuilder()" - "kotlin.text.buildString(kotlin.Function1)" - "kotlin.text.buildString(kotlin.Int, kotlin.Function1)" - - "java.lang.StringBuilder.clear()" # endregion # region Kotlin Misc - "kotlin.IllegalArgumentException(kotlin.String?)" diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/anr/AndroidTraceParser.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/anr/AndroidTraceParser.kt index 58f7dcfc22..ccb599b5e5 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/anr/AndroidTraceParser.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/anr/AndroidTraceParser.kt @@ -7,8 +7,8 @@ package com.datadog.android.rum.internal.anr import com.datadog.android.api.InternalLogger -import com.datadog.android.core.appendIfNotEmpty import com.datadog.android.core.feature.event.ThreadDump +import com.datadog.android.internal.utils.appendIfNotEmpty import java.io.IOException import java.io.InputStream import java.util.Locale From 0ed3809fd7927675f88bb49eaba08a34c3049d7e Mon Sep 17 00:00:00 2001 From: Timur Valeev Date: Wed, 18 Dec 2024 13:18:14 +0000 Subject: [PATCH 8/9] RUM-6286: post-review fixes - removing seed --- .../utils/JointToStringVsStringBuilderPerformanceTest.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/utils/JointToStringVsStringBuilderPerformanceTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/utils/JointToStringVsStringBuilderPerformanceTest.kt index ea6583a03b..6f3b8acd40 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/utils/JointToStringVsStringBuilderPerformanceTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/utils/JointToStringVsStringBuilderPerformanceTest.kt @@ -6,9 +6,7 @@ package com.datadog.android.utils import com.datadog.android.internal.utils.appendIfNotEmpty -import com.datadog.android.utils.forge.Configurator import fr.xgouchet.elmyr.Forge -import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -22,7 +20,6 @@ import kotlin.system.measureNanoTime @Extensions( ExtendWith(ForgeExtension::class) ) -@ForgeConfiguration(value = Configurator::class, seed = 0x50c72968d123L) internal class JointToStringVsStringBuilderPerformanceTest { @Test From b2a6eaef92804964f5b304839642a32ebacb72d4 Mon Sep 17 00:00:00 2001 From: Timur Valeev Date: Wed, 18 Dec 2024 14:47:05 +0000 Subject: [PATCH 9/9] RUM-6286: post-review fixes - align test names with methods --- .../com/datadog/internal/utils/StringBuilderExtKtTest.kt | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/dd-sdk-android-internal/src/test/java/com/datadog/internal/utils/StringBuilderExtKtTest.kt b/dd-sdk-android-internal/src/test/java/com/datadog/internal/utils/StringBuilderExtKtTest.kt index 8817047753..2350c6c887 100644 --- a/dd-sdk-android-internal/src/test/java/com/datadog/internal/utils/StringBuilderExtKtTest.kt +++ b/dd-sdk-android-internal/src/test/java/com/datadog/internal/utils/StringBuilderExtKtTest.kt @@ -20,7 +20,7 @@ import org.junit.jupiter.api.extension.Extensions internal class StringBuilderExtKtTest { @Test - fun `M add char W addIfNotEmpty {buffer is not empty}`( + fun `M add char W appendIfNotEmpty {buffer is not empty}`( @StringForgery(regex = ".+") initialContent: String ) { // Given @@ -30,12 +30,11 @@ internal class StringBuilderExtKtTest { buffer.appendIfNotEmpty(' ') // Then - assertThat(buffer.toString()).isEqualTo("$initialContent ") } @Test - fun `M add str W addIfNotEmpty {buffer is not empty}`( + fun `M add str W appendIfNotEmpty {buffer is not empty}`( @StringForgery(regex = ".+") initialContent: String ) { // Given @@ -49,7 +48,7 @@ internal class StringBuilderExtKtTest { } @Test - fun `M not add any char W addIfNotEmpty {buffer is empty}`() { + fun `M not add any char W appendIfNotEmpty {buffer is empty}`() { // Given val buffer = StringBuilder() @@ -61,7 +60,7 @@ internal class StringBuilderExtKtTest { } @Test - fun `M not add any str W addIfNotEmpty {buffer is empty}`() { + fun `M not add any str W appendIfNotEmpty {buffer is empty}`() { // Given val buffer = StringBuilder()