Migrate benchmarks to UiAutomator 3 API

Change-Id: I3ccc4108cb1cc20daff83cf3b48b0c14f125dbb4
bw/uiautomator3
Ben Weiss 5 months ago
parent 689ef92e41
commit dea3da5fa7
No known key found for this signature in database
GPG Key ID: 8424F9C1E763A74C

@ -1,44 +0,0 @@
/*
* Copyright 2022 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
*
* https://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.test.uiautomator
import androidx.test.uiautomator.HasChildrenOp.AT_LEAST
import androidx.test.uiautomator.HasChildrenOp.AT_MOST
import androidx.test.uiautomator.HasChildrenOp.EXACTLY
// These helpers need to be in the androidx.test.uiautomator package,
// because the abstract class has package local method that needs to be implemented.
/**
* Condition will be satisfied if given element has specified count of children
*/
fun untilHasChildren(
childCount: Int = 1,
op: HasChildrenOp = AT_LEAST,
): UiObject2Condition<Boolean> = object : UiObject2Condition<Boolean>() {
override fun apply(element: UiObject2): Boolean = when (op) {
AT_LEAST -> element.childCount >= childCount
EXACTLY -> element.childCount == childCount
AT_MOST -> element.childCount <= childCount
}
}
enum class HasChildrenOp {
AT_LEAST,
EXACTLY,
AT_MOST,
}

@ -16,57 +16,30 @@
package com.google.samples.apps.nowinandroid package com.google.samples.apps.nowinandroid
import android.Manifest.permission import android.view.accessibility.AccessibilityNodeInfo
import android.os.Build.VERSION.SDK_INT import androidx.test.uiautomator.UiAutomatorTestScope
import android.os.Build.VERSION_CODES.TIRAMISU import androidx.test.uiautomator.onView
import androidx.benchmark.macro.MacrobenchmarkScope import androidx.test.uiautomator.textAsString
import androidx.test.uiautomator.By import androidx.test.uiautomator.watcher.PermissionDialog
import androidx.test.uiautomator.BySelector
import androidx.test.uiautomator.UiObject2
import androidx.test.uiautomator.Until
/** /**
* Because the app under test is different from the one running the instrumentation test, * Wraps starting the default activity, waiting for it to start and then allowing permissions in
* the permission has to be granted manually by either:
*
* - tapping the Allow button
* ```kotlin
* val obj = By.text("Allow")
* val dialog = device.wait(Until.findObject(obj), TIMEOUT)
* dialog?.let {
* it.click()
* device.wait(Until.gone(obj), 5_000)
* }
* ```
* - or (preferred) executing the grant command on the target package.
*/
fun MacrobenchmarkScope.allowNotifications() {
if (SDK_INT >= TIRAMISU) {
val command = "pm grant $packageName ${permission.POST_NOTIFICATIONS}"
device.executeShellCommand(command)
}
}
/**
* Wraps starting the default activity, waiting for it to start and then allowing notifications in
* one convenient call. * one convenient call.
*/ */
fun MacrobenchmarkScope.startActivityAndAllowNotifications() { fun UiAutomatorTestScope.startAppAndAllowPermission() {
startActivityAndWait() startApp(PACKAGE_NAME)
allowNotifications() watchFor(PermissionDialog) {
clickAllow()
}
} }
/** fun UiAutomatorTestScope.textVisibleOnTopAppBar(text: String) =
* Waits for and returns the `niaTopAppBar` onTopAppBar { textAsString == text && isVisibleToUser }
*/
fun MacrobenchmarkScope.getTopAppBar(): UiObject2 {
device.wait(Until.hasObject(By.res("niaTopAppBar")), 2_000)
return device.findObject(By.res("niaTopAppBar"))
}
/** fun UiAutomatorTestScope.onTopAppBar(
* Waits for an object on the top app bar, passed in as [selector]. timeoutMs: Long = 10000,
*/ pollIntervalMs: Long = 100,
fun MacrobenchmarkScope.waitForObjectOnTopAppBar(selector: BySelector, timeout: Long = 2_000) { block: AccessibilityNodeInfo.() -> (Boolean),
getTopAppBar().wait(Until.hasObject(selector), timeout) ) {
onView { viewIdResourceName == "niaTopAppBar" }.onView(timeoutMs, pollIntervalMs, block)
} }

