Skip to content

Migrate benchmarks to UiAutomator 3 API #1867

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -16,57 +16,30 @@

package com.google.samples.apps.nowinandroid

import android.Manifest.permission
import android.os.Build.VERSION.SDK_INT
import android.os.Build.VERSION_CODES.TIRAMISU
import androidx.benchmark.macro.MacrobenchmarkScope
import androidx.test.uiautomator.By
import androidx.test.uiautomator.BySelector
import androidx.test.uiautomator.UiObject2
import androidx.test.uiautomator.Until
import android.view.accessibility.AccessibilityNodeInfo
import androidx.test.uiautomator.UiAutomatorTestScope
import androidx.test.uiautomator.onView
import androidx.test.uiautomator.textAsString
import androidx.test.uiautomator.watcher.PermissionDialog

/**
* Because the app under test is different from the one running the instrumentation test,
* 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
* Wraps starting the default activity, waiting for it to start and then allowing permissions in
* one convenient call.
*/
fun MacrobenchmarkScope.startActivityAndAllowNotifications() {
startActivityAndWait()
allowNotifications()
fun UiAutomatorTestScope.startAppAndAllowPermission() {
startApp(PACKAGE_NAME)
watchFor(PermissionDialog) {
clickAllow()
}
}

/**
* Waits for and returns the `niaTopAppBar`
*/
fun MacrobenchmarkScope.getTopAppBar(): UiObject2 {
device.wait(Until.hasObject(By.res("niaTopAppBar")), 2_000)
return device.findObject(By.res("niaTopAppBar"))
}
fun UiAutomatorTestScope.textVisibleOnTopAppBar(text: String) =
onTopAppBar { textAsString == text && isVisibleToUser }

/**
* Waits for an object on the top app bar, passed in as [selector].
*/
fun MacrobenchmarkScope.waitForObjectOnTopAppBar(selector: BySelector, timeout: Long = 2_000) {
getTopAppBar().wait(Until.hasObject(selector), timeout)
fun UiAutomatorTestScope.onTopAppBar(
timeoutMs: Long = 10000,
pollIntervalMs: Long = 100,
block: AccessibilityNodeInfo.() -> (Boolean),
) {
onView { viewIdResourceName == "niaTopAppBar" }.onView(timeoutMs, pollIntervalMs, block)
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,7 @@

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.UiObject2
import androidx.test.uiautomator.Until
import com.google.samples.apps.nowinandroid.benchmarks.BuildConfig
import java.io.ByteArrayOutputStream

Expand All @@ -32,26 +28,10 @@ val PACKAGE_NAME = buildString {
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.
* If the element is not available in [timeout], throws [AssertionError]
* Default iteration parameter for Now in Android.
*/
fun UiDevice.waitAndFindObject(selector: BySelector, timeout: Long): UiObject2 {
if (!wait(Until.hasObject(selector), timeout)) {
throw AssertionError("Element not found on screen in ${timeout}ms (selector=$selector)")
}

return findObject(selector)
}
const val ITERATIONS = 10

/**
* Helper to dump window hierarchy into a string.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@
package com.google.samples.apps.nowinandroid.baselineprofile

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.bookmarks.goToBookmarksScreen
import com.google.samples.apps.nowinandroid.startActivityAndAllowNotifications
import com.google.samples.apps.nowinandroid.startAppAndAllowPermission
import org.junit.Rule
import org.junit.Test

Expand All @@ -32,9 +33,10 @@ class BookmarksBaselineProfile {
@Test
fun generate() =
baselineProfileRule.collect(PACKAGE_NAME) {
startActivityAndAllowNotifications()

// Navigate to saved screen
goToBookmarksScreen()
uiAutomator {
startAppAndAllowPermission()
// Navigate to saved screen
goToBookmarksScreen()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@
package com.google.samples.apps.nowinandroid.baselineprofile

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.foryou.forYouScrollFeedDownUp
import com.google.samples.apps.nowinandroid.foryou.forYouSelectTopics
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.Test

Expand All @@ -34,11 +35,13 @@ class ForYouBaselineProfile {
@Test
fun generate() =
baselineProfileRule.collect(PACKAGE_NAME) {
startActivityAndAllowNotifications()
uiAutomator {
startAppAndAllowPermission()

// Scroll the feed critical user journey
forYouWaitForContent()
forYouSelectTopics(true)
forYouScrollFeedDownUp()
// Scroll the feed critical user journey
forYouWaitForContent()
forYouSelectTopics(true)
forYouScrollFeedDownUp()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@
package com.google.samples.apps.nowinandroid.baselineprofile

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.interests.goToInterestsScreen
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.Test

Expand All @@ -33,10 +34,11 @@ class InterestsBaselineProfile {
@Test
fun generate() =
baselineProfileRule.collect(PACKAGE_NAME) {
startActivityAndAllowNotifications()

// Navigate to interests screen
goToInterestsScreen()
interestsScrollTopicsDownUp()
uiAutomator {
startAppAndAllowPermission()
// Navigate to interests screen
goToInterestsScreen()
interestsScrollTopicsDownUp()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@

package com.google.samples.apps.nowinandroid.baselineprofile

import androidx.benchmark.macro.MacrobenchmarkScope
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.startActivityAndAllowNotifications
import com.google.samples.apps.nowinandroid.startAppAndAllowPermission
import org.junit.Rule
import org.junit.Test

Expand All @@ -34,6 +34,6 @@ class StartupBaselineProfile {
fun generate() = baselineProfileRule.collect(
PACKAGE_NAME,
includeInStartupProfile = true,
profileBlock = MacrobenchmarkScope::startActivityAndAllowNotifications,
profileBlock = { uiAutomator { startAppAndAllowPermission() } },
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,11 @@

package com.google.samples.apps.nowinandroid.bookmarks

import androidx.benchmark.macro.MacrobenchmarkScope
import androidx.test.uiautomator.By
import com.google.samples.apps.nowinandroid.waitForObjectOnTopAppBar
import androidx.test.uiautomator.UiAutomatorTestScope
import androidx.test.uiautomator.textAsString
import com.google.samples.apps.nowinandroid.textVisibleOnTopAppBar

fun MacrobenchmarkScope.goToBookmarksScreen() {
val savedSelector = By.text("Saved")
val savedButton = device.findObject(savedSelector)
savedButton.click()
device.waitForIdle()
// Wait until saved title are shown on screen
waitForObjectOnTopAppBar(savedSelector)
fun UiAutomatorTestScope.goToBookmarksScreen() {
onView { textAsString == "Saved" }.click()
textVisibleOnTopAppBar("Saved")
}
Loading
Loading