Add incoming group message benchmark tests.

This commit is contained in:
Cody Henthorne
2026-02-13 14:58:22 -05:00
committed by Alex Hart
parent e67307a961
commit 08254edae6
16 changed files with 344 additions and 128 deletions

View File

@@ -2,6 +2,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<queries>
<package android:name="org.thoughtcrime.securesms" />
<package android:name="org.thoughtcrime.securesms.benchmark" />
</queries>
</manifest>

View File

@@ -5,10 +5,11 @@ import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until
object BenchmarkSetup {
private const val TARGET_PACKAGE = "org.thoughtcrime.securesms"
private const val TARGET_PACKAGE = "org.thoughtcrime.securesms.benchmark"
private const val RECEIVER = "org.signal.benchmark.BenchmarkCommandReceiver"
fun setup(type: String, device: UiDevice) {
device.executeShellCommand("pm clear $TARGET_PACKAGE")
device.executeShellCommand("am start -W -n $TARGET_PACKAGE/org.signal.benchmark.BenchmarkSetupActivity --es setup-type $type")
device.wait(Until.hasObject(By.textContains("done")), 25_000L)
}
@@ -17,6 +18,10 @@ object BenchmarkSetup {
device.benchmarkCommandBroadcast("individual-send")
}
fun setupGroupSend(device: UiDevice) {
device.benchmarkCommandBroadcast("group-send")
}
fun releaseMessages(device: UiDevice) {
device.benchmarkCommandBroadcast("release-messages")
}

View File

@@ -25,7 +25,7 @@ class ConversationBenchmarks {
fun simpleConversationOpen() {
var setup = false
benchmarkRule.measureRepeated(
packageName = "org.thoughtcrime.securesms",
packageName = "org.thoughtcrime.securesms.benchmark",
metrics = listOf(
TraceSectionMetric("6-ConversationOpen"),
TraceSectionMetric("1-ConversationOpen-ViewModel-Init"),

View File

@@ -0,0 +1,85 @@
/*
* Copyright 2026 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.benchmark
import androidx.annotation.RequiresApi
import androidx.benchmark.macro.CompilationMode
import androidx.benchmark.macro.ExperimentalMetricApi
import androidx.benchmark.macro.TraceSectionMetric
import androidx.benchmark.macro.TraceSectionMetric.Mode
import androidx.benchmark.macro.junit4.MacrobenchmarkRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
/**
* Macrobenchmark benchmarks for message processing performance.
*/
@OptIn(ExperimentalMetricApi::class)
@RunWith(AndroidJUnit4::class)
@RequiresApi(31)
class GroupMessageProcessingBenchmarks {
@get:Rule
val benchmarkRule = MacrobenchmarkRule()
@Test
fun groupMessageReceiveOnConversationList() {
run(withConversationOpen = false)
}
@Test
fun individualMessageReceiveOnConversation() {
run(withConversationOpen = true)
}
private fun run(withConversationOpen: Boolean) {
benchmarkRule.measureRepeated(
packageName = "org.thoughtcrime.securesms.benchmark",
metrics = listOf(
TraceSectionMetric(
sectionName = "IncomingMessageObserver#decryptMessage",
mode = Mode.Average
),
TraceSectionMetric(
sectionName = "MessageContentProcessor#handleMessage",
mode = Mode.Average
),
TraceSectionMetric(
sectionName = "IncomingMessageObserver#processMessage",
mode = Mode.Average
),
TraceSectionMetric(
sectionName = "IncomingMessageObserver#totalProcessing",
mode = Mode.Sum
)
),
iterations = 5,
compilationMode = CompilationMode.Partial(),
setupBlock = {
BenchmarkSetup.setup("group-message-send", device)
killProcess()
startActivityAndWait()
device.waitForIdle()
BenchmarkSetup.setupGroupSend(device)
val uiObject = device.wait(Until.findObject(By.textContains("Title")), 5_000)
if (withConversationOpen) {
uiObject.click()
}
}
) {
BenchmarkSetup.releaseMessages(device)
device.wait(Until.hasObject(By.textContains("505")),10_000L)
}
}
}

View File

@@ -5,7 +5,6 @@
package org.thoughtcrime.benchmark
import android.os.SystemClock
import androidx.annotation.RequiresApi
import androidx.benchmark.macro.CompilationMode
import androidx.benchmark.macro.ExperimentalMetricApi
@@ -14,8 +13,6 @@ import androidx.benchmark.macro.TraceSectionMetric.Mode
import androidx.benchmark.macro.junit4.MacrobenchmarkRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.uiautomator.By
import androidx.test.uiautomator.BySelector
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until
import org.junit.Rule
import org.junit.Test
@@ -23,8 +20,6 @@ import org.junit.runner.RunWith
/**
* Macrobenchmark benchmarks for message processing performance.
*
* WARNING! THIS WILL WIPE YOUR SIGNAL INSTALL
*/
@OptIn(ExperimentalMetricApi::class)
@RunWith(AndroidJUnit4::class)
@@ -44,9 +39,8 @@ class MessageProcessingBenchmarks {
}
private fun run(withConversationOpen: Boolean) {
var setup = false
benchmarkRule.measureRepeated(
packageName = "org.thoughtcrime.securesms",
packageName = "org.thoughtcrime.securesms.benchmark",
metrics = listOf(
TraceSectionMetric(
sectionName = "IncomingMessageObserver#decryptMessage",
@@ -59,14 +53,16 @@ class MessageProcessingBenchmarks {
TraceSectionMetric(
sectionName = "IncomingMessageObserver#processMessage",
mode = Mode.Average
),
TraceSectionMetric(
sectionName = "IncomingMessageObserver#totalProcessing",
mode = Mode.Sum
)
),
iterations = 5,
compilationMode = CompilationMode.Partial(),
setupBlock = {
if (!setup) {
BenchmarkSetup.setup("message-send", device)
}
BenchmarkSetup.setup("message-send", device)
killProcess()
startActivityAndWait()
@@ -74,7 +70,7 @@ class MessageProcessingBenchmarks {
BenchmarkSetup.setupIndividualSend(device)
val uiObject = device.wait(Until.findObject(By.textContains("Bob")), 5_000)
val uiObject = device.wait(Until.findObject(By.textContains("Buddy")), 5_000)
if (withConversationOpen) {
uiObject.click()
}
@@ -83,31 +79,7 @@ class MessageProcessingBenchmarks {
BenchmarkSetup.releaseMessages(device)
device.waitForAny(
10_000L,
By.textContains("101"),
By.textContains("202"),
By.textContains("303"),
By.textContains("404"),
By.textContains("505"),
)
device.wait(Until.hasObject(By.textContains("101")), 10_000L)
}
}
}
/**
* Inspired by [androidx.test.uiautomator.WaitMixin]
*/
fun UiDevice.waitForAny(timeout: Long, vararg conditions: BySelector): Boolean {
val startTime = SystemClock.uptimeMillis()
var result = conditions.any { this.hasObject(it) }
var elapsedTime = 0L
while (!result && elapsedTime < timeout) {
SystemClock.sleep(100)
result = conditions.any { this.hasObject(it) }
elapsedTime = SystemClock.uptimeMillis() - startTime
}
return result
}

View File

@@ -15,8 +15,6 @@ import org.junit.runner.RunWith
/**
* Macrobenchmark benchmarks for app startup performance.
*
* WARNING! THIS WILL WIPE YOUR SIGNAL INSTALL
*/
@RunWith(AndroidJUnit4::class)
@RequiresApi(31)
@@ -38,7 +36,7 @@ class StartupBenchmarks {
private fun measureStartup(iterations: Int, compilationMode: CompilationMode) {
var setup = false
benchmarkRule.measureRepeated(
packageName = "org.thoughtcrime.securesms",
packageName = "org.thoughtcrime.securesms.benchmark",
metrics = listOf(StartupTimingMetric(), TraceSectionMetric("ConversationListDataSource#load")),
iterations = iterations,
startupMode = StartupMode.COLD,

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2026 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.benchmark
import android.os.SystemClock
import androidx.test.uiautomator.BySelector
import androidx.test.uiautomator.UiDevice
/**
* Inspired by [androidx.test.uiautomator.WaitMixin]
*/
fun UiDevice.waitForAny(timeout: Long, vararg conditions: BySelector): Boolean {
val startTime = SystemClock.uptimeMillis()
var result = conditions.any { this.hasObject(it) }
var elapsedTime = 0L
while (!result && elapsedTime < timeout) {
SystemClock.sleep(100)
result = conditions.any { this.hasObject(it) }
elapsedTime = SystemClock.uptimeMillis() - startTime
}
return result
}