@ -16,11 +16,7 @@
package com.google.samples.apps.nowinandroid package com.google.samples.apps.nowinandroid
import androidx.test.uiautomator.BySelector
import androidx.test.uiautomator.Direction
import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.UiObject2
import androidx.test.uiautomator.Until
import com.google.samples.apps.nowinandroid.benchmarks.BuildConfig import com.google.samples.apps.nowinandroid.benchmarks.BuildConfig
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
@ -32,26 +28,10 @@ val PACKAGE_NAME = buildString {
append(BuildConfig.APP_FLAVOR_SUFFIX) append(BuildConfig.APP_FLAVOR_SUFFIX)
} }
fun UiDevice.flingElementDownUp(element: UiObject2) {
// Set some margin from the sides to prevent triggering system navigation
element.setGestureMargin(displayWidth / 5)
element.fling(Direction.DOWN)
waitForIdle()
element.fling(Direction.UP)
}
/** /**
* Waits until an object with [selector] if visible on screen and returns the object. * Default iteration parameter for Now in Android.
* If the element is not available in [timeout], throws [AssertionError]
*/ */
fun UiDevice.waitAndFindObject(selector: BySelector, timeout: Long): UiObject2 { const val ITERATIONS = 10
if (!wait(Until.hasObject(selector), timeout)) {
throw AssertionError("Element not found on screen in ${timeout}ms (selector=$selector)")
}
return findObject(selector)
}
/** /**
* Helper to dump window hierarchy into a string. * Helper to dump window hierarchy into a string.

@ -17,9 +17,10 @@
package com.google.samples.apps.nowinandroid.baselineprofile package com.google.samples.apps.nowinandroid.baselineprofile
import androidx.benchmark.macro.junit4.BaselineProfileRule import androidx.benchmark.macro.junit4.BaselineProfileRule
import androidx.test.uiautomator.uiAutomator
import com.google.samples.apps.nowinandroid.PACKAGE_NAME import com.google.samples.apps.nowinandroid.PACKAGE_NAME
import com.google.samples.apps.nowinandroid.bookmarks.goToBookmarksScreen import com.google.samples.apps.nowinandroid.bookmarks.goToBookmarksScreen
import com.google.samples.apps.nowinandroid.startActivityAndAllowNotifications import com.google.samples.apps.nowinandroid.startAppAndAllowPermission
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
@ -32,9 +33,10 @@ class BookmarksBaselineProfile {
@Test @Test
fun generate() = fun generate() =
baselineProfileRule.collect(PACKAGE_NAME) { baselineProfileRule.collect(PACKAGE_NAME) {
startActivityAndAllowNotifications() uiAutomator {
startAppAndAllowPermission()
// Navigate to saved screen // Navigate to saved screen
goToBookmarksScreen() goToBookmarksScreen()
}
} }
} }

@ -17,11 +17,12 @@
package com.google.samples.apps.nowinandroid.baselineprofile package com.google.samples.apps.nowinandroid.baselineprofile
import androidx.benchmark.macro.junit4.BaselineProfileRule import androidx.benchmark.macro.junit4.BaselineProfileRule
import androidx.test.uiautomator.uiAutomator
import com.google.samples.apps.nowinandroid.PACKAGE_NAME import com.google.samples.apps.nowinandroid.PACKAGE_NAME
import com.google.samples.apps.nowinandroid.foryou.forYouScrollFeedDownUp import com.google.samples.apps.nowinandroid.foryou.forYouScrollFeedDownUp
import com.google.samples.apps.nowinandroid.foryou.forYouSelectTopics import com.google.samples.apps.nowinandroid.foryou.forYouSelectTopics
import com.google.samples.apps.nowinandroid.foryou.forYouWaitForContent import com.google.samples.apps.nowinandroid.foryou.forYouWaitForContent
import com.google.samples.apps.nowinandroid.startActivityAndAllowNotifications import com.google.samples.apps.nowinandroid.startAppAndAllowPermission
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
@ -34,11 +35,13 @@ class ForYouBaselineProfile {
@Test @Test
fun generate() = fun generate() =
baselineProfileRule.collect(PACKAGE_NAME) { baselineProfileRule.collect(PACKAGE_NAME) {
startActivityAndAllowNotifications() uiAutomator {
startAppAndAllowPermission()
// Scroll the feed critical user journey // Scroll the feed critical user journey
forYouWaitForContent() forYouWaitForContent()
forYouSelectTopics(true) forYouSelectTopics(true)
forYouScrollFeedDownUp() forYouScrollFeedDownUp()
}
} }
} }

@ -17,10 +17,11 @@
package com.google.samples.apps.nowinandroid.baselineprofile package com.google.samples.apps.nowinandroid.baselineprofile
import androidx.benchmark.macro.junit4.BaselineProfileRule import androidx.benchmark.macro.junit4.BaselineProfileRule
import androidx.test.uiautomator.uiAutomator
import com.google.samples.apps.nowinandroid.PACKAGE_NAME import com.google.samples.apps.nowinandroid.PACKAGE_NAME
import com.google.samples.apps.nowinandroid.interests.goToInterestsScreen import com.google.samples.apps.nowinandroid.interests.goToInterestsScreen
import com.google.samples.apps.nowinandroid.interests.interestsScrollTopicsDownUp import com.google.samples.apps.nowinandroid.interests.interestsScrollTopicsDownUp
import com.google.samples.apps.nowinandroid.startActivityAndAllowNotifications import com.google.samples.apps.nowinandroid.startAppAndAllowPermission
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
@ -33,10 +34,11 @@ class InterestsBaselineProfile {
@Test @Test
fun generate() = fun generate() =
baselineProfileRule.collect(PACKAGE_NAME) { baselineProfileRule.collect(PACKAGE_NAME) {
startActivityAndAllowNotifications() uiAutomator {
startAppAndAllowPermission()
// Navigate to interests screen // Navigate to interests screen
goToInterestsScreen() goToInterestsScreen()
interestsScrollTopicsDownUp() interestsScrollTopicsDownUp()
}
} }
} }

@ -16,10 +16,10 @@
package com.google.samples.apps.nowinandroid.baselineprofile package com.google.samples.apps.nowinandroid.baselineprofile
import androidx.benchmark.macro.MacrobenchmarkScope
import androidx.benchmark.macro.junit4.BaselineProfileRule import androidx.benchmark.macro.junit4.BaselineProfileRule
import androidx.test.uiautomator.uiAutomator
import com.google.samples.apps.nowinandroid.PACKAGE_NAME import com.google.samples.apps.nowinandroid.PACKAGE_NAME
import com.google.samples.apps.nowinandroid.startActivityAndAllowNotifications import com.google.samples.apps.nowinandroid.startAppAndAllowPermission
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
@ -34,6 +34,6 @@ class StartupBaselineProfile {
fun generate() = baselineProfileRule.collect( fun generate() = baselineProfileRule.collect(
PACKAGE_NAME, PACKAGE_NAME,
includeInStartupProfile = true, includeInStartupProfile = true,
profileBlock = MacrobenchmarkScope::startActivityAndAllowNotifications, profileBlock = { uiAutomator { startAppAndAllowPermission() } },
) )
} }

@ -16,15 +16,11 @@
package com.google.samples.apps.nowinandroid.bookmarks package com.google.samples.apps.nowinandroid.bookmarks
import androidx.benchmark.macro.MacrobenchmarkScope import androidx.test.uiautomator.UiAutomatorTestScope
import androidx.test.uiautomator.By import androidx.test.uiautomator.textAsString
import com.google.samples.apps.nowinandroid.waitForObjectOnTopAppBar import com.google.samples.apps.nowinandroid.textVisibleOnTopAppBar
fun MacrobenchmarkScope.goToBookmarksScreen() { fun UiAutomatorTestScope.goToBookmarksScreen() {
val savedSelector = By.text("Saved") onView { textAsString == "Saved" }.click()
val savedButton = device.findObject(savedSelector) textVisibleOnTopAppBar("Saved")
savedButton.click()
device.waitForIdle()
// Wait until saved title are shown on screen
waitForObjectOnTopAppBar(savedSelector)
} }

@ -16,93 +16,57 @@
package com.google.samples.apps.nowinandroid.foryou package com.google.samples.apps.nowinandroid.foryou
import androidx.benchmark.macro.MacrobenchmarkScope import androidx.test.uiautomator.Direction
import androidx.test.uiautomator.By import androidx.test.uiautomator.UiAutomatorTestScope
import androidx.test.uiautomator.Until import androidx.test.uiautomator.textAsString
import androidx.test.uiautomator.untilHasChildren import com.google.samples.apps.nowinandroid.textVisibleOnTopAppBar
import com.google.samples.apps.nowinandroid.flingElementDownUp
import com.google.samples.apps.nowinandroid.waitAndFindObject
import com.google.samples.apps.nowinandroid.waitForObjectOnTopAppBar
import org.junit.Assert.fail import org.junit.Assert.fail
fun MacrobenchmarkScope.forYouWaitForContent() { fun UiAutomatorTestScope.forYouWaitForContent() {
// Wait until content is loaded by checking if topics are loaded onViewOrNull(timeoutMs = 500) { viewIdResourceName == "loadingWheel" && !isVisibleToUser }
device.wait(Until.gone(By.res("loadingWheel")), 5_000)
// Sometimes, the loading wheel is gone, but the content is not loaded yet // Sometimes, the loading wheel is gone, but the content is not loaded yet
// So we'll wait here for topics to be sure // So we'll wait here for topics to be sure
val obj = device.waitAndFindObject(By.res("forYou:topicSelection"), 10_000)
// Timeout here is quite big, because sometimes data loading takes a long time! // Timeout here is quite big, because sometimes data loading takes a long time!
obj.wait(untilHasChildren(), 60_000) onView(timeoutMs = 30_000) {
viewIdResourceName == "forYou:topicSelection" && childCount > 0 && isVisibleToUser
}
} }
/** /**
* Selects some topics, which will show the feed content for them. * Selects some topics, which will show the feed content for them.
* [recheckTopicsIfChecked] Topics may be already checked from the previous iteration. * [recheckTopicsIfChecked] Topics may be already checked from the previous iteration.
*/ */
fun MacrobenchmarkScope.forYouSelectTopics(recheckTopicsIfChecked: Boolean = false) { fun UiAutomatorTestScope.forYouSelectTopics(recheckTopicsIfChecked: Boolean = false) {
val topics = device.findObject(By.res("forYou:topicSelection")) onView { viewIdResourceName == "forYou:topicSelection" }.run {
if (children.isEmpty()) {
// Set gesture margin from sides not to trigger system gesture navigation
val horizontalMargin = 10 * topics.visibleBounds.width() / 100
topics.setGestureMargins(horizontalMargin, 0, horizontalMargin, 0)
// Select some topics to show some feed content
var index = 0
var visited = 0
while (visited < 3) {
if (topics.childCount == 0) {
fail("No topics found, can't generate profile for ForYou page.") fail("No topics found, can't generate profile for ForYou page.")
} }
// Selecting some topics, which will populate items in the feed.
val topic = topics.children[index % topics.childCount]
// Find the checkable element to figure out whether it's checked or not
val topicCheckIcon = topic.findObject(By.checkable(true))
// Topic icon may not be visible if it's out of the screen boundaries
// If that's the case, let's try another index
if (topicCheckIcon == null) {
index++
continue
}
when {
// Topic wasn't checked, so just do that
!topicCheckIcon.isChecked -> {
topic.click()
device.waitForIdle()
}
// Topic was checked already and we want to recheck it, so just do it twice val horizontalMargin = visibleBounds.width() / 10
recheckTopicsIfChecked -> { setGestureMargins(horizontalMargin, 0, horizontalMargin, 0)
repeat(2) {
topic.click()
device.waitForIdle()
}
}
else -> { // Filter for any checkable and unchecked children unless recheck is enabled
// Topic is checked, but we don't recheck it children.filter { child ->
child.isCheckable and (recheckTopicsIfChecked or !child.isChecked)
}.forEach { child ->
// recheck check to uncheck before recheck
if (child.isChecked) {
child.click()
} }
child.click()
} }
index++
visited++
} }
} }
fun MacrobenchmarkScope.forYouScrollFeedDownUp() { fun UiAutomatorTestScope.forYouScrollFeedDownUp() {
val feedList = device.findObject(By.res("forYou:feed")) onView { viewIdResourceName == "forYou:feed" }.run {
device.flingElementDownUp(feedList) fling(Direction.DOWN)
} fling(Direction.UP)
fun MacrobenchmarkScope.setAppTheme(isDark: Boolean) {
when (isDark) {
true -> device.findObject(By.text("Dark")).click()
false -> device.findObject(By.text("Light")).click()
} }
device.waitForIdle() }
device.findObject(By.text("OK")).click()
// Wait until the top app bar is visible on screen fun UiAutomatorTestScope.setAppTheme(isDark: Boolean) {
waitForObjectOnTopAppBar(By.text("Now in Android")) onView { textAsString == if (isDark) "Dark" else "Light" }.click()
onView { textAsString == "OK" }.click()
textVisibleOnTopAppBar("Now in Android")
} }

