Добавлен модуль macrobenchmark и сценарии замера производительности
This commit is contained in:
@@ -83,6 +83,14 @@ android {
|
||||
// Enable baseline profiles in debug builds too for testing
|
||||
// Remove this in production
|
||||
}
|
||||
create("benchmark") {
|
||||
initWith(getByName("release"))
|
||||
signingConfig = signingConfigs.getByName("release")
|
||||
matchingFallbacks += listOf("release")
|
||||
isDebuggable = false
|
||||
isMinifyEnabled = false
|
||||
isShrinkResources = false
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
@@ -192,7 +200,7 @@ dependencies {
|
||||
}
|
||||
|
||||
// Baseline Profiles for startup performance
|
||||
implementation("androidx.profileinstaller:profileinstaller:1.3.1")
|
||||
implementation("androidx.profileinstaller:profileinstaller:1.4.1")
|
||||
|
||||
// Firebase Cloud Messaging
|
||||
implementation(platform("com.google.firebase:firebase-bom:32.7.0"))
|
||||
|
||||
@@ -29,5 +29,5 @@ dependencies {
|
||||
implementation("androidx.test.ext:junit:1.1.5")
|
||||
implementation("androidx.test.espresso:espresso-core:3.5.1")
|
||||
implementation("androidx.test.uiautomator:uiautomator:2.2.0")
|
||||
implementation("androidx.benchmark:benchmark-macro-junit4:1.2.2")
|
||||
implementation("androidx.benchmark:benchmark-macro-junit4:1.4.1")
|
||||
}
|
||||
|
||||
20
benchmark/README.md
Normal file
20
benchmark/README.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# Macrobenchmark
|
||||
|
||||
Этот модуль запускает замеры производительности приложения `:app` на устройстве:
|
||||
|
||||
- `coldStartup` — холодный запуск
|
||||
- `chatListScroll` — прокрутка списка чатов
|
||||
- `searchFlow` — вход в поиск и ввод запроса
|
||||
|
||||
## Запуск
|
||||
|
||||
```bash
|
||||
./gradlew :benchmark:connectedCheck
|
||||
```
|
||||
|
||||
Запуск только одного класса:
|
||||
|
||||
```bash
|
||||
./gradlew :benchmark:connectedAndroidTest \
|
||||
-Pandroid.testInstrumentationRunnerArguments.class=com.rosetta.messenger.benchmark.AppMacrobenchmark
|
||||
```
|
||||
37
benchmark/build.gradle.kts
Normal file
37
benchmark/build.gradle.kts
Normal file
@@ -0,0 +1,37 @@
|
||||
plugins {
|
||||
id("com.android.test")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.rosetta.messenger.benchmark"
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 28
|
||||
targetSdk = 34
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
testInstrumentationRunnerArguments["androidx.benchmark.enabledRules"] = "Macrobenchmark"
|
||||
testInstrumentationRunnerArguments["androidx.benchmark.suppressErrors"] = "EMULATOR,DEBUGGABLE"
|
||||
testInstrumentationRunnerArguments["androidx.benchmark.compilation.enabled"] = "false"
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "11"
|
||||
}
|
||||
|
||||
targetProjectPath = ":app"
|
||||
experimentalProperties["android.experimental.self-instrumenting"] = true
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("androidx.benchmark:benchmark-macro-junit4:1.4.1")
|
||||
implementation("androidx.test.ext:junit:1.1.5")
|
||||
implementation("androidx.test:runner:1.5.2")
|
||||
implementation("androidx.test.uiautomator:uiautomator:2.2.0")
|
||||
}
|
||||
2
benchmark/src/main/AndroidManifest.xml
Normal file
2
benchmark/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" />
|
||||
@@ -0,0 +1,150 @@
|
||||
package com.rosetta.messenger.benchmark
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.benchmark.macro.CompilationMode
|
||||
import androidx.benchmark.macro.FrameTimingMetric
|
||||
import androidx.benchmark.macro.MacrobenchmarkScope
|
||||
import androidx.benchmark.macro.StartupMode
|
||||
import androidx.benchmark.macro.StartupTimingMetric
|
||||
import androidx.benchmark.macro.junit4.MacrobenchmarkRule
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.LargeTest
|
||||
import androidx.test.uiautomator.By
|
||||
import androidx.test.uiautomator.Direction
|
||||
import androidx.test.uiautomator.UiObject2
|
||||
import androidx.test.uiautomator.Until
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@LargeTest
|
||||
class AppMacrobenchmark {
|
||||
|
||||
@get:Rule
|
||||
val benchmarkRule = MacrobenchmarkRule()
|
||||
|
||||
private val appPackage = "com.rosetta.messenger"
|
||||
private val searchTriggers = listOf("Search", "search", "Поиск", "поиск", "Найти", "найти")
|
||||
private val runtimePermissions = listOf(
|
||||
"android.permission.POST_NOTIFICATIONS",
|
||||
"android.permission.CAMERA",
|
||||
"android.permission.RECORD_AUDIO",
|
||||
"android.permission.READ_MEDIA_IMAGES",
|
||||
"android.permission.READ_MEDIA_VIDEO",
|
||||
"android.permission.BLUETOOTH_CONNECT"
|
||||
)
|
||||
|
||||
@Test
|
||||
fun coldStartup() {
|
||||
benchmarkRule.measureRepeated(
|
||||
packageName = appPackage,
|
||||
metrics = listOf(StartupTimingMetric(), FrameTimingMetric()),
|
||||
compilationMode = CompilationMode.Partial(),
|
||||
startupMode = StartupMode.COLD,
|
||||
iterations = 1,
|
||||
setupBlock = {
|
||||
grantRuntimePermissions()
|
||||
pressHome()
|
||||
}
|
||||
) {
|
||||
launchMainActivity()
|
||||
device.waitForIdle()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun chatListScroll() {
|
||||
benchmarkRule.measureRepeated(
|
||||
packageName = appPackage,
|
||||
metrics = listOf(FrameTimingMetric()),
|
||||
compilationMode = CompilationMode.Partial(),
|
||||
iterations = 1,
|
||||
setupBlock = {
|
||||
prepareUi()
|
||||
}
|
||||
) {
|
||||
val list = device.findObject(By.scrollable(true)) ?: return@measureRepeated
|
||||
repeat(2) {
|
||||
list.safeScroll(Direction.DOWN, 1.0f)
|
||||
device.waitForIdle()
|
||||
list.safeScroll(Direction.UP, 1.0f)
|
||||
device.waitForIdle()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun searchFlow() {
|
||||
benchmarkRule.measureRepeated(
|
||||
packageName = appPackage,
|
||||
metrics = listOf(FrameTimingMetric()),
|
||||
compilationMode = CompilationMode.Partial(),
|
||||
iterations = 1,
|
||||
setupBlock = {
|
||||
prepareUi()
|
||||
}
|
||||
) {
|
||||
if (!openSearchInput()) return@measureRepeated
|
||||
|
||||
val input = device.wait(Until.findObject(By.clazz("android.widget.EditText")), 1_500)
|
||||
?: return@measureRepeated
|
||||
|
||||
input.text = "rosetta"
|
||||
device.waitForIdle()
|
||||
|
||||
device.findObject(By.scrollable(true))?.safeScroll(Direction.DOWN, 0.7f)
|
||||
device.waitForIdle()
|
||||
|
||||
device.pressBack()
|
||||
device.waitForIdle()
|
||||
}
|
||||
}
|
||||
|
||||
private fun MacrobenchmarkScope.prepareUi() {
|
||||
grantRuntimePermissions()
|
||||
pressHome()
|
||||
launchMainActivity()
|
||||
device.waitForIdle()
|
||||
device.wait(Until.hasObject(By.pkg(appPackage).depth(0)), 3_000)
|
||||
}
|
||||
|
||||
private fun MacrobenchmarkScope.openSearchInput(): Boolean {
|
||||
if (device.hasObject(By.clazz("android.widget.EditText"))) return true
|
||||
|
||||
for (label in searchTriggers) {
|
||||
val node = device.findObject(By.descContains(label)) ?: device.findObject(By.textContains(label))
|
||||
if (node != null) {
|
||||
node.click()
|
||||
device.waitForIdle()
|
||||
if (device.wait(Until.hasObject(By.clazz("android.widget.EditText")), 1_000)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return device.hasObject(By.clazz("android.widget.EditText"))
|
||||
}
|
||||
|
||||
private fun UiObject2.safeScroll(direction: Direction, percent: Float) {
|
||||
runCatching { scroll(direction, percent) }
|
||||
}
|
||||
|
||||
private fun MacrobenchmarkScope.grantRuntimePermissions() {
|
||||
runtimePermissions.forEach { permission ->
|
||||
runCatching {
|
||||
device.executeShellCommand("pm grant $appPackage $permission")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun MacrobenchmarkScope.launchMainActivity() {
|
||||
val intent = Intent(Intent.ACTION_MAIN).apply {
|
||||
addCategory(Intent.CATEGORY_LAUNCHER)
|
||||
setClassName(appPackage, "$appPackage.MainActivity")
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
||||
}
|
||||
startActivityAndWait(intent)
|
||||
}
|
||||
}
|
||||
@@ -19,3 +19,4 @@ rootProject.name = "rosetta-android"
|
||||
|
||||
include(":app")
|
||||
include(":baselineprofile")
|
||||
include(":benchmark")
|
||||
|
||||
Reference in New Issue
Block a user