Merge changes from topic "bugreporting-aosp-sync"

* changes:
  Fix consent timed out to sharing bugreport data
  Add BugreportManagerTestCases to bugreport presubmit
  Enable bug report tests in pre/postsubmit
  Run bugreport unit test without script
This commit is contained in:
Gavin Corkery
2020-10-21 11:48:11 +00:00
committed by Gerrit Code Review
7 changed files with 257 additions and 80 deletions

View File

@@ -0,0 +1,48 @@
{
"presubmit": [
{
"file_patterns": ["Bugreport[^/]*\\.java"],
"name": "BugreportManagerTestCases",
"options": [
{
"exclude-annotation": "androidx.test.filters.LargeTest"
}
]
},
{
"file_patterns": ["Bugreport[^/]*\\.java"],
"name": "CtsBugreportTestCases",
"options": [
{
"exclude-annotation": "androidx.test.filters.LargeTest"
}
]
},
{
"file_patterns": ["Bugreport[^/]*\\.java"],
"name": "ShellTests",
"options": [
{
"exclude-annotation": "androidx.test.filters.LargeTest"
},
{
"exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
}
],
"postsubmit": [
{
"file_patterns": ["Bugreport[^/]*\\.java"],
"name": "BugreportManagerTestCases"
},
{
"file_patterns": ["Bugreport[^/]*\\.java"],
"name": "CtsBugreportTestCases"
},
{
"file_patterns": ["Bugreport[^/]*\\.java"],
"name": "ShellTests"
}
]
}

View File

@@ -15,12 +15,22 @@
android_test { android_test {
name: "BugreportManagerTestCases", name: "BugreportManagerTestCases",
srcs: ["src/**/*.java"], srcs: ["src/**/*.java"],
data: [":bugreport_artifacts"],
libs: [ libs: [
"android.test.runner", "android.test.runner",
"android.test.base", "android.test.base",
], ],
static_libs: ["androidx.test.rules", "truth-prebuilt"], static_libs: [
"androidx.test.rules",
"androidx.test.uiautomator_uiautomator",
"truth-prebuilt",
],
test_suites: ["general-tests"],
sdk_version: "test_current", sdk_version: "test_current",
platform_apis: true, platform_apis: true,
} }
filegroup {
name: "bugreport_artifacts",
srcs: ["config/test-sysconfig.xml"],
}

View File

@@ -21,11 +21,16 @@
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
<option name="cleanup-apks" value="true"/> <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="test-file-name" value="BugreportManagerTestCases.apk"/> <option name="push-file" key="test-sysconfig.xml" value="/system/etc/sysconfig/allow-br-from-tests.xml" />
<option name="push-file" key="BugreportManagerTestCases.apk" value="/system/priv-app/BugreportManagerTestCases.apk" />
<option name="abort-on-push-failure" value="true" />
<option name="cleanup" value="true" />
<option name="remount-system" value="true" />
</target_preparer> </target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RebootTargetPreparer" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest"> <test class="com.android.tradefed.testtype.AndroidJUnitTest">
<option name="package" value="com.android.os.bugreports.tests"/> <option name="package" value="com.android.os.bugreports.tests"/>
<!-- test-timeout unit is ms, value = 30 min --> <!-- test-timeout unit is ms, value = 30 min -->

View File

@@ -1,61 +0,0 @@
#!/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

View File

@@ -18,6 +18,8 @@ package com.android.os.bugreports.tests;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import android.Manifest; import android.Manifest;
@@ -25,17 +27,27 @@ import android.content.Context;
import android.os.BugreportManager; import android.os.BugreportManager;
import android.os.BugreportManager.BugreportCallback; import android.os.BugreportManager.BugreportCallback;
import android.os.BugreportParams; import android.os.BugreportParams;
import android.os.FileUtils;
import android.os.Handler; import android.os.Handler;
import android.os.HandlerThread; import android.os.HandlerThread;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.os.StrictMode;
import android.util.Log; import android.util.Log;
import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry; import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.UiDevice;
import androidx.test.uiautomator.UiObject2;
import androidx.test.uiautomator.Until;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExternalResource;
import org.junit.rules.TestName; import org.junit.rules.TestName;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.JUnit4; import org.junit.runners.JUnit4;
@@ -51,9 +63,11 @@ import java.util.concurrent.TimeUnit;
@RunWith(JUnit4.class) @RunWith(JUnit4.class)
public class BugreportManagerTest { public class BugreportManagerTest {
@Rule public TestName name = new TestName(); @Rule public TestName name = new TestName();
@Rule public ExtendedStrictModeVmPolicy mTemporaryVmPolicy = new ExtendedStrictModeVmPolicy();
private static final String TAG = "BugreportManagerTest"; private static final String TAG = "BugreportManagerTest";
private static final long BUGREPORT_TIMEOUT_MS = TimeUnit.MINUTES.toMillis(10); private static final long BUGREPORT_TIMEOUT_MS = TimeUnit.MINUTES.toMillis(10);
private static final long UIAUTOMATOR_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(10);
private Handler mHandler; private Handler mHandler;
private Executor mExecutor; private Executor mExecutor;
@@ -86,6 +100,8 @@ public class BugreportManagerTest {
@After @After
public void teardown() throws Exception { public void teardown() throws Exception {
dropPermissions(); dropPermissions();
FileUtils.closeQuietly(mBugreportFd);
FileUtils.closeQuietly(mScreenshotFd);
} }
@@ -95,47 +111,45 @@ public class BugreportManagerTest {
// wifi bugreport does not take screenshot // wifi bugreport does not take screenshot
mBrm.startBugreport(mBugreportFd, null /*screenshotFd = null*/, wifi(), mBrm.startBugreport(mBugreportFd, null /*screenshotFd = null*/, wifi(),
mExecutor, callback); mExecutor, callback);
shareConsentDialog(ConsentReply.ALLOW);
waitTillDoneOrTimeout(callback); waitTillDoneOrTimeout(callback);
assertThat(callback.isDone()).isTrue(); assertThat(callback.isDone()).isTrue();
// Wifi bugreports should not receive any progress. // Wifi bugreports should not receive any progress.
assertThat(callback.hasReceivedProgress()).isFalse(); assertThat(callback.hasReceivedProgress()).isFalse();
// TODO: Because of b/130234145, consent dialog is not shown; so we get a timeout error. assertThat(mBugreportFile.length()).isGreaterThan(0L);
// 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); assertFdsAreClosed(mBugreportFd);
} }
@LargeTest
@Test @Test
public void normalFlow_interactive() throws Exception { public void normalFlow_interactive() throws Exception {
BugreportCallbackImpl callback = new BugreportCallbackImpl(); BugreportCallbackImpl callback = new BugreportCallbackImpl();
// interactive bugreport does not take screenshot // interactive bugreport does not take screenshot
mBrm.startBugreport(mBugreportFd, null /*screenshotFd = null*/, interactive(), mBrm.startBugreport(mBugreportFd, null /*screenshotFd = null*/, interactive(),
mExecutor, callback); mExecutor, callback);
shareConsentDialog(ConsentReply.ALLOW);
waitTillDoneOrTimeout(callback); waitTillDoneOrTimeout(callback);
assertThat(callback.isDone()).isTrue(); assertThat(callback.isDone()).isTrue();
// Interactive bugreports show progress updates. // Interactive bugreports show progress updates.
assertThat(callback.hasReceivedProgress()).isTrue(); assertThat(callback.hasReceivedProgress()).isTrue();
assertThat(callback.getErrorCode()).isEqualTo( assertThat(mBugreportFile.length()).isGreaterThan(0L);
BugreportCallback.BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
assertFdsAreClosed(mBugreportFd); assertFdsAreClosed(mBugreportFd);
} }
@LargeTest
@Test @Test
public void normalFlow_full() throws Exception { public void normalFlow_full() throws Exception {
BugreportCallbackImpl callback = new BugreportCallbackImpl(); BugreportCallbackImpl callback = new BugreportCallbackImpl();
mBrm.startBugreport(mBugreportFd, mScreenshotFd, full(), mExecutor, callback); mBrm.startBugreport(mBugreportFd, mScreenshotFd, full(), mExecutor, callback);
shareConsentDialog(ConsentReply.ALLOW);
waitTillDoneOrTimeout(callback); waitTillDoneOrTimeout(callback);
assertThat(callback.isDone()).isTrue(); assertThat(callback.isDone()).isTrue();
assertThat(callback.getErrorCode()).isEqualTo( // bugreport and screenshot files shouldn't be empty when user consents.
BugreportCallback.BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT); assertThat(mBugreportFile.length()).isGreaterThan(0L);
// bugreport and screenshot files should be empty when user consent timed out. assertThat(mScreenshotFile.length()).isGreaterThan(0L);
assertThat(mBugreportFile.length()).isEqualTo(0);
assertThat(mScreenshotFile.length()).isEqualTo(0);
assertFdsAreClosed(mBugreportFd, mScreenshotFd); assertFdsAreClosed(mBugreportFd, mScreenshotFd);
} }
@@ -144,6 +158,8 @@ public class BugreportManagerTest {
// Start bugreport #1 // Start bugreport #1
BugreportCallbackImpl callback = new BugreportCallbackImpl(); BugreportCallbackImpl callback = new BugreportCallbackImpl();
mBrm.startBugreport(mBugreportFd, mScreenshotFd, wifi(), mExecutor, callback); mBrm.startBugreport(mBugreportFd, mScreenshotFd, wifi(), mExecutor, callback);
// TODO(b/162389762) Make sure the wait time is reasonable
shareConsentDialog(ConsentReply.ALLOW);
// Before #1 is done, try to start #2. // Before #1 is done, try to start #2.
assertThat(callback.isDone()).isFalse(); assertThat(callback.isDone()).isFalse();
@@ -375,4 +391,88 @@ public class BugreportManagerTest {
private static BugreportParams full() { private static BugreportParams full() {
return new BugreportParams(BugreportParams.BUGREPORT_MODE_FULL); return new BugreportParams(BugreportParams.BUGREPORT_MODE_FULL);
} }
/* Allow/deny the consent dialog to sharing bugreport data or check existence only. */
private enum ConsentReply {
ALLOW,
DENY,
TIMEOUT
}
/*
* Ensure the consent dialog is shown and take action according to <code>consentReply<code/>.
* It will fail if the dialog is not shown when <code>ignoreNotFound<code/> is false.
*/
private void shareConsentDialog(@NonNull ConsentReply consentReply) throws Exception {
mTemporaryVmPolicy.permitIncorrectContextUse();
final UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
// Unlock before finding/clicking an object.
device.wakeUp();
device.executeShellCommand("wm dismiss-keyguard");
final BySelector consentTitleObj = By.res("android", "alertTitle");
if (!device.wait(Until.hasObject(consentTitleObj), UIAUTOMATOR_TIMEOUT_MS)) {
fail("The consent dialog is not found");
}
if (consentReply.equals(ConsentReply.TIMEOUT)) {
return;
}
final BySelector selector;
if (consentReply.equals(ConsentReply.ALLOW)) {
selector = By.res("android", "button1");
Log.d(TAG, "Allow the consent dialog");
} else { // ConsentReply.DENY
selector = By.res("android", "button2");
Log.d(TAG, "Deny the consent dialog");
}
final UiObject2 btnObj = device.findObject(selector);
assertNotNull("The button of consent dialog is not found", btnObj);
btnObj.click();
Log.d(TAG, "Wait for the dialog to be dismissed");
assertTrue(device.wait(Until.gone(consentTitleObj), UIAUTOMATOR_TIMEOUT_MS));
}
/**
* A rule to change strict mode vm policy temporarily till test method finished.
*
* To permit the non-visual context usage in tests while taking bugreports need user consent,
* or UiAutomator/BugreportManager.DumpstateListener would run into error.
* UiDevice#findObject creates UiObject2, its Gesture object and ViewConfiguration and
* UiObject2#click need to know bounds. Both of them access to WindowManager internally without
* visual context comes from InstrumentationRegistry and violate the policy.
* Also <code>DumpstateListener<code/> violate the policy when onScreenshotTaken is called.
*
* TODO(b/161201609) Remove this class once violations fixed.
*/
static class ExtendedStrictModeVmPolicy extends ExternalResource {
private boolean mWasVmPolicyChanged = false;
private StrictMode.VmPolicy mOldVmPolicy;
@Override
protected void after() {
restoreVmPolicyIfNeeded();
}
public void permitIncorrectContextUse() {
// Allow to call multiple times without losing old policy.
if (mOldVmPolicy == null) {
mOldVmPolicy = StrictMode.getVmPolicy();
}
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectAll()
.permitIncorrectContextUse()
.penaltyLog()
.build());
mWasVmPolicyChanged = true;
}
private void restoreVmPolicyIfNeeded() {
if (mWasVmPolicyChanged && mOldVmPolicy != null) {
StrictMode.setVmPolicy(mOldVmPolicy);
mOldVmPolicy = null;
}
}
}
} }

View File

@@ -0,0 +1,31 @@
{
"presubmit": [
{
"name": "CtsBugreportTestCases",
"options": [
{
"exclude-annotation": "androidx.test.filters.LargeTest"
}
]
},
{
"name": "ShellTests",
"options": [
{
"exclude-annotation": "androidx.test.filters.LargeTest"
},
{
"exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
}
],
"postsubmit": [
{
"name": "CtsBugreportTestCases"
},
{
"name": "ShellTests"
}
]
}

View File

@@ -1,7 +1,51 @@
{ {
"presubmit": [ "presubmit": [
{
"file_patterns": ["Bugreport[^/]*\\.java"],
"name": "BugreportManagerTestCases",
"options": [
{
"exclude-annotation": "androidx.test.filters.LargeTest"
}
]
},
{
"file_patterns": ["Bugreport[^/]*\\.java"],
"name": "CtsBugreportTestCases",
"options": [
{
"exclude-annotation": "androidx.test.filters.LargeTest"
}
]
},
{ {
"name": "CtsUsbTests" "name": "CtsUsbTests"
},
{
"file_patterns": ["Bugreport[^/]*\\.java"],
"name": "ShellTests",
"options": [
{
"exclude-annotation": "androidx.test.filters.LargeTest"
},
{
"exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
}
],
"postsubmit": [
{
"file_patterns": ["Bugreport[^/]*\\.java"],
"name": "BugreportManagerTestCases"
},
{
"file_patterns": ["Bugreport[^/]*\\.java"],
"name": "CtsBugreportTestCases"
},
{
"file_patterns": ["Bugreport[^/]*\\.java"],
"name": "ShellTests"
} }
] ]
} }