summaryrefslogtreecommitdiff
path: root/common/testutils/host/com/android/testutils/ConnectivityTestTargetPreparer.kt
blob: 3fc74aa1a468010910468cab42679ad623f68e42 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/*
 * Copyright (C) 2021 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 com.android.testutils

import com.android.ddmlib.testrunner.TestResult
import com.android.tradefed.config.Option
import com.android.tradefed.invoker.TestInformation
import com.android.tradefed.result.CollectingTestListener
import com.android.tradefed.result.ddmlib.DefaultRemoteAndroidTestRunner
import com.android.tradefed.targetprep.BaseTargetPreparer
import com.android.tradefed.targetprep.TargetSetupError
import com.android.tradefed.targetprep.suite.SuiteApkInstaller

private const val CONNECTIVITY_CHECKER_APK = "ConnectivityTestPreparer.apk"
private const val CONNECTIVITY_PKG_NAME = "com.android.testutils.connectivitypreparer"
private const val CONNECTIVITY_CHECK_CLASS = "$CONNECTIVITY_PKG_NAME.ConnectivityCheckTest"
// As per the <instrumentation> defined in the checker manifest
private const val CONNECTIVITY_CHECK_RUNNER_NAME = "androidx.test.runner.AndroidJUnitRunner"
private const val IGNORE_CONN_CHECK_OPTION = "ignore-connectivity-check"

/**
 * A target preparer that sets up and verifies a device for connectivity tests.
 *
 * For quick and dirty local testing, the connectivity check can be disabled by running tests with
 * "atest -- \
 * --test-arg com.android.testutils.ConnectivityTestTargetPreparer:ignore-connectivity-check:true".
 */
open class ConnectivityTestTargetPreparer : BaseTargetPreparer() {
    private val installer = SuiteApkInstaller()

    @Option(name = IGNORE_CONN_CHECK_OPTION,
            description = "Disables the check for mobile data and wifi")
    private var ignoreConnectivityCheck = false

    override fun setUp(testInformation: TestInformation) {
        if (isDisabled) return
        disableGmsUpdate(testInformation)
        runPreparerApk(testInformation)
    }

    private fun runPreparerApk(testInformation: TestInformation) {
        installer.setCleanApk(true)
        installer.addTestFileName(CONNECTIVITY_CHECKER_APK)
        installer.setShouldGrantPermission(true)
        installer.setUp(testInformation)

        val runner = DefaultRemoteAndroidTestRunner(
                CONNECTIVITY_PKG_NAME,
                CONNECTIVITY_CHECK_RUNNER_NAME,
                testInformation.device.iDevice)
        runner.runOptions = "--no-hidden-api-checks"

        val receiver = CollectingTestListener()
        if (!testInformation.device.runInstrumentationTests(runner, receiver)) {
            throw TargetSetupError("Device state check failed to complete",
                    testInformation.device.deviceDescriptor)
        }

        val runResult = receiver.currentRunResults
        if (runResult.isRunFailure) {
            throw TargetSetupError("Failed to check device state before the test: " +
                    runResult.runFailureMessage, testInformation.device.deviceDescriptor)
        }

        val ignoredTestClasses = mutableSetOf<String>()
        if (ignoreConnectivityCheck) {
            ignoredTestClasses.add(CONNECTIVITY_CHECK_CLASS)
        }

        val errorMsg = runResult.testResults.mapNotNull { (testDescription, testResult) ->
            if (TestResult.TestStatus.FAILURE != testResult.status ||
                    ignoredTestClasses.contains(testDescription.className)) {
                null
            } else {
                "$testDescription: ${testResult.stackTrace}"
            }
        }.joinToString("\n")
        if (errorMsg.isBlank()) return

        throw TargetSetupError("Device setup checks failed. Check the test bench: \n$errorMsg",
                testInformation.device.deviceDescriptor)
    }

    private fun disableGmsUpdate(testInformation: TestInformation) {
        // This will be a no-op on devices without root (su) or not using gservices, but that's OK.
        testInformation.device.executeShellCommand("su 0 am broadcast " +
                "-a com.google.gservices.intent.action.GSERVICES_OVERRIDE " +
                "-e finsky.play_services_auto_update_enabled false")
    }

    private fun clearGmsUpdateOverride(testInformation: TestInformation) {
        testInformation.device.executeShellCommand("su 0 am broadcast " +
                "-a com.google.gservices.intent.action.GSERVICES_OVERRIDE " +
                "--esn finsky.play_services_auto_update_enabled")
    }

    override fun tearDown(testInformation: TestInformation, e: Throwable?) {
        if (isTearDownDisabled) return
        installer.tearDown(testInformation, e)
        clearGmsUpdateOverride(testInformation)
    }
}