0
0

macrobenchmarkでsetupBlockが終了するとアプリが終わってしまいテストが実行できない(失敗する)

Posted at

Macrobenchmarkとは

アプリの起動時間とか、フレーム時間とかを簡単に計測できるテストライブラリです。
https://developer.android.com/topic/performance/benchmarking/macrobenchmark-overview?hl=ja

UiAutomatorというのを使ってテストを書き、その上でいろいろ計測してくれます。
なのですごく細かい操作をして計測したい場合などは、UiAutomatorでのテストの書き方の知識も必要になってきます。
まあ、起動時間計測くらいなら比較的容易に出来るようになっています。

簡単なサンプルはこちらから。
https://developer.android.com/codelabs/android-macrobenchmark-inspect
(情報は古めなので注意)

実施環境など

ライブラリ バージョン
uiautomator "2.3.0"
benchmark-macro-junit4 "1.2.0-beta01"

遭遇した問題

リストを最下部までスクロールして描画が完了するまでのフレーム時間計測を行いたいと思い、以下のようにテストを書きました。
ほぼcodelabのサンプルのままです。

@RunWith(AndroidJUnit4::class)
class ScrollBenchmark {
    @get:Rule
    val benchmarkRule = MacrobenchmarkRule()

    @Test
    fun scroll() = benchmarkRule.measureRepeated(
        packageName = "xx.xxx.xxxx",
        metrics = listOf(FrameTimingMetric()),
        iterations = 5,
        startupMode = StartupMode.COLD,
        setupBlock = {
            pressHome()
            startActivityAndWait()
            // リストの行が表示されるまで待つ
            if (!device.wait(Until.hasObject(By.res("list_items")), 25_000)) {
                fail("Could not find resource in time")
            }
        },
    ) {
        val contentList = device.findObject(By.res("list"))

        // Set gesture margin to avoid triggering system gesture navigation
        contentList.setGestureMargin(device.displayWidth / 5)

        // Scroll down the list to bottom
        while (contentList.fling(Direction.DOWN)) {
            // Wait for the scroll to finish
            device.waitForIdle()
        }

        // Wait for the scroll to finish
        device.waitForIdle()
    }

ところが、contentListがnullになってしまい、テストが落ちます。
端末での動作を見る限り、どうもsetupBlockを実行後にすぐにアプリが終了されてしまっています。
measureBlockの時点でアプリが終了しているので、当然listが見つからないわけです。

解決策

benchmarkRule.measureRepeatedのパラメータ、startupMode=StartupMode.COLDがいけないようです。
startupMode=StartupMode.WARMにするとよいようです。

    @Test
    fun scroll() = benchmarkRule.measureRepeated(
        packageName = "xx.xxx.xxxx",
        metrics = listOf(FrameTimingMetric()),
        iterations = 5,
        startupMode = StartupMode.WARM, // ←ここ
        setupBlock = {

あるいは、StartupMode.HOTにして、毎回setupBlock内でkillProcess()するのもよいようです。
StartupMode.WARMだと、Activityは再作成されますがアプリプロセスは同じなので、設定値とかグローバル変数上のものなどは引き継がれて残ってしまうのでその辺が注意でしょうか。

参考ページ

あまり情報が無くて困っていましたが、最終的にはやっぱりStackoverflowにありました。

スレッドで提示されているissueを見ると、担当者は「期待された動作だ」と説明していますが、setup後にアプリを動かして計測したいのが普通じゃないのと思いますけどね。なんでsetup後にアプリのプロセスをkillするのが期待値なんだろう・・・?
現時点ではStatusはIn Progress (Accepted)になっているので、今後修正される可能性はありそうですが。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0