@ -21,8 +21,10 @@ import androidx.benchmark.macro.FrameTimingMetric
import androidx.benchmark.macro.StartupMode import androidx.benchmark.macro.StartupMode
import androidx.benchmark.macro.junit4.MacrobenchmarkRule import androidx.benchmark.macro.junit4.MacrobenchmarkRule
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import androidx.test.uiautomator.uiAutomator
import com.google.samples.apps.nowinandroid.ITERATIONS
import com.google.samples.apps.nowinandroid.PACKAGE_NAME import com.google.samples.apps.nowinandroid.PACKAGE_NAME
import com.google.samples.apps.nowinandroid.startActivityAndAllowNotifications import com.google.samples.apps.nowinandroid.startAppAndAllowPermission
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
@ -45,16 +47,20 @@ class ScrollForYouFeedBenchmark {
packageName = PACKAGE_NAME, packageName = PACKAGE_NAME,
metrics = listOf(FrameTimingMetric()), metrics = listOf(FrameTimingMetric()),
compilationMode = compilationMode, compilationMode = compilationMode,
iterations = 10, iterations = ITERATIONS,
startupMode = StartupMode.WARM, startupMode = StartupMode.WARM,
setupBlock = { setupBlock = {
// Start the app uiAutomator {
pressHome() // Start the app
startActivityAndAllowNotifications() pressHome()
startAppAndAllowPermission()
}
}, },
) { ) {
forYouWaitForContent() uiAutomator {
forYouSelectTopics() forYouWaitForContent()
forYouScrollFeedDownUp() forYouSelectTopics()
forYouScrollFeedDownUp()
}
} }
} }

