Merge "Add unittests for BugreportManager API" into qt-dev
This commit is contained in:
27
core/tests/bugreports/Android.bp
Normal file
27
core/tests/bugreports/Android.bp
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright (C) 2019 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.
|
||||
|
||||
android_test {
|
||||
name: "BugreportManagerTestCases",
|
||||
srcs: ["src/**/*.java"],
|
||||
libs: [
|
||||
"android.test.runner",
|
||||
"android.test.base",
|
||||
],
|
||||
static_libs: ["androidx.test.rules", "truth-prebuilt"],
|
||||
test_suites: ["general-tests"],
|
||||
sdk_version: "test_current",
|
||||
platform_apis: true,
|
||||
}
|
||||
|
||||
24
core/tests/bugreports/AndroidManifest.xml
Normal file
24
core/tests/bugreports/AndroidManifest.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
* Copyright (C) 2019 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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:installLocation="internalOnly"
|
||||
package="com.android.os.bugreports.tests">
|
||||
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
|
||||
android:targetPackage="com.android.os.bugreports.tests"
|
||||
android:label="Unit tests of BugreportManager" />
|
||||
</manifest>
|
||||
35
core/tests/bugreports/AndroidTest.xml
Normal file
35
core/tests/bugreports/AndroidTest.xml
Normal file
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2019 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.
|
||||
-->
|
||||
<configuration description="Config for BugreportManager test cases">
|
||||
<option name="test-suite-tag" value="apct" />
|
||||
<option name="test-suite-tag" value="apct-instrumentation" />
|
||||
|
||||
<option name="config-descriptor:metadata" key="component" value="framework"/>
|
||||
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
|
||||
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
|
||||
|
||||
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
|
||||
<option name="cleanup-apks" value="true"/>
|
||||
<option name="test-file-name" value="BugreportManagerTestCases.apk"/>
|
||||
</target_preparer>
|
||||
|
||||
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
|
||||
<option name="package" value="com.android.os.bugreports.tests"/>
|
||||
<!-- test-timeout unit is ms, value = 30 min -->
|
||||
<option name="test-timeout" value="1800000" />
|
||||
<option name="runtime-hint" value="30m" />
|
||||
</test>
|
||||
</configuration>
|
||||
20
core/tests/bugreports/config/test-sysconfig.xml
Normal file
20
core/tests/bugreports/config/test-sysconfig.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2019 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.
|
||||
-->
|
||||
|
||||
<!-- WARNING: This is a test config. -->
|
||||
<config>
|
||||
<bugreport-whitelisted package="com.android.os.bugreports.tests" />
|
||||
</config>
|
||||
61
core/tests/bugreports/run.sh
Executable file
61
core/tests/bugreports/run.sh
Executable file
@@ -0,0 +1,61 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright (C) 2019 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.
|
||||
|
||||
# Script to run bugreport unitests
|
||||
# Must run on a rooted device.
|
||||
# Must run lunch before running the script
|
||||
# Usage: ${ANDROID_BUILD_TOP}/frameworks/base/core/tests/bugreports/run.sh
|
||||
|
||||
# NOTE: This script replaces the framework-sysconfig.xml on your device, so use with caution.
|
||||
# It tries to replace it when done, but if the script does not finish cleanly
|
||||
# (for e.g. force stopped mid-way) your device will be left in an inconsistent state.
|
||||
# Reflashing will restore the right config.
|
||||
|
||||
TMP_SYS_CONFIG=/var/tmp/framework-sysconfig.xml
|
||||
|
||||
if [[ -z $ANDROID_PRODUCT_OUT ]]; then
|
||||
echo "Please lunch before running this test."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Print every command to console.
|
||||
set -x
|
||||
|
||||
make -j BugreportManagerTestCases &&
|
||||
adb root &&
|
||||
adb remount &&
|
||||
adb wait-for-device &&
|
||||
# Save the sysconfig file in a tmp location and push the test config in
|
||||
adb pull /system/etc/sysconfig/framework-sysconfig.xml "${TMP_SYS_CONFIG}" &&
|
||||
adb push $ANDROID_BUILD_TOP/frameworks/base/core/tests/bugreports/config/test-sysconfig.xml /system/etc/sysconfig/framework-sysconfig.xml &&
|
||||
# The test app needs to be a priv-app.
|
||||
adb push $OUT/testcases/BugreportManagerTestCases/*/BugreportManagerTestCases.apk /system/priv-app ||
|
||||
exit 1
|
||||
|
||||
adb reboot &&
|
||||
adb wait-for-device &&
|
||||
atest BugreportManagerTest || echo "Tests FAILED!"
|
||||
|
||||
# Restore the saved config file
|
||||
if [ -f "${TMP_SYS_CONFIG}" ]; then
|
||||
SIZE=$(stat --printf="%s" "${TMP_SYS_CONFIG}")
|
||||
if [ SIZE > 0 ]; then
|
||||
adb remount &&
|
||||
adb wait-for-device &&
|
||||
adb push "${TMP_SYS_CONFIG}" /system/etc/sysconfig/framework-sysconfig.xml &&
|
||||
rm "${TMP_SYS_CONFIG}"
|
||||
fi
|
||||
fi
|
||||
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.os.bugreports.tests;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.os.BugreportManager;
|
||||
import android.os.BugreportManager.BugreportCallback;
|
||||
import android.os.BugreportParams;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestName;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
||||
/**
|
||||
* Tests for BugreportManager API.
|
||||
*/
|
||||
@RunWith(JUnit4.class)
|
||||
public class BugreportManagerTest {
|
||||
@Rule public TestName name = new TestName();
|
||||
|
||||
private static final String TAG = "BugreportManagerTest";
|
||||
private static final long BUGREPORT_TIMEOUT_MS = TimeUnit.MINUTES.toMillis(10);
|
||||
|
||||
private Handler mHandler;
|
||||
private Executor mExecutor;
|
||||
private BugreportManager mBrm;
|
||||
private ParcelFileDescriptor mBugreportFd;
|
||||
private ParcelFileDescriptor mScreenshotFd;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
mHandler = createHandler();
|
||||
mExecutor = (runnable) -> {
|
||||
if (mHandler != null) {
|
||||
mHandler.post(() -> {
|
||||
runnable.run();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
mBrm = getBugreportManager();
|
||||
mBugreportFd = parcelFd("bugreport_" + name.getMethodName(), ".zip");
|
||||
mScreenshotFd = parcelFd("screenshot_" + name.getMethodName(), ".png");
|
||||
|
||||
getPermissions();
|
||||
}
|
||||
|
||||
@After
|
||||
public void teardown() throws Exception {
|
||||
dropPermissions();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void normalFlow_wifi() throws Exception {
|
||||
BugreportCallbackImpl callback = new BugreportCallbackImpl();
|
||||
mBrm.startBugreport(mBugreportFd, mScreenshotFd, wifi(), mExecutor, callback);
|
||||
waitTillDoneOrTimeout(callback);
|
||||
|
||||
assertThat(callback.isDone()).isTrue();
|
||||
// Wifi bugreports should not receive any progress.
|
||||
assertThat(callback.hasReceivedProgress()).isFalse();
|
||||
// TODO: Because of b/130234145, consent dialog is not shown; so we get a timeout error.
|
||||
// When the bug is fixed, accept consent via UIAutomator and verify contents
|
||||
// of mBugreportFd.
|
||||
assertThat(callback.getErrorCode()).isEqualTo(
|
||||
BugreportCallback.BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
|
||||
assertFdsAreClosed(mBugreportFd, mScreenshotFd);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void normalFlow_interactive() throws Exception {
|
||||
BugreportCallbackImpl callback = new BugreportCallbackImpl();
|
||||
mBrm.startBugreport(mBugreportFd, mScreenshotFd, interactive(), mExecutor, callback);
|
||||
|
||||
waitTillDoneOrTimeout(callback);
|
||||
assertThat(callback.isDone()).isTrue();
|
||||
// Interactive bugreports show progress updates.
|
||||
assertThat(callback.hasReceivedProgress()).isTrue();
|
||||
assertThat(callback.getErrorCode()).isEqualTo(
|
||||
BugreportCallback.BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
|
||||
assertFdsAreClosed(mBugreportFd, mScreenshotFd);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simultaneousBugreportsNotAllowed() throws Exception {
|
||||
// Start bugreport #1
|
||||
BugreportCallbackImpl callback = new BugreportCallbackImpl();
|
||||
mBrm.startBugreport(mBugreportFd, mScreenshotFd, wifi(), mExecutor, callback);
|
||||
|
||||
// Before #1 is done, try to start #2.
|
||||
assertThat(callback.isDone()).isFalse();
|
||||
BugreportCallbackImpl callback2 = new BugreportCallbackImpl();
|
||||
ParcelFileDescriptor bugreportFd2 = parcelFd("bugreport_2_" + name.getMethodName(), ".zip");
|
||||
ParcelFileDescriptor screenshotFd2 =
|
||||
parcelFd("screenshot_2_" + name.getMethodName(), ".png");
|
||||
mBrm.startBugreport(bugreportFd2, screenshotFd2, wifi(), mExecutor, callback2);
|
||||
Thread.sleep(500 /* .5s */);
|
||||
|
||||
// Verify #2 encounters an error.
|
||||
assertThat(callback2.getErrorCode()).isEqualTo(
|
||||
BugreportCallback.BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS);
|
||||
assertFdsAreClosed(bugreportFd2, screenshotFd2);
|
||||
|
||||
// Cancel #1 so we can move on to the next test.
|
||||
mBrm.cancelBugreport();
|
||||
Thread.sleep(500 /* .5s */);
|
||||
assertThat(callback.isDone()).isTrue();
|
||||
assertFdsAreClosed(mBugreportFd, mScreenshotFd);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cancelBugreport() throws Exception {
|
||||
// Start a bugreport.
|
||||
BugreportCallbackImpl callback = new BugreportCallbackImpl();
|
||||
mBrm.startBugreport(mBugreportFd, mScreenshotFd, wifi(), mExecutor, callback);
|
||||
|
||||
// Verify it's not finished yet.
|
||||
assertThat(callback.isDone()).isFalse();
|
||||
|
||||
// Try to cancel it, but first without DUMP permission.
|
||||
dropPermissions();
|
||||
try {
|
||||
mBrm.cancelBugreport();
|
||||
fail("Expected cancelBugreport to throw SecurityException without DUMP permission");
|
||||
} catch (SecurityException expected) {
|
||||
}
|
||||
assertThat(callback.isDone()).isFalse();
|
||||
|
||||
// Try again, with DUMP permission.
|
||||
getPermissions();
|
||||
mBrm.cancelBugreport();
|
||||
Thread.sleep(500 /* .5s */);
|
||||
assertThat(callback.isDone()).isTrue();
|
||||
assertFdsAreClosed(mBugreportFd, mScreenshotFd);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void insufficientPermissions_throwsException() throws Exception {
|
||||
dropPermissions();
|
||||
|
||||
BugreportCallbackImpl callback = new BugreportCallbackImpl();
|
||||
try {
|
||||
mBrm.startBugreport(mBugreportFd, mScreenshotFd, wifi(), mExecutor, callback);
|
||||
fail("Expected startBugreport to throw SecurityException without DUMP permission");
|
||||
} catch (SecurityException expected) {
|
||||
}
|
||||
assertFdsAreClosed(mBugreportFd, mScreenshotFd);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidBugreportMode_throwsException() throws Exception {
|
||||
BugreportCallbackImpl callback = new BugreportCallbackImpl();
|
||||
|
||||
try {
|
||||
mBrm.startBugreport(mBugreportFd, mScreenshotFd,
|
||||
new BugreportParams(25) /* unknown bugreport mode */, mExecutor, callback);
|
||||
fail("Expected to throw IllegalArgumentException with unknown bugreport mode");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
assertFdsAreClosed(mBugreportFd, mScreenshotFd);
|
||||
}
|
||||
|
||||
private Handler createHandler() {
|
||||
HandlerThread handlerThread = new HandlerThread("BugreportManagerTest");
|
||||
handlerThread.start();
|
||||
return new Handler(handlerThread.getLooper());
|
||||
}
|
||||
|
||||
/* Implementatiion of {@link BugreportCallback} that offers wrappers around execution result */
|
||||
private static final class BugreportCallbackImpl extends BugreportCallback {
|
||||
private int mErrorCode = -1;
|
||||
private boolean mSuccess = false;
|
||||
private boolean mReceivedProgress = false;
|
||||
private final Object mLock = new Object();
|
||||
|
||||
@Override
|
||||
public void onProgress(float progress) {
|
||||
synchronized (mLock) {
|
||||
mReceivedProgress = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(int errorCode) {
|
||||
synchronized (mLock) {
|
||||
mErrorCode = errorCode;
|
||||
Log.d(TAG, "bugreport errored.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinished() {
|
||||
synchronized (mLock) {
|
||||
Log.d(TAG, "bugreport finished.");
|
||||
mSuccess = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Indicates completion; and ended up with a success or error. */
|
||||
public boolean isDone() {
|
||||
synchronized (mLock) {
|
||||
return (mErrorCode != -1) || mSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
public int getErrorCode() {
|
||||
synchronized (mLock) {
|
||||
return mErrorCode;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
synchronized (mLock) {
|
||||
return mSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasReceivedProgress() {
|
||||
synchronized (mLock) {
|
||||
return mReceivedProgress;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static BugreportManager getBugreportManager() {
|
||||
Context context = InstrumentationRegistry.getContext();
|
||||
BugreportManager bm =
|
||||
(BugreportManager) context.getSystemService(Context.BUGREPORT_SERVICE);
|
||||
if (bm == null) {
|
||||
throw new AssertionError("Failed to get BugreportManager");
|
||||
}
|
||||
return bm;
|
||||
}
|
||||
|
||||
private static ParcelFileDescriptor parcelFd(String prefix, String extension) throws Exception {
|
||||
File f = File.createTempFile(prefix, extension);
|
||||
f.setReadable(true, true);
|
||||
f.setWritable(true, true);
|
||||
|
||||
return ParcelFileDescriptor.open(f,
|
||||
ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_APPEND);
|
||||
}
|
||||
|
||||
private static void dropPermissions() {
|
||||
InstrumentationRegistry.getInstrumentation().getUiAutomation()
|
||||
.dropShellPermissionIdentity();
|
||||
}
|
||||
|
||||
private static void getPermissions() {
|
||||
InstrumentationRegistry.getInstrumentation().getUiAutomation()
|
||||
.adoptShellPermissionIdentity(Manifest.permission.DUMP);
|
||||
}
|
||||
|
||||
private static void assertFdIsClosed(ParcelFileDescriptor pfd) {
|
||||
try {
|
||||
int fd = pfd.getFd();
|
||||
fail("Expected ParcelFileDescriptor argument to be closed, but got: " + fd);
|
||||
} catch (IllegalStateException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertFdsAreClosed(ParcelFileDescriptor... pfds) {
|
||||
for (int i = 0; i < pfds.length; i++) {
|
||||
assertFdIsClosed(pfds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private static long now() {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
|
||||
private static boolean shouldTimeout(long startTimeMs) {
|
||||
return now() - startTimeMs >= BUGREPORT_TIMEOUT_MS;
|
||||
}
|
||||
|
||||
private static void waitTillDoneOrTimeout(BugreportCallbackImpl callback) throws Exception {
|
||||
long startTimeMs = now();
|
||||
while (!callback.isDone()) {
|
||||
Thread.sleep(1000 /* 1s */);
|
||||
if (shouldTimeout(startTimeMs)) {
|
||||
break;
|
||||
}
|
||||
Log.d(TAG, "Waited " + (now() - startTimeMs) + "ms");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a {@link BugreportParams} for wifi only bugreport.
|
||||
*
|
||||
* <p>Wifi bugreports have minimal content and are fast to run. They also suppress progress
|
||||
* updates.
|
||||
*/
|
||||
private static BugreportParams wifi() {
|
||||
return new BugreportParams(BugreportParams.BUGREPORT_MODE_WIFI);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a {@link BugreportParams} for interactive bugreport that offers progress updates.
|
||||
*
|
||||
* <p>This is the typical bugreport taken by users. This can take on the order of minutes to
|
||||
* finish.
|
||||
*/
|
||||
private static BugreportParams interactive() {
|
||||
return new BugreportParams(BugreportParams.BUGREPORT_MODE_INTERACTIVE);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user