Merge "Initial integration test for BugReportReceiver."
This commit is contained in:
@@ -3,7 +3,7 @@ include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
|
||||
LOCAL_SRC_FILES := $(call all-subdir-java-files)
|
||||
LOCAL_SRC_FILES := $(call all-java-files-under, src)
|
||||
|
||||
LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4
|
||||
|
||||
@@ -12,3 +12,5 @@ LOCAL_CERTIFICATE := platform
|
||||
LOCAL_PRIVILEGED_MODULE := true
|
||||
|
||||
include $(BUILD_PACKAGE)
|
||||
|
||||
include $(LOCAL_PATH)/tests/Android.mk
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/*
|
||||
* Copyright (c) 2014 Google Inc.
|
||||
* Copyright (C) 2015 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.
|
||||
|
||||
@@ -61,8 +61,8 @@ public class BugreportReceiver extends BroadcastReceiver {
|
||||
|
||||
private static final String AUTHORITY = "com.android.shell";
|
||||
|
||||
private static final String EXTRA_BUGREPORT = "android.intent.extra.BUGREPORT";
|
||||
private static final String EXTRA_SCREENSHOT = "android.intent.extra.SCREENSHOT";
|
||||
static final String EXTRA_BUGREPORT = "android.intent.extra.BUGREPORT";
|
||||
static final String EXTRA_SCREENSHOT = "android.intent.extra.SCREENSHOT";
|
||||
|
||||
/**
|
||||
* Always keep the newest 8 bugreport files; 4 reports and 4 screenshots are
|
||||
|
||||
20
packages/Shell/tests/Android.mk
Normal file
20
packages/Shell/tests/Android.mk
Normal file
@@ -0,0 +1,20 @@
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
|
||||
LOCAL_SRC_FILES := $(call all-java-files-under, src)
|
||||
|
||||
LOCAL_JAVA_LIBRARIES := android.test.runner
|
||||
|
||||
# TODO: update and/or remove
|
||||
LOCAL_STATIC_JAVA_LIBRARIES := ub-uiautomator
|
||||
#LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 mockito-target ub-uiautomator
|
||||
|
||||
LOCAL_PACKAGE_NAME := ShellTests
|
||||
LOCAL_INSTRUMENTATION_FOR := Shell
|
||||
|
||||
LOCAL_CERTIFICATE := platform
|
||||
|
||||
include $(BUILD_PACKAGE)
|
||||
43
packages/Shell/tests/AndroidManifest.xml
Normal file
43
packages/Shell/tests/AndroidManifest.xml
Normal file
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/*
|
||||
* Copyright (C) 2015 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"
|
||||
package="com.android.shell.tests">
|
||||
|
||||
<application>
|
||||
<uses-library android:name="android.test.runner" />
|
||||
|
||||
<activity
|
||||
android:name="com.android.shell.ActionSendMultipleConsumerActivity"
|
||||
android:label="ActionSendMultipleConsumer"
|
||||
android:theme="@android:style/Theme.NoDisplay"
|
||||
android:noHistory="true"
|
||||
android:excludeFromRecents="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="*/*" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
<instrumentation android:name="android.test.InstrumentationTestRunner"
|
||||
android:targetPackage="com.android.shell"
|
||||
android:label="Tests for Shell" />
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.shell;
|
||||
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Bundle;
|
||||
|
||||
/**
|
||||
* Activity responsible for handling ACTION_SEND_MULTIPLE intents and passing them back to the test
|
||||
* case class (through a {@link CustomActionSendMultipleListener}).
|
||||
*/
|
||||
public class ActionSendMultipleConsumerActivity extends Activity {
|
||||
|
||||
private static final String CUSTOM_ACTION_SEND_MULTIPLE_INTENT =
|
||||
"com.android.shell.tests.CUSTOM_ACTION_SEND_MULTIPLE";
|
||||
|
||||
private static CustomActionSendMultipleListener sListener;
|
||||
|
||||
static final String UI_NAME = "ActionSendMultipleConsumer";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// The original intent cannot be broadcasted, it will fail due to security violations.
|
||||
// Since the test case is only interested in the extras, we need to create a new custom
|
||||
// intent with just them.
|
||||
final Intent intent = getIntent();
|
||||
final Intent customIntent = new Intent(CUSTOM_ACTION_SEND_MULTIPLE_INTENT);
|
||||
customIntent.putExtras(intent.getExtras());
|
||||
|
||||
getApplicationContext().sendBroadcast(customIntent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
/*
|
||||
* TODO: if finish() is not called, app will crash with an exception such as:
|
||||
* AndroidRuntime: java.lang.RuntimeException: Unable to resume activity
|
||||
* {com.android.shell.tests/com.android.shell.SendMultipleActivity}:
|
||||
* java.lang.IllegalStateException: Activity
|
||||
* {com.android.shell.tests/com.android.shell.SendMultipleActivity} did not call finish()
|
||||
* prior to onResume() completing. That seems to be a problem on M:
|
||||
* https://code.google.com/p/android-developer-preview/issues/detail?id=2353
|
||||
*/
|
||||
finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link CustomActionSendMultipleListener} singleton.
|
||||
*/
|
||||
static CustomActionSendMultipleListener getListener(Context context) {
|
||||
synchronized (ActionSendMultipleConsumerActivity.class) {
|
||||
if (sListener == null) {
|
||||
sListener = new CustomActionSendMultipleListener(context);
|
||||
}
|
||||
}
|
||||
return sListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener of custom ACTION_SEND_MULTIPLE_INTENTS.
|
||||
*/
|
||||
static class CustomActionSendMultipleListener {
|
||||
|
||||
private static final int TIMEOUT = 10;
|
||||
private final BlockingQueue<Bundle> mQueue = new SynchronousQueue<>();
|
||||
|
||||
public CustomActionSendMultipleListener(Context context) {
|
||||
BroadcastReceiver receiver = new BroadcastReceiver() {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
try {
|
||||
mQueue.put(intent.getExtras());
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
final IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(CUSTOM_ACTION_SEND_MULTIPLE_INTENT);
|
||||
context.registerReceiver(receiver, filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the extras from the custom intent, blocking until it's received.
|
||||
*/
|
||||
Bundle getExtras() {
|
||||
Bundle bundle = null;
|
||||
try {
|
||||
bundle = mQueue.poll(TIMEOUT, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
if (bundle == null) {
|
||||
throw new IllegalStateException("Intent not received after " + TIMEOUT + "s");
|
||||
}
|
||||
return bundle;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.shell;
|
||||
|
||||
import static android.test.MoreAsserts.assertContainsRegex;
|
||||
import static com.android.shell.ActionSendMultipleConsumerActivity.UI_NAME;
|
||||
import static com.android.shell.BugreportReceiver.EXTRA_BUGREPORT;
|
||||
import static com.android.shell.BugreportReceiver.EXTRA_SCREENSHOT;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import libcore.io.Streams;
|
||||
import android.app.Instrumentation;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.SystemProperties;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.support.test.uiautomator.UiDevice;
|
||||
import android.test.InstrumentationTestCase;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.shell.ActionSendMultipleConsumerActivity.CustomActionSendMultipleListener;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link BugreportReceiver}.
|
||||
* <p>
|
||||
* These tests don't mock any component and rely on external UI components (like the notification
|
||||
* bar and activity chooser), which can make them unreliable and slow.
|
||||
* <p>
|
||||
* The general workflow is:
|
||||
* <ul>
|
||||
* <li>creates the bug report files
|
||||
* <li>generates the BUGREPORT_FINISHED intent
|
||||
* <li>emulate user actions to share the intent with a custom activity
|
||||
* <li>asserts the extras received by the custom activity
|
||||
* </ul>
|
||||
* <p>
|
||||
* TODO: currently, these tests only work if the bug report sharing warning is disabled and the
|
||||
* device screen is unlocked.
|
||||
*/
|
||||
public class BugreportReceiverTest extends InstrumentationTestCase {
|
||||
|
||||
private static final String TAG = "BugreportReceiverTest";
|
||||
|
||||
// Timeout for UI operations, in milliseconds.
|
||||
private static final int TIMEOUT = 1000;
|
||||
|
||||
private static final String ROOT_DIR = "/data/data/com.android.shell/files/bugreports";
|
||||
private static final String BUGREPORT_FILE = "test_bugreport.txt";
|
||||
private static final String ZIP_FILE = "test_bugreport.zip";
|
||||
private static final String PLAIN_TEXT_PATH = ROOT_DIR + "/" + BUGREPORT_FILE;
|
||||
private static final String ZIP_PATH = ROOT_DIR + "/" + ZIP_FILE;
|
||||
private static final String SCREENSHOT_PATH = ROOT_DIR + "/test_screenshot.png";
|
||||
|
||||
private static final String BUGREPORT_CONTENT = "Dump, might as well dump!\n";
|
||||
private static final String SCREENSHOT_CONTENT = "A picture is worth a thousand words!\n";
|
||||
|
||||
private Context mContext;
|
||||
private UiBot mUiBot;
|
||||
private CustomActionSendMultipleListener mListener;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
Instrumentation instrumentation = getInstrumentation();
|
||||
mContext = instrumentation.getTargetContext();
|
||||
mUiBot = new UiBot(UiDevice.getInstance(instrumentation), TIMEOUT);
|
||||
mListener = ActionSendMultipleConsumerActivity.getListener(mContext);
|
||||
cancelExistingNotifications();
|
||||
}
|
||||
|
||||
public void testBugreportFinished_plainBugreportAndScreenshot() throws Exception {
|
||||
createTextFile(PLAIN_TEXT_PATH, BUGREPORT_CONTENT);
|
||||
createTextFile(SCREENSHOT_PATH, SCREENSHOT_CONTENT);
|
||||
Bundle extras = sendBugreportFinishedIntent(PLAIN_TEXT_PATH, SCREENSHOT_PATH);
|
||||
assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT);
|
||||
}
|
||||
|
||||
public void testBugreportFinished_zippedBugreportAndScreenshot() throws Exception {
|
||||
createZipFile(ZIP_PATH, BUGREPORT_FILE, BUGREPORT_CONTENT);
|
||||
createTextFile(SCREENSHOT_PATH, SCREENSHOT_CONTENT);
|
||||
Bundle extras = sendBugreportFinishedIntent(ZIP_PATH, SCREENSHOT_PATH);
|
||||
assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT);
|
||||
}
|
||||
|
||||
public void testBugreportFinished_plainBugreportAndNoScreenshot() throws Exception {
|
||||
createTextFile(PLAIN_TEXT_PATH, BUGREPORT_CONTENT);
|
||||
Bundle extras = sendBugreportFinishedIntent(PLAIN_TEXT_PATH, null);
|
||||
assertActionSendMultiple(extras, BUGREPORT_CONTENT, null);
|
||||
}
|
||||
|
||||
public void testBugreportFinished_zippedBugreportAndNoScreenshot() throws Exception {
|
||||
createZipFile(ZIP_PATH, BUGREPORT_FILE, BUGREPORT_CONTENT);
|
||||
Bundle extras = sendBugreportFinishedIntent(ZIP_PATH, null);
|
||||
assertActionSendMultiple(extras, BUGREPORT_CONTENT, null);
|
||||
}
|
||||
|
||||
private void cancelExistingNotifications() {
|
||||
NotificationManager nm = NotificationManager.from(mContext);
|
||||
for (StatusBarNotification notification : nm.getActiveNotifications()) {
|
||||
int id = notification.getId();
|
||||
Log.i(TAG, "Canceling existing notification (id=" + id + ")");
|
||||
nm.cancel(id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a "bugreport finished" intent and waits for the result.
|
||||
*
|
||||
* @return extras sent to the bugreport finished consumer.
|
||||
*/
|
||||
private Bundle sendBugreportFinishedIntent(String bugreportPath, String screenshotPath) {
|
||||
Intent intent = new Intent("android.intent.action.BUGREPORT_FINISHED");
|
||||
if (bugreportPath != null) {
|
||||
intent.putExtra(EXTRA_BUGREPORT, bugreportPath);
|
||||
}
|
||||
if (screenshotPath != null) {
|
||||
intent.putExtra(EXTRA_SCREENSHOT, screenshotPath);
|
||||
}
|
||||
|
||||
mContext.sendBroadcast(intent);
|
||||
|
||||
mUiBot.clickOnNotification(mContext.getString(R.string.bugreport_finished_title));
|
||||
mUiBot.chooseActivity(UI_NAME);
|
||||
return mListener.getExtras();
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts the proper ACTION_SEND_MULTIPLE intent was sent.
|
||||
*/
|
||||
private void assertActionSendMultiple(Bundle extras, String bugreportContent,
|
||||
String screenshotContent) throws IOException {
|
||||
String body = extras.getString(Intent.EXTRA_TEXT);
|
||||
assertContainsRegex("missing build info",
|
||||
SystemProperties.get("ro.build.description"), body);
|
||||
assertContainsRegex("missing serial number",
|
||||
SystemProperties.get("ro.serialno"), body);
|
||||
|
||||
assertEquals("wrong subject", ZIP_FILE, extras.getString(Intent.EXTRA_SUBJECT));
|
||||
|
||||
List<Uri> attachments = extras.getParcelableArrayList(Intent.EXTRA_STREAM);
|
||||
int expectedSize = screenshotContent != null ? 2 : 1;
|
||||
assertEquals("wrong number of attachments", expectedSize, attachments.size());
|
||||
|
||||
// Need to interact through all attachments, since order is not guaranteed.
|
||||
Uri zipUri = null, screenshotUri = null;
|
||||
for (Uri attachment : attachments) {
|
||||
if (attachment.getPath().endsWith(".zip")) {
|
||||
zipUri = attachment;
|
||||
}
|
||||
if (attachment.getPath().endsWith(".png")) {
|
||||
screenshotUri = attachment;
|
||||
}
|
||||
}
|
||||
assertNotNull("did not get .zip attachment", zipUri);
|
||||
assertZipContent(zipUri, BUGREPORT_FILE, BUGREPORT_CONTENT);
|
||||
|
||||
if (screenshotContent != null) {
|
||||
assertNotNull("did not get .png attachment", screenshotUri);
|
||||
assertContent(screenshotUri, SCREENSHOT_CONTENT);
|
||||
} else {
|
||||
assertNull("should not have .png attachment", screenshotUri);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertContent(Uri uri, String expectedContent) throws IOException {
|
||||
Log.v(TAG, "assertContents(uri=" + uri);
|
||||
try (InputStream is = mContext.getContentResolver().openInputStream(uri)) {
|
||||
String actualContent = new String(Streams.readFully(is));
|
||||
assertEquals("wrong content for '" + uri + "'", expectedContent, actualContent);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertZipContent(Uri uri, String entryName, String expectedContent)
|
||||
throws IOException, IOException {
|
||||
Log.v(TAG, "assertZipEntry(uri=" + uri + ", entryName=" + entryName);
|
||||
try (ZipInputStream zis = new ZipInputStream(mContext.getContentResolver().openInputStream(
|
||||
uri))) {
|
||||
ZipEntry entry;
|
||||
while ((entry = zis.getNextEntry()) != null) {
|
||||
Log.v(TAG, "Zip entry: " + entry.getName());
|
||||
if (entry.getName().equals(entryName)) {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
Streams.copy(zis, bos);
|
||||
String actualContent = new String(bos.toByteArray(), "UTF-8");
|
||||
bos.close();
|
||||
assertEquals("wrong content for zip entry'" + entryName + "' on '" + uri + "'",
|
||||
expectedContent, actualContent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
fail("Did not find entry '" + entryName + "' on file '" + uri + "'");
|
||||
}
|
||||
|
||||
private static void createTextFile(String path, String content) throws IOException {
|
||||
Log.v(TAG, "createFile(" + path + ")");
|
||||
try (Writer writer = new BufferedWriter(new OutputStreamWriter(
|
||||
new FileOutputStream(path)))) {
|
||||
writer.write(content);
|
||||
}
|
||||
}
|
||||
|
||||
private void createZipFile(String path, String entryName, String content) throws IOException {
|
||||
Log.v(TAG, "createZipFile(" + path + ", " + entryName + ")");
|
||||
try (ZipOutputStream zos = new ZipOutputStream(
|
||||
new BufferedOutputStream(new FileOutputStream(path)))) {
|
||||
ZipEntry entry = new ZipEntry(entryName);
|
||||
zos.putNextEntry(entry);
|
||||
byte[] data = content.getBytes();
|
||||
zos.write(data, 0, data.length);
|
||||
zos.closeEntry();
|
||||
}
|
||||
}
|
||||
}
|
||||
130
packages/Shell/tests/src/com/android/shell/UiBot.java
Normal file
130
packages/Shell/tests/src/com/android/shell/UiBot.java
Normal file
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.shell;
|
||||
|
||||
import android.support.test.uiautomator.By;
|
||||
import android.support.test.uiautomator.UiDevice;
|
||||
import android.support.test.uiautomator.UiObject;
|
||||
import android.support.test.uiautomator.UiObjectNotFoundException;
|
||||
import android.support.test.uiautomator.UiSelector;
|
||||
import android.support.test.uiautomator.Until;
|
||||
import android.util.Log;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* A helper class for UI-related testing tasks.
|
||||
*/
|
||||
final class UiBot {
|
||||
|
||||
private static final String TAG = "UiBot";
|
||||
private static final String SYSTEMUI_PACKAGED = "com.android.systemui";
|
||||
|
||||
private final UiDevice mDevice;
|
||||
private final int mTimeout;
|
||||
|
||||
public UiBot(UiDevice device, int timeout) {
|
||||
mDevice = device;
|
||||
mTimeout = timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the system notification and clicks a given notification.
|
||||
*
|
||||
* @param text Notificaton's text as displayed by the UI.
|
||||
*/
|
||||
public void clickOnNotification(String text) {
|
||||
boolean opened = mDevice.openNotification();
|
||||
Log.v(TAG, "openNotification(): " + opened);
|
||||
boolean gotIt = mDevice.wait(Until.hasObject(By.pkg(SYSTEMUI_PACKAGED)), mTimeout);
|
||||
assertTrue("could not get system ui (" + SYSTEMUI_PACKAGED + ")", gotIt);
|
||||
|
||||
gotIt = mDevice.wait(Until.hasObject(By.text(text)), mTimeout);
|
||||
assertTrue("object with text '(" + text + "') not visible yet", gotIt);
|
||||
|
||||
UiObject notification = getVisibleObject(text);
|
||||
|
||||
click(notification, "bug report notification");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an object which is guaranteed to be present in the current UI.\
|
||||
*
|
||||
* @param text Object's text as displayed by the UI.
|
||||
*/
|
||||
public UiObject getVisibleObject(String text) {
|
||||
UiObject uiObject = mDevice.findObject(new UiSelector().text(text));
|
||||
assertTrue("could not find object with text '(" + text + "')", uiObject.exists());
|
||||
return uiObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clicks on a UI element.
|
||||
*
|
||||
* @param uiObject UI element to be clicked.
|
||||
* @param description Elements's description used on logging statements.
|
||||
*/
|
||||
public void click(UiObject uiObject, String description) {
|
||||
try {
|
||||
boolean clicked = uiObject.click();
|
||||
// TODO: assertion below fails sometimes, even though the click succeeded,
|
||||
// (specially when clicking the "Just Once" button), so it's currently just logged.
|
||||
// assertTrue("could not click on object '" + description + "'", clicked);
|
||||
|
||||
Log.v(TAG, "onClick for " + description + ": " + clicked);
|
||||
} catch (UiObjectNotFoundException e) {
|
||||
throw new IllegalStateException("exception when clicking on object '" + description
|
||||
+ "'", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Chooses a given activity to handle an Intent, using the "Just Once" button.
|
||||
*
|
||||
* @param name name of the activity as displayed in the UI (typically the value set by
|
||||
* {@code android:label} in the manifest).
|
||||
*/
|
||||
// TODO: UI Automator should provide such logic.
|
||||
public void chooseActivity(String name) {
|
||||
// First select activity if it's not the default option.
|
||||
boolean gotIt = mDevice.wait(Until.hasObject(By.text(name)), mTimeout);
|
||||
// TODO: if the activity is indeed the default option, call above will timeout, which will
|
||||
// make the tests run slower. It might be better to change the logic to assume the default
|
||||
// first.
|
||||
if (gotIt) {
|
||||
Log.v(TAG, "Found activity " + name + ", it's not default action");
|
||||
UiObject activityChooser = getVisibleObject(name);
|
||||
click(activityChooser, "activity chooser");
|
||||
} else {
|
||||
String text = String.format("Share with %s", name);
|
||||
Log.v(TAG, "Didn't find activity " + name
|
||||
+ ", assuming it's the default action and search for '" + text + "'");
|
||||
gotIt = mDevice.wait(Until.hasObject(By.text(text)), mTimeout);
|
||||
assertTrue("did not find text '" + text + "'", gotIt);
|
||||
}
|
||||
|
||||
// Then clicks the "Just Once" button.
|
||||
gotIt = mDevice
|
||||
.wait(Until.hasObject(By.res("android", "button_once")), mTimeout);
|
||||
assertTrue("'Just Once' button not visible yet", gotIt);
|
||||
|
||||
UiObject justOnce = mDevice
|
||||
.findObject(new UiSelector().resourceId("android:id/button_once"));
|
||||
assertTrue("'Just Once' button not found", justOnce.exists());
|
||||
|
||||
click(justOnce, "Just Once");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user