Add MacrobenchmarkRule as primary API surface
Bug: 171325609
Test: ./gradlew benchmark:benchmark-simple-macro-benchmark:cC
Rule provides test class/method name as benchmark name.
Config object added to enable easier layering without duplicating many
configuration options.
Change-Id: I757bdf935cadedcc009e62a6ccdecde7d56f6a04
diff --git a/benchmark/benchmark-macro-runtime/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt b/benchmark/benchmark-macro-runtime/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
index 2b1ac70..2c0bc9c 100644
--- a/benchmark/benchmark-macro-runtime/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
+++ b/benchmark/benchmark-macro-runtime/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
@@ -26,7 +26,7 @@
* Provides access to common operations in app automation, such as killing the app,
* or navigating home.
*/
-class MacrobenchmarkScope(
+public class MacrobenchmarkScope(
private val packageName: String
) {
private val instrumentation = InstrumentationRegistry.getInstrumentation()
@@ -53,6 +53,14 @@
}
}
+data class MacrobenchmarkConfig(
+ val packageName: String,
+ val metrics: List<Metric>,
+ val compilationMode: CompilationMode = CompilationMode.SpeedProfile(),
+ val killProcessEachIteration: Boolean = false,
+ val iterations: Int
+)
+
/**
* Primary macrobenchmark test entrypoint.
*
@@ -60,44 +68,40 @@
*/
fun macrobenchmark(
benchmarkName: String,
- packageName: String,
- metrics: List<Metric>,
- compilationMode: CompilationMode = CompilationMode.SpeedProfile(),
- killProcessEachIteration: Boolean = false,
- iterations: Int,
+ config: MacrobenchmarkConfig,
block: MacrobenchmarkScope.() -> Unit
) = withPermissiveSeLinuxPolicy {
- val scope = MacrobenchmarkScope(packageName)
+ val scope = MacrobenchmarkScope(config.packageName)
// always kill the process at beginning of test
scope.killProcess()
- compilationMode.compile(packageName) {
+ config.compilationMode.compile(config.packageName) {
block(scope)
}
// Perfetto collector is separate from metrics, so we can control file
// output, and give it different (test-wide) lifecycle
- val perfettoCollector = PerfettoCollector("$benchmarkName.trace")
+ val perfettoCollector = PerfettoCaptureWrapper()
try {
perfettoCollector.start()
- metrics.forEach {
+ config.metrics.forEach {
it.start()
}
- repeat(iterations) {
- if (killProcessEachIteration) {
+ repeat(config.iterations) {
+ if (config.killProcessEachIteration) {
scope.killProcess()
}
block(scope)
}
- metrics.forEach {
+ config.metrics.forEach {
it.stop()
}
- metrics.map {
+ config.metrics.map {
it.collector
}.report()
} finally {
- perfettoCollector.stop()
+ perfettoCollector.stop("$benchmarkName.trace")
scope.killProcess()
}
}
diff --git a/benchmark/benchmark-macro-runtime/src/main/java/androidx/benchmark/macro/MacrobenchmarkRule.kt b/benchmark/benchmark-macro-runtime/src/main/java/androidx/benchmark/macro/MacrobenchmarkRule.kt
new file mode 100644
index 0000000..babaad7
--- /dev/null
+++ b/benchmark/benchmark-macro-runtime/src/main/java/androidx/benchmark/macro/MacrobenchmarkRule.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed 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 androidx.benchmark.macro
+
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+/**
+ * JUnit rule for benchmarking large app operations like startup.
+ */
+class MacrobenchmarkRule : TestRule {
+ lateinit var benchmarkName: String
+
+ fun measureRepeated(
+ config: MacrobenchmarkConfig,
+ block: MacrobenchmarkScope.() -> Unit
+ ) {
+ macrobenchmark(benchmarkName, config, block)
+ }
+
+ override fun apply(base: Statement, description: Description) = object : Statement() {
+ override fun evaluate() {
+ benchmarkName = description.toUniqueName()
+ base.evaluate()
+ }
+ }
+
+ internal fun Description.toUniqueName() = testClass.simpleName + "_" + methodName
+}
diff --git a/benchmark/benchmark-macro-runtime/src/main/java/androidx/benchmark/macro/PerfettoCollector.kt b/benchmark/benchmark-macro-runtime/src/main/java/androidx/benchmark/macro/PerfettoCaptureWrapper.kt
similarity index 82%
rename from benchmark/benchmark-macro-runtime/src/main/java/androidx/benchmark/macro/PerfettoCollector.kt
rename to benchmark/benchmark-macro-runtime/src/main/java/androidx/benchmark/macro/PerfettoCaptureWrapper.kt
index 2b0cbef..88014df 100644
--- a/benchmark/benchmark-macro-runtime/src/main/java/androidx/benchmark/macro/PerfettoCollector.kt
+++ b/benchmark/benchmark-macro-runtime/src/main/java/androidx/benchmark/macro/PerfettoCaptureWrapper.kt
@@ -23,9 +23,9 @@
import androidx.benchmark.perfetto.reportAdditionalFileToCopy
/**
- * Helps capture Perfetto traces.
+ * Wrapper for PerfettoCapture, which does nothing on API < Q
*/
-class PerfettoCollector(private val traceName: String) : Collector<Unit> {
+class PerfettoCaptureWrapper {
private var capture: PerfettoCapture? = null
init {
@@ -34,15 +34,15 @@
}
}
- override fun start(): Boolean {
+ fun start(): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- Log.d(TAG, "Recording perfetto trace $traceName")
+ Log.d(TAG, "Recording perfetto trace")
capture?.start()
}
return true
}
- override fun stop(): Boolean {
+ fun stop(traceName: String): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val destination = destinationPath(traceName).absolutePath
capture?.stop(destination)
@@ -51,10 +51,6 @@
return true
}
- override fun metrics(): Map<String, Unit> {
- return emptyMap()
- }
-
companion object {
private const val TAG = "PerfettoCollector"
}
diff --git a/benchmark/benchmark-simple-macro-benchmark/src/androidTest/java/androidx/benchmark/macro/sample/MacroBenchmarkTest.kt b/benchmark/benchmark-simple-macro-benchmark/src/androidTest/java/androidx/benchmark/macro/sample/MacroBenchmarkTest.kt
index 4a2f637..a7359f6 100644
--- a/benchmark/benchmark-simple-macro-benchmark/src/androidTest/java/androidx/benchmark/macro/sample/MacroBenchmarkTest.kt
+++ b/benchmark/benchmark-simple-macro-benchmark/src/androidTest/java/androidx/benchmark/macro/sample/MacroBenchmarkTest.kt
@@ -19,27 +19,32 @@
import android.content.Intent
import androidx.benchmark.macro.CompilationMode
import androidx.benchmark.macro.CpuUsageMetric
+import androidx.benchmark.macro.MacrobenchmarkConfig
+import androidx.benchmark.macro.MacrobenchmarkRule
import androidx.benchmark.macro.StartupTimingMetric
-import androidx.benchmark.macro.macrobenchmark
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import org.junit.Ignore
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@LargeTest
@RunWith(AndroidJUnit4::class)
class MacroBenchmarkTest {
+ @get:Rule
+ val benchmarkRule = MacrobenchmarkRule()
+
@Test
- @LargeTest
@Ignore("Not running the test in CI")
- fun basicTest() = macrobenchmark(
- "benchmarkUniqueName",
- packageName = "androidx.benchmark.integration.macro.target",
- listOf(StartupTimingMetric(), CpuUsageMetric()),
- CompilationMode.Speed,
- killProcessEachIteration = true,
- iterations = 4
+ fun basicTest() = benchmarkRule.measureRepeated(
+ MacrobenchmarkConfig(
+ packageName = "androidx.benchmark.integration.macro.target",
+ listOf(StartupTimingMetric(), CpuUsageMetric()),
+ CompilationMode.Speed,
+ killProcessEachIteration = true,
+ iterations = 4
+ )
) {
pressHome()
launchPackageAndWait { launchIntent ->