Fix BugreportReceiverTest
- Implements a local binder and #onBind in the service to return the service instance. - Mocks BugreportManager in the service to avoid service interacting with the dumpstate. - Fixes dialog name field did not disable after service is finished. - Fixes screenshot did not remove if the name is empty. - Extends screenshot delay timeout in tests. - Fine tune the UiBot to fit new bugreport notification. - Removes obsolete cases in the tests. Bug: 143130523 Test: atest BugreportReceiverTest Change-Id: Iae89206da1d08a10891503869bbbf1ce18d4e31f
This commit is contained in:
@@ -47,6 +47,7 @@ import android.content.res.Configuration;
|
|||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
|
import android.os.Binder;
|
||||||
import android.os.BugreportManager;
|
import android.os.BugreportManager;
|
||||||
import android.os.BugreportManager.BugreportCallback;
|
import android.os.BugreportManager.BugreportCallback;
|
||||||
import android.os.BugreportManager.BugreportCallback.BugreportErrorCode;
|
import android.os.BugreportManager.BugreportCallback.BugreportErrorCode;
|
||||||
@@ -186,7 +187,7 @@ public class BugreportProgressService extends Service {
|
|||||||
static final int SCREENSHOT_DELAY_SECONDS = 3;
|
static final int SCREENSHOT_DELAY_SECONDS = 3;
|
||||||
|
|
||||||
/** System property where dumpstate stores last triggered bugreport id */
|
/** System property where dumpstate stores last triggered bugreport id */
|
||||||
private static final String PROPERTY_LAST_ID = "dumpstate.last_id";
|
static final String PROPERTY_LAST_ID = "dumpstate.last_id";
|
||||||
|
|
||||||
private static final String BUGREPORT_SERVICE = "bugreport";
|
private static final String BUGREPORT_SERVICE = "bugreport";
|
||||||
|
|
||||||
@@ -233,7 +234,7 @@ public class BugreportProgressService extends Service {
|
|||||||
|
|
||||||
private File mBugreportsDir;
|
private File mBugreportsDir;
|
||||||
|
|
||||||
private BugreportManager mBugreportManager;
|
@VisibleForTesting BugreportManager mBugreportManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* id of the notification used to set service on foreground.
|
* id of the notification used to set service on foreground.
|
||||||
@@ -248,6 +249,11 @@ public class BugreportProgressService extends Service {
|
|||||||
*/
|
*/
|
||||||
private boolean mTakingScreenshot;
|
private boolean mTakingScreenshot;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The delay timeout before taking a screenshot.
|
||||||
|
*/
|
||||||
|
@VisibleForTesting int mScreenshotDelaySec = SCREENSHOT_DELAY_SECONDS;
|
||||||
|
|
||||||
@GuardedBy("sNotificationBundle")
|
@GuardedBy("sNotificationBundle")
|
||||||
private static final Bundle sNotificationBundle = new Bundle();
|
private static final Bundle sNotificationBundle = new Bundle();
|
||||||
|
|
||||||
@@ -282,6 +288,7 @@ public class BugreportProgressService extends Service {
|
|||||||
mContext.getString(R.string.bugreport_notification_channel),
|
mContext.getString(R.string.bugreport_notification_channel),
|
||||||
isTv(this) ? NotificationManager.IMPORTANCE_DEFAULT
|
isTv(this) ? NotificationManager.IMPORTANCE_DEFAULT
|
||||||
: NotificationManager.IMPORTANCE_LOW));
|
: NotificationManager.IMPORTANCE_LOW));
|
||||||
|
mBugreportManager = mContext.getSystemService(BugreportManager.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -305,7 +312,7 @@ public class BugreportProgressService extends Service {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IBinder onBind(Intent intent) {
|
public IBinder onBind(Intent intent) {
|
||||||
return null;
|
return new LocalBinder();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -375,6 +382,7 @@ public class BugreportProgressService extends Service {
|
|||||||
mInfo.renameScreenshots();
|
mInfo.renameScreenshots();
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
sendBugreportFinishedBroadcastLocked();
|
sendBugreportFinishedBroadcastLocked();
|
||||||
|
mMainThreadHandler.post(() -> mInfoDialog.onBugreportFinished(mInfo));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -624,8 +632,6 @@ public class BugreportProgressService extends Service {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mBugreportManager = (BugreportManager) mContext.getSystemService(
|
|
||||||
Context.BUGREPORT_SERVICE);
|
|
||||||
final Executor executor = ActivityThread.currentActivityThread().getExecutor();
|
final Executor executor = ActivityThread.currentActivityThread().getExecutor();
|
||||||
|
|
||||||
Log.i(TAG, "bugreport type = " + bugreportType
|
Log.i(TAG, "bugreport type = " + bugreportType
|
||||||
@@ -885,12 +891,12 @@ public class BugreportProgressService extends Service {
|
|||||||
collapseNotificationBar();
|
collapseNotificationBar();
|
||||||
final String msg = mContext.getResources()
|
final String msg = mContext.getResources()
|
||||||
.getQuantityString(com.android.internal.R.plurals.bugreport_countdown,
|
.getQuantityString(com.android.internal.R.plurals.bugreport_countdown,
|
||||||
SCREENSHOT_DELAY_SECONDS, SCREENSHOT_DELAY_SECONDS);
|
mScreenshotDelaySec, mScreenshotDelaySec);
|
||||||
Log.i(TAG, msg);
|
Log.i(TAG, msg);
|
||||||
// Show a toast just once, otherwise it might be captured in the screenshot.
|
// Show a toast just once, otherwise it might be captured in the screenshot.
|
||||||
Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show();
|
Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show();
|
||||||
|
|
||||||
takeScreenshot(id, SCREENSHOT_DELAY_SECONDS);
|
takeScreenshot(id, mScreenshotDelaySec);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1619,6 +1625,16 @@ public class BugreportProgressService extends Service {
|
|||||||
|| c == '_' || c == '-';
|
|| c == '_' || c == '-';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A local binder with interface to return an instance of BugreportProgressService for the
|
||||||
|
* purpose of testing.
|
||||||
|
*/
|
||||||
|
final class LocalBinder extends Binder {
|
||||||
|
@VisibleForTesting BugreportProgressService getService() {
|
||||||
|
return BugreportProgressService.this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class encapsulating the UI elements and logic used to display a dialog where user
|
* Helper class encapsulating the UI elements and logic used to display a dialog where user
|
||||||
* can change the details of a bugreport.
|
* can change the details of a bugreport.
|
||||||
@@ -1748,6 +1764,22 @@ public class BugreportProgressService extends Service {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the dialog that the bugreport has finished so it disables the {@code name}
|
||||||
|
* field.
|
||||||
|
* <p>Once the bugreport is finished dumpstate has already generated the final files, so
|
||||||
|
* changing the name would have no effect.
|
||||||
|
*/
|
||||||
|
void onBugreportFinished(BugreportInfo info) {
|
||||||
|
if (mId == info.id && mInfoName != null) {
|
||||||
|
mInfoName.setEnabled(false);
|
||||||
|
mInfoName.setText(null);
|
||||||
|
if (!TextUtils.isEmpty(info.getName())) {
|
||||||
|
mInfoName.setText(info.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void cancel() {
|
void cancel() {
|
||||||
if (mDialog != null) {
|
if (mDialog != null) {
|
||||||
mDialog.cancel();
|
mDialog.cancel();
|
||||||
@@ -1992,12 +2024,21 @@ public class BugreportProgressService extends Service {
|
|||||||
Log.i(TAG, "Deleting empty bugreport file: " + bugreportFile);
|
Log.i(TAG, "Deleting empty bugreport file: " + bugreportFile);
|
||||||
bugreportFile.delete();
|
bugreportFile.delete();
|
||||||
}
|
}
|
||||||
for (File file : screenshotFiles) {
|
deleteEmptyScreenshots();
|
||||||
if (file.length() == 0) {
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes empty screenshot files.
|
||||||
|
*/
|
||||||
|
private void deleteEmptyScreenshots() {
|
||||||
|
screenshotFiles.removeIf(file -> {
|
||||||
|
final long length = file.length();
|
||||||
|
if (length == 0) {
|
||||||
Log.i(TAG, "Deleting empty screenshot file: " + file);
|
Log.i(TAG, "Deleting empty screenshot file: " + file);
|
||||||
file.delete();
|
file.delete();
|
||||||
}
|
}
|
||||||
}
|
return length == 0;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2005,7 +2046,8 @@ public class BugreportProgressService extends Service {
|
|||||||
* {@code initialName} if user has changed it.
|
* {@code initialName} if user has changed it.
|
||||||
*/
|
*/
|
||||||
void renameScreenshots() {
|
void renameScreenshots() {
|
||||||
if (TextUtils.isEmpty(name)) {
|
deleteEmptyScreenshots();
|
||||||
|
if (TextUtils.isEmpty(name) || screenshotFiles.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final List<File> renamedFiles = new ArrayList<>(screenshotFiles.size());
|
final List<File> renamedFiles = new ArrayList<>(screenshotFiles.size());
|
||||||
@@ -2024,7 +2066,7 @@ public class BugreportProgressService extends Service {
|
|||||||
if (newFile.length() > 0) {
|
if (newFile.length() > 0) {
|
||||||
renamedFiles.add(newFile);
|
renamedFiles.add(newFile);
|
||||||
} else if (newFile.delete()) {
|
} else if (newFile.delete()) {
|
||||||
Log.d(TAG, "screenshot file: " + newFile + "deleted successfully.");
|
Log.d(TAG, "screenshot file: " + newFile + " deleted successfully.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
screenshotFiles = renamedFiles;
|
screenshotFiles = renamedFiles;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -18,17 +18,23 @@ package com.android.shell;
|
|||||||
|
|
||||||
import android.app.Instrumentation;
|
import android.app.Instrumentation;
|
||||||
import android.app.StatusBarManager;
|
import android.app.StatusBarManager;
|
||||||
|
import android.os.SystemClock;
|
||||||
import android.support.test.uiautomator.By;
|
import android.support.test.uiautomator.By;
|
||||||
import android.support.test.uiautomator.UiDevice;
|
import android.support.test.uiautomator.UiDevice;
|
||||||
import android.support.test.uiautomator.UiObject;
|
import android.support.test.uiautomator.UiObject;
|
||||||
|
import android.support.test.uiautomator.UiObject2;
|
||||||
import android.support.test.uiautomator.UiObjectNotFoundException;
|
import android.support.test.uiautomator.UiObjectNotFoundException;
|
||||||
import android.support.test.uiautomator.UiSelector;
|
import android.support.test.uiautomator.UiSelector;
|
||||||
import android.support.test.uiautomator.Until;
|
import android.support.test.uiautomator.Until;
|
||||||
|
import android.text.format.DateUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import static junit.framework.Assert.assertFalse;
|
import static junit.framework.Assert.assertFalse;
|
||||||
|
import static junit.framework.Assert.assertNotNull;
|
||||||
import static junit.framework.Assert.assertTrue;
|
import static junit.framework.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A helper class for UI-related testing tasks.
|
* A helper class for UI-related testing tasks.
|
||||||
*/
|
*/
|
||||||
@@ -36,6 +42,9 @@ final class UiBot {
|
|||||||
|
|
||||||
private static final String TAG = "UiBot";
|
private static final String TAG = "UiBot";
|
||||||
private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
|
private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
|
||||||
|
private static final String ANDROID_PACKAGE = "android";
|
||||||
|
|
||||||
|
private static final long SHORT_UI_TIMEOUT_MS = (3 * DateUtils.SECOND_IN_MILLIS);
|
||||||
|
|
||||||
private final Instrumentation mInstrumentation;
|
private final Instrumentation mInstrumentation;
|
||||||
private final UiDevice mDevice;
|
private final UiDevice mDevice;
|
||||||
@@ -48,9 +57,9 @@ final class UiBot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens the system notification and gets a given notification.
|
* Opens the system notification and gets a UiObject with the text.
|
||||||
*
|
*
|
||||||
* @param text Notificaton's text as displayed by the UI.
|
* @param text Notification's text as displayed by the UI.
|
||||||
* @return notification object.
|
* @return notification object.
|
||||||
*/
|
*/
|
||||||
public UiObject getNotification(String text) {
|
public UiObject getNotification(String text) {
|
||||||
@@ -62,6 +71,43 @@ final class UiBot {
|
|||||||
return getObject(text);
|
return getObject(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the system notification and gets a notification containing the text.
|
||||||
|
*
|
||||||
|
* @param text Notification's text as displayed by the UI.
|
||||||
|
* @return notification object.
|
||||||
|
*/
|
||||||
|
public UiObject2 getNotification2(String text) {
|
||||||
|
boolean opened = mDevice.openNotification();
|
||||||
|
Log.v(TAG, "openNotification(): " + opened);
|
||||||
|
final UiObject2 notificationScroller = mDevice.wait(Until.findObject(
|
||||||
|
By.res(SYSTEMUI_PACKAGE, "notification_stack_scroller")), mTimeout);
|
||||||
|
assertNotNull("could not get notification stack scroller", notificationScroller);
|
||||||
|
final List<UiObject2> notificationList = notificationScroller.getChildren();
|
||||||
|
for (UiObject2 notification: notificationList) {
|
||||||
|
final UiObject2 notificationText = notification.findObject(By.textContains(text));
|
||||||
|
if (notificationText != null) {
|
||||||
|
return notification;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expands the notification.
|
||||||
|
*
|
||||||
|
* @param notification The notification object returned by {@link #getNotification2(String)}.
|
||||||
|
*/
|
||||||
|
public void expandNotification(UiObject2 notification) {
|
||||||
|
final UiObject2 expandBtn = notification.findObject(
|
||||||
|
By.res(ANDROID_PACKAGE, "expand_button"));
|
||||||
|
if (expandBtn.getContentDescription().equals("Collapse")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
expandBtn.click();
|
||||||
|
mDevice.waitForIdle();
|
||||||
|
}
|
||||||
|
|
||||||
public void collapseStatusBar() throws Exception {
|
public void collapseStatusBar() throws Exception {
|
||||||
// TODO: mDevice should provide such method..
|
// TODO: mDevice should provide such method..
|
||||||
StatusBarManager sbm =
|
StatusBarManager sbm =
|
||||||
@@ -162,6 +208,12 @@ final class UiBot {
|
|||||||
*/
|
*/
|
||||||
public void chooseActivity(String name) {
|
public void chooseActivity(String name) {
|
||||||
// It uses an intent chooser now, so just getting the activity by text is enough...
|
// It uses an intent chooser now, so just getting the activity by text is enough...
|
||||||
|
final String share = mInstrumentation.getContext().getString(
|
||||||
|
com.android.internal.R.string.share);
|
||||||
|
boolean gotIt = mDevice.wait(Until.hasObject(By.text(share)), mTimeout);
|
||||||
|
assertTrue("could not get share activity (" + share + ")", gotIt);
|
||||||
|
swipeUp();
|
||||||
|
SystemClock.sleep(SHORT_UI_TIMEOUT_MS);
|
||||||
UiObject activity = getObject(name);
|
UiObject activity = getObject(name);
|
||||||
click(activity, name);
|
click(activity, name);
|
||||||
}
|
}
|
||||||
@@ -173,6 +225,11 @@ final class UiBot {
|
|||||||
public void turnScreenOn() throws Exception {
|
public void turnScreenOn() throws Exception {
|
||||||
mDevice.executeShellCommand("input keyevent KEYCODE_WAKEUP");
|
mDevice.executeShellCommand("input keyevent KEYCODE_WAKEUP");
|
||||||
mDevice.executeShellCommand("wm dismiss-keyguard");
|
mDevice.executeShellCommand("wm dismiss-keyguard");
|
||||||
|
mDevice.waitForIdle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void swipeUp() {
|
||||||
|
mDevice.swipe(mDevice.getDisplayWidth() / 2, mDevice.getDisplayHeight() * 3 / 4,
|
||||||
|
mDevice.getDisplayWidth() / 2, 0, 30);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user