@ -16,35 +16,27 @@
package com.google.samples.apps.nowinandroid.interests package com.google.samples.apps.nowinandroid.interests
import androidx.benchmark.macro.MacrobenchmarkScope import androidx.test.uiautomator.Direction
import androidx.test.uiautomator.By import androidx.test.uiautomator.UiAutomatorTestScope
import androidx.test.uiautomator.Until import androidx.test.uiautomator.textAsString
import com.google.samples.apps.nowinandroid.flingElementDownUp import com.google.samples.apps.nowinandroid.textVisibleOnTopAppBar
import com.google.samples.apps.nowinandroid.waitForObjectOnTopAppBar
fun MacrobenchmarkScope.goToInterestsScreen() {
device.findObject(By.text("Interests")).click()
device.waitForIdle()
// Wait until interests are shown on screen
waitForObjectOnTopAppBar(By.text("Interests"))
fun UiAutomatorTestScope.goToInterestsScreen() {
onView { textAsString == "Interests" }.click()
textVisibleOnTopAppBar("Interests")
// Wait until content is loaded by checking if interests are loaded // Wait until content is loaded by checking if interests are loaded
device.wait(Until.gone(By.res("loadingWheel")), 5_000) assert(onViewOrNull { viewIdResourceName == "loadingWheel" && !isVisibleToUser } == null)
}
fun MacrobenchmarkScope.interestsScrollTopicsDownUp() {
device.wait(Until.hasObject(By.res("interests:topics")), 5_000)
val topicsList = device.findObject(By.res("interests:topics"))
device.flingElementDownUp(topicsList)
} }
fun MacrobenchmarkScope.interestsWaitForTopics() { fun UiAutomatorTestScope.interestsScrollTopicsDownUp() {
device.wait(Until.hasObject(By.text("Accessibility")), 30_000) onView { viewIdResourceName == "interests:topics" }.run {
// Set some margin from the sides to prevent triggering system navigation
setGestureMargin(uiDevice.displayWidth / 5)
fling(Direction.DOWN)
fling(Direction.UP)
}
} }
fun MacrobenchmarkScope.interestsToggleBookmarked() { fun UiAutomatorTestScope.interestsWaitForTopics() {
val topicsList = device.findObject(By.res("interests:topics")) onView(30_000) { textAsString == "Accessibility" }
val checkable = topicsList.findObject(By.checkable(true))
checkable.click()
device.waitForIdle()
} }

@ -21,9 +21,11 @@ import androidx.benchmark.macro.FrameTimingMetric
import androidx.benchmark.macro.StartupMode import androidx.benchmark.macro.StartupMode
import androidx.benchmark.macro.junit4.MacrobenchmarkRule import androidx.benchmark.macro.junit4.MacrobenchmarkRule
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.uiautomator.By import androidx.test.uiautomator.textAsString
import androidx.test.uiautomator.uiAutomator
import com.google.samples.apps.nowinandroid.ITERATIONS
import com.google.samples.apps.nowinandroid.PACKAGE_NAME import com.google.samples.apps.nowinandroid.PACKAGE_NAME
import com.google.samples.apps.nowinandroid.startActivityAndAllowNotifications import com.google.samples.apps.nowinandroid.startAppAndAllowPermission
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
@ -42,20 +44,23 @@ class ScrollTopicListBenchmark {
packageName = PACKAGE_NAME, packageName = PACKAGE_NAME,
metrics = listOf(FrameTimingMetric()), metrics = listOf(FrameTimingMetric()),
compilationMode = compilationMode, compilationMode = compilationMode,
iterations = 10, iterations = ITERATIONS,
startupMode = StartupMode.WARM, startupMode = StartupMode.WARM,
setupBlock = { setupBlock = {
// Start the app uiAutomator {
pressHome() // Start the app
startActivityAndAllowNotifications() pressHome()
// Navigate to interests screen startAppAndAllowPermission()
device.findObject(By.text("Interests")).click() // Navigate to interests screen
device.waitForIdle() onView { textAsString == "Interests" }.click()
}
}, },
) { ) {
interestsWaitForTopics() uiAutomator {
repeat(3) { interestsWaitForTopics()
interestsScrollTopicsDownUp() repeat(3) {
interestsScrollTopicsDownUp()
}
} }
} }
} }

@ -27,13 +27,14 @@ import androidx.benchmark.macro.PowerMetric
import androidx.benchmark.macro.StartupMode import androidx.benchmark.macro.StartupMode
import androidx.benchmark.macro.junit4.MacrobenchmarkRule import androidx.benchmark.macro.junit4.MacrobenchmarkRule
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.uiautomator.By import androidx.test.uiautomator.uiAutomator
import com.google.samples.apps.nowinandroid.ITERATIONS
import com.google.samples.apps.nowinandroid.PACKAGE_NAME import com.google.samples.apps.nowinandroid.PACKAGE_NAME
import com.google.samples.apps.nowinandroid.allowNotifications
import com.google.samples.apps.nowinandroid.foryou.forYouScrollFeedDownUp import com.google.samples.apps.nowinandroid.foryou.forYouScrollFeedDownUp
import com.google.samples.apps.nowinandroid.foryou.forYouSelectTopics import com.google.samples.apps.nowinandroid.foryou.forYouSelectTopics
import com.google.samples.apps.nowinandroid.foryou.forYouWaitForContent import com.google.samples.apps.nowinandroid.foryou.forYouWaitForContent
import com.google.samples.apps.nowinandroid.foryou.setAppTheme import com.google.samples.apps.nowinandroid.foryou.setAppTheme
import com.google.samples.apps.nowinandroid.startAppAndAllowPermission
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
@ -61,23 +62,23 @@ class ScrollTopicListPowerMetricsBenchmark {
packageName = PACKAGE_NAME, packageName = PACKAGE_NAME,
metrics = listOf(FrameTimingMetric(), PowerMetric(PowerMetric.Energy(categories))), metrics = listOf(FrameTimingMetric(), PowerMetric(PowerMetric.Energy(categories))),
compilationMode = compilationMode, compilationMode = compilationMode,
iterations = 2, iterations = ITERATIONS,
startupMode = StartupMode.WARM, startupMode = StartupMode.WARM,
setupBlock = { setupBlock = {
// Start the app uiAutomator {
pressHome() pressHome()
startActivityAndWait() startAppAndAllowPermission()
allowNotifications() onView { contentDescription == "Settings" }.click()
// Navigate to Settings setAppTheme(isDark)
device.findObject(By.desc("Settings")).click() }
device.waitForIdle()
setAppTheme(isDark)
}, },
) { ) {
forYouWaitForContent() uiAutomator {
forYouSelectTopics() forYouWaitForContent()
repeat(3) { forYouSelectTopics()
forYouScrollFeedDownUp() repeat(3) {
forYouScrollFeedDownUp()
}
} }
} }
} }

@ -21,9 +21,12 @@ import androidx.benchmark.macro.FrameTimingMetric
import androidx.benchmark.macro.StartupMode import androidx.benchmark.macro.StartupMode
import androidx.benchmark.macro.junit4.MacrobenchmarkRule import androidx.benchmark.macro.junit4.MacrobenchmarkRule
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.uiautomator.By import androidx.test.uiautomator.onView
import androidx.test.uiautomator.textAsString
import androidx.test.uiautomator.uiAutomator
import com.google.samples.apps.nowinandroid.ITERATIONS
import com.google.samples.apps.nowinandroid.PACKAGE_NAME import com.google.samples.apps.nowinandroid.PACKAGE_NAME
import com.google.samples.apps.nowinandroid.startActivityAndAllowNotifications import com.google.samples.apps.nowinandroid.startAppAndAllowPermission
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
@ -42,20 +45,25 @@ class TopicsScreenRecompositionBenchmark {
packageName = PACKAGE_NAME, packageName = PACKAGE_NAME,
metrics = listOf(FrameTimingMetric()), metrics = listOf(FrameTimingMetric()),
compilationMode = compilationMode, compilationMode = compilationMode,
iterations = 10, iterations = ITERATIONS,
startupMode = StartupMode.WARM, startupMode = StartupMode.WARM,
setupBlock = { setupBlock = {
// Start the app uiAutomator {
pressHome() // Start the app
startActivityAndAllowNotifications() pressHome()
// Navigate to interests screen startAppAndAllowPermission()
device.findObject(By.text("Interests")).click() // Navigate to interests screen
device.waitForIdle() onView { textAsString == "Interests" }.click()
}
}, },
) { ) {
interestsWaitForTopics() uiAutomator {
repeat(3) { interestsWaitForTopics()
interestsToggleBookmarked() repeat(3) {
// Toggle bookmarked
onView { viewIdResourceName == "interests:topics" }.onView { isCheckable }
.click()
}
} }
} }
} }

@ -22,11 +22,12 @@ import androidx.benchmark.macro.CompilationMode
import androidx.benchmark.macro.StartupMode.COLD import androidx.benchmark.macro.StartupMode.COLD
import androidx.benchmark.macro.junit4.MacrobenchmarkRule import androidx.benchmark.macro.junit4.MacrobenchmarkRule
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import androidx.test.uiautomator.uiAutomator
import com.google.samples.apps.nowinandroid.BaselineProfileMetrics import com.google.samples.apps.nowinandroid.BaselineProfileMetrics
import com.google.samples.apps.nowinandroid.ITERATIONS
import com.google.samples.apps.nowinandroid.PACKAGE_NAME import com.google.samples.apps.nowinandroid.PACKAGE_NAME
import com.google.samples.apps.nowinandroid.allowNotifications
import com.google.samples.apps.nowinandroid.foryou.forYouWaitForContent import com.google.samples.apps.nowinandroid.foryou.forYouWaitForContent
import com.google.samples.apps.nowinandroid.startActivityAndAllowNotifications import com.google.samples.apps.nowinandroid.startAppAndAllowPermission
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
@ -61,15 +62,13 @@ class StartupBenchmark {
metrics = BaselineProfileMetrics.allMetrics, metrics = BaselineProfileMetrics.allMetrics,
compilationMode = compilationMode, compilationMode = compilationMode,
// More iterations result in higher statistical significance. // More iterations result in higher statistical significance.
iterations = 20, iterations = ITERATIONS,
startupMode = COLD, startupMode = COLD,
setupBlock = {
pressHome()
allowNotifications()
},
) { ) {
startActivityAndAllowNotifications() uiAutomator {
// Waits until the content is ready to capture Time To Full Display startAppAndAllowPermission()
forYouWaitForContent() // Waits until the content is ready to capture Time To Full Display
forYouWaitForContent()
}
} }
} }

@ -18,7 +18,7 @@ androidxEspresso = "3.6.1"
androidxHiltNavigationCompose = "1.2.0" androidxHiltNavigationCompose = "1.2.0"
androidxLifecycle = "2.8.7" androidxLifecycle = "2.8.7"
androidxLintGradle = "1.0.0-alpha03" androidxLintGradle = "1.0.0-alpha03"
androidxMacroBenchmark = "1.3.4" androidxMacroBenchmark = "1.4.0-alpha11"
androidxMetrics = "1.0.0-beta01" androidxMetrics = "1.0.0-beta01"
androidxNavigation = "2.8.5" androidxNavigation = "2.8.5"
androidxProfileinstaller = "1.4.1" androidxProfileinstaller = "1.4.1"
@ -27,7 +27,7 @@ androidxTestExt = "1.2.1"
androidxTestRules = "1.6.1" androidxTestRules = "1.6.1"
androidxTestRunner = "1.6.2" androidxTestRunner = "1.6.2"
androidxTracing = "1.3.0-alpha02" androidxTracing = "1.3.0-alpha02"
androidxUiAutomator = "2.3.0" androidxUiAutomator = "2.4.0-SNAPSHOT"
androidxWindowManager = "1.3.0" androidxWindowManager = "1.3.0"
androidxWork = "2.10.0" androidxWork = "2.10.0"
coil = "2.7.0" coil = "2.7.0"

@ -26,6 +26,9 @@ pluginManagement {
} }
mavenCentral() mavenCentral()
gradlePluginPortal() gradlePluginPortal()
maven { // androidx snapshots
url = uri("https://androidx.dev/snapshots/builds/13364063/artifacts/repository")
}
} }
} }
@ -40,6 +43,9 @@ dependencyResolutionManagement {
} }
} }
mavenCentral() mavenCentral()
maven { // androidx snapshots
url = uri("https://androidx.dev/snapshots/builds/13364063/artifacts/repository")
}
} }
} }
rootProject.name = "nowinandroid" rootProject.name = "nowinandroid"

Loading…
Cancel
Save