[go: nahoru, domu]

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 ->