summaryrefslogtreecommitdiff
path: root/library
diff options
context:
space:
mode:
authorMaxim Siniavine <siniavine@google.com>2012-11-28 17:50:09 -0800
committerMaxim Siniavine <siniavine@google.com>2012-11-30 16:14:50 -0800
commitb04f3c526835516b098227342e741b7ce944c2f3 (patch)
tree007259843f17cd1424df21c062309f3efb07f30f /library
parent5478840ad89ace2474b9b4d3ab14b92d9e06bf47 (diff)
downloaduiautomator-b04f3c526835516b098227342e741b7ce944c2f3.tar.gz
Added tracing of UiAutomator calls.
Bug: 7565311 Change-Id: I4e426c68ce929c1dd0c8ee47b053045fa58e63aa
Diffstat (limited to 'library')
-rw-r--r--library/src/com/android/uiautomator/core/Tracer.java285
-rw-r--r--library/src/com/android/uiautomator/core/UiCollection.java5
-rw-r--r--library/src/com/android/uiautomator/core/UiDevice.java49
-rw-r--r--library/src/com/android/uiautomator/core/UiObject.java35
-rw-r--r--library/src/com/android/uiautomator/core/UiScrollable.java28
-rw-r--r--library/src/com/android/uiautomator/testrunner/UiAutomatorTestRunner.java17
6 files changed, 417 insertions, 2 deletions
diff --git a/library/src/com/android/uiautomator/core/Tracer.java b/library/src/com/android/uiautomator/core/Tracer.java
new file mode 100644
index 0000000..d574fc0
--- /dev/null
+++ b/library/src/com/android/uiautomator/core/Tracer.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2012 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.uiautomator.core;
+
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Class that creates traces of the calls to the UiAutomator API and outputs the
+ * traces either to logcat or a logfile. Each public method in the UiAutomator
+ * that needs to be traced should include a call to Tracer.trace in the
+ * beginning. Tracing is turned off by defualt and needs to be enabled
+ * explicitly.
+ * @hide
+ */
+public class Tracer {
+ private static final String UNKNOWN_METHOD_STRING = "(unknown method)";
+ private static final String UIAUTOMATOR_PACKAGE = "com.android.uiautomator.core";
+ private static final int CALLER_LOCATION = 6;
+ private static final int METHOD_TO_TRACE_LOCATION = 5;
+ private static final int MIN_STACK_TRACE_LENGTH = 7;
+
+ /**
+ * Enum that determines where the trace output goes. It can go to either
+ * logcat, log file or both.
+ */
+ public enum Mode {
+ NONE,
+ FILE,
+ LOGCAT,
+ ALL
+ }
+
+ private interface TracerSink {
+ public void log(String message);
+
+ public void close();
+ }
+
+ private class FileSink implements TracerSink {
+ private PrintWriter mOut;
+ private SimpleDateFormat mDateFormat;
+
+ public FileSink(File file) throws FileNotFoundException {
+ mOut = new PrintWriter(file);
+ mDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);
+ }
+
+ public void log(String message) {
+ mOut.printf("%s %s\n", mDateFormat.format(new Date()), message);
+ }
+
+ public void close() {
+ mOut.close();
+ }
+ }
+
+ private class LogcatSink implements TracerSink {
+
+ private static final String LOGCAT_TAG = "UiAutomatorTrace";
+
+ public void log(String message) {
+ Log.i(LOGCAT_TAG, message);
+ }
+
+ public void close() {
+ // nothing is needed
+ }
+ }
+
+ private Mode mCurrentMode = Mode.NONE;
+ private List<TracerSink> mSinks = new ArrayList<TracerSink>();
+ private File mOutputFile;
+
+ private static Tracer mInstance = null;
+
+ /**
+ * Returns a reference to an instance of the tracer. Useful to set the
+ * parameters before the trace is collected.
+ *
+ * @return
+ */
+ public static Tracer getInstance() {
+ if (mInstance == null) {
+ mInstance = new Tracer();
+ }
+ return mInstance;
+ }
+
+ /**
+ * Sets where the trace output will go. Can be either be logcat or a file or
+ * both. Setting this to NONE will turn off tracing.
+ *
+ * @param mode
+ */
+ public void setOutputMode(Mode mode) {
+ closeSinks();
+ mCurrentMode = mode;
+ try {
+ switch (mode) {
+ case FILE:
+ if (mOutputFile == null) {
+ throw new IllegalArgumentException("Please provide a filename before " +
+ "attempting write trace to a file");
+ }
+ mSinks.add(new FileSink(mOutputFile));
+ break;
+ case LOGCAT:
+ mSinks.add(new LogcatSink());
+ break;
+ case ALL:
+ mSinks.add(new LogcatSink());
+ if (mOutputFile == null) {
+ throw new IllegalArgumentException("Please provide a filename before " +
+ "attempting write trace to a file");
+ }
+ mSinks.add(new FileSink(mOutputFile));
+ break;
+ default:
+ break;
+ }
+ } catch (FileNotFoundException e) {
+ Log.w("Tracer", "Could not open log file: " + e.getMessage());
+ }
+ }
+
+ private void closeSinks() {
+ for (TracerSink sink : mSinks) {
+ sink.close();
+ }
+ mSinks.clear();
+ }
+
+ /**
+ * Sets the name of the log file where tracing output will be written if the
+ * tracer is set to write to a file.
+ *
+ * @param filename name of the log file.
+ */
+ public void setOutputFilename(String filename) {
+ mOutputFile = new File(filename);
+ }
+
+ private void doTrace(Object[] arguments) {
+ if (mCurrentMode == Mode.NONE) {
+ return;
+ }
+
+ String caller = getCaller();
+ if (caller == null) {
+ return;
+ }
+
+ log(String.format("%s (%s)", caller, join(", ", arguments)));
+ }
+
+ private void log(String message) {
+ for (TracerSink sink : mSinks) {
+ sink.log(message);
+ }
+ }
+
+ /**
+ * Queries whether the tracing is enabled.
+ * @return true if tracing is enabled, false otherwise.
+ */
+ public boolean isTracingEnabled() {
+ return mCurrentMode != Mode.NONE;
+ }
+
+ /**
+ * Public methods in the UiAutomator should call this function to generate a
+ * trace. The trace will include the method thats is being called, it's
+ * arguments and where in the user's code the method is called from. If a
+ * public method is called internally from UIAutomator then this will not
+ * output a trace entry. Only calls from outise the UiAutomator package will
+ * produce output.
+ *
+ * Special note about array arguments. You can safely pass arrays of reference types
+ * to this function. Like String[] or Integer[]. The trace function will print their
+ * contents by calling toString() on each of the elements. This will not work for
+ * array of primitive types like int[] or float[]. Before passing them to this function
+ * convert them to arrays of reference types manually. Example: convert int[] to Integer[].
+ *
+ * @param arguments arguments of the method being traced.
+ */
+ public static void trace(Object... arguments) {
+ Tracer.getInstance().doTrace(arguments);
+ }
+
+ private static String join(String separator, Object[] strings) {
+ if (strings.length == 0)
+ return "";
+
+ StringBuilder builder = new StringBuilder(objectToString(strings[0]));
+ for (int i = 1; i < strings.length; i++) {
+ builder.append(separator);
+ builder.append(objectToString(strings[i]));
+ }
+ return builder.toString();
+ }
+
+ /**
+ * Special toString method to handle arrays. If the argument is a normal object then this will
+ * return normal output of obj.toString(). If the argument is an array this will return a
+ * string representation of the elements of the array.
+ *
+ * This method will not work for arrays of primitive types. Arrays of primitive types are
+ * expected to be converted manually by the caller. If the array is not converter then
+ * this function will only output "[...]" instead of the contents of the array.
+ *
+ * @param obj object to convert to a string
+ * @return String representation of the object.
+ */
+ private static String objectToString(Object obj) {
+ if (obj.getClass().isArray()) {
+ if (obj instanceof Object[]) {
+ return Arrays.deepToString((Object[])obj);
+ } else {
+ return "[...]";
+ }
+ } else {
+ return obj.toString();
+ }
+ }
+
+ /**
+ * This method outputs which UiAutomator method was called and where in the
+ * user code it was called from. If it can't deside which method is called
+ * it will output "(unknown method)". If the method was called from inside
+ * the UiAutomator then it returns null.
+ *
+ * @return name of the method called and where it was called from. Null if
+ * method was called from inside UiAutomator.
+ */
+ private static String getCaller() {
+ StackTraceElement stackTrace[] = Thread.currentThread().getStackTrace();
+ if (stackTrace.length < MIN_STACK_TRACE_LENGTH) {
+ return UNKNOWN_METHOD_STRING;
+ }
+
+ StackTraceElement caller = stackTrace[METHOD_TO_TRACE_LOCATION];
+ StackTraceElement previousCaller = stackTrace[CALLER_LOCATION];
+
+ if (previousCaller.getClassName().startsWith(UIAUTOMATOR_PACKAGE)) {
+ return null;
+ }
+
+ int indexOfDot = caller.getClassName().lastIndexOf('.');
+ if (indexOfDot < 0) {
+ indexOfDot = 0;
+ }
+
+ if (indexOfDot + 1 >= caller.getClassName().length()) {
+ return UNKNOWN_METHOD_STRING;
+ }
+
+ String shortClassName = caller.getClassName().substring(indexOfDot + 1);
+ return String.format("%s.%s from %s() at %s:%d", shortClassName, caller.getMethodName(),
+ previousCaller.getMethodName(), previousCaller.getFileName(),
+ previousCaller.getLineNumber());
+ }
+}
diff --git a/library/src/com/android/uiautomator/core/UiCollection.java b/library/src/com/android/uiautomator/core/UiCollection.java
index 20b6d9a..e15beb2 100644
--- a/library/src/com/android/uiautomator/core/UiCollection.java
+++ b/library/src/com/android/uiautomator/core/UiCollection.java
@@ -49,6 +49,7 @@ public class UiCollection extends UiObject {
*/
public UiObject getChildByDescription(UiSelector childPattern, String text)
throws UiObjectNotFoundException {
+ Tracer.trace(childPattern, text);
if (text != null) {
int count = getChildCount(childPattern);
for (int x = 0; x < count; x++) {
@@ -82,6 +83,7 @@ public class UiCollection extends UiObject {
*/
public UiObject getChildByInstance(UiSelector childPattern, int instance)
throws UiObjectNotFoundException {
+ Tracer.trace(childPattern, instance);
UiSelector patternSelector = UiSelector.patternBuilder(getSelector(),
UiSelector.patternBuilder(childPattern).instance(instance));
return new UiObject(patternSelector);
@@ -105,7 +107,7 @@ public class UiCollection extends UiObject {
*/
public UiObject getChildByText(UiSelector childPattern, String text)
throws UiObjectNotFoundException {
-
+ Tracer.trace(childPattern, text);
if (text != null) {
int count = getChildCount(childPattern);
for (int x = 0; x < count; x++) {
@@ -135,6 +137,7 @@ public class UiCollection extends UiObject {
* @since API Level 16
*/
public int getChildCount(UiSelector childPattern) {
+ Tracer.trace(childPattern);
UiSelector patternSelector =
UiSelector.patternBuilder(getSelector(), UiSelector.patternBuilder(childPattern));
return getQueryController().getPatternCount(patternSelector);
diff --git a/library/src/com/android/uiautomator/core/UiDevice.java b/library/src/com/android/uiautomator/core/UiDevice.java
index 7afc485..b668bea 100644
--- a/library/src/com/android/uiautomator/core/UiDevice.java
+++ b/library/src/com/android/uiautomator/core/UiDevice.java
@@ -27,6 +27,7 @@ import android.os.Environment;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.Trace;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
@@ -112,6 +113,7 @@ public class UiDevice {
* @hide
*/
public Point getDisplaySizeDp() {
+ Tracer.trace();
Display display = getDefaultDisplay();
Point p = new Point();
display.getSize(p);
@@ -134,6 +136,7 @@ public class UiDevice {
* @since API Level 17
*/
public String getProductName() {
+ Tracer.trace();
return Build.PRODUCT;
}
@@ -153,6 +156,7 @@ public class UiDevice {
* @since API Level 16
*/
public String getLastTraversedText() {
+ Tracer.trace();
return mUiAutomationBridge.getQueryController().getLastTraversedText();
}
@@ -162,6 +166,7 @@ public class UiDevice {
* @since API Level 16
*/
public void clearLastTraversedText() {
+ Tracer.trace();
mUiAutomationBridge.getQueryController().clearLastTraversedText();
}
@@ -171,6 +176,7 @@ public class UiDevice {
* @since API Level 16
*/
public boolean pressMenu() {
+ Tracer.trace();
waitForIdle();
return mUiAutomationBridge.getInteractionController().sendKeyAndWaitForEvent(
KeyEvent.KEYCODE_MENU, 0, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED,
@@ -183,6 +189,7 @@ public class UiDevice {
* @since API Level 16
*/
public boolean pressBack() {
+ Tracer.trace();
waitForIdle();
return mUiAutomationBridge.getInteractionController().sendKeyAndWaitForEvent(
KeyEvent.KEYCODE_BACK, 0, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED,
@@ -195,6 +202,7 @@ public class UiDevice {
* @since API Level 16
*/
public boolean pressHome() {
+ Tracer.trace();
waitForIdle();
return mUiAutomationBridge.getInteractionController().sendKeyAndWaitForEvent(
KeyEvent.KEYCODE_HOME, 0, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED,
@@ -207,6 +215,7 @@ public class UiDevice {
* @since API Level 16
*/
public boolean pressSearch() {
+ Tracer.trace();
return pressKeyCode(KeyEvent.KEYCODE_SEARCH);
}
@@ -216,6 +225,7 @@ public class UiDevice {
* @since API Level 16
*/
public boolean pressDPadCenter() {
+ Tracer.trace();
return pressKeyCode(KeyEvent.KEYCODE_DPAD_CENTER);
}
@@ -225,6 +235,7 @@ public class UiDevice {
* @since API Level 16
*/
public boolean pressDPadDown() {
+ Tracer.trace();
return pressKeyCode(KeyEvent.KEYCODE_DPAD_DOWN);
}
@@ -234,6 +245,7 @@ public class UiDevice {
* @since API Level 16
*/
public boolean pressDPadUp() {
+ Tracer.trace();
return pressKeyCode(KeyEvent.KEYCODE_DPAD_UP);
}
@@ -243,6 +255,7 @@ public class UiDevice {
* @since API Level 16
*/
public boolean pressDPadLeft() {
+ Tracer.trace();
return pressKeyCode(KeyEvent.KEYCODE_DPAD_LEFT);
}
@@ -252,6 +265,7 @@ public class UiDevice {
* @since API Level 16
*/
public boolean pressDPadRight() {
+ Tracer.trace();
return pressKeyCode(KeyEvent.KEYCODE_DPAD_RIGHT);
}
@@ -261,6 +275,7 @@ public class UiDevice {
* @since API Level 16
*/
public boolean pressDelete() {
+ Tracer.trace();
return pressKeyCode(KeyEvent.KEYCODE_DEL);
}
@@ -270,6 +285,7 @@ public class UiDevice {
* @since API Level 16
*/
public boolean pressEnter() {
+ Tracer.trace();
return pressKeyCode(KeyEvent.KEYCODE_ENTER);
}
@@ -281,6 +297,7 @@ public class UiDevice {
* @since API Level 16
*/
public boolean pressKeyCode(int keyCode) {
+ Tracer.trace(keyCode);
waitForIdle();
return mUiAutomationBridge.getInteractionController().sendKey(keyCode, 0);
}
@@ -295,6 +312,7 @@ public class UiDevice {
* @since API Level 16
*/
public boolean pressKeyCode(int keyCode, int metaState) {
+ Tracer.trace(keyCode, metaState);
waitForIdle();
return mUiAutomationBridge.getInteractionController().sendKey(keyCode, metaState);
}
@@ -307,6 +325,7 @@ public class UiDevice {
* @since API Level 16
*/
public boolean pressRecentApps() throws RemoteException {
+ Tracer.trace();
waitForIdle();
final IStatusBarService statusBar = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
@@ -325,6 +344,7 @@ public class UiDevice {
* @since API Level 16
*/
public int getDisplayWidth() {
+ Tracer.trace();
Display display = getDefaultDisplay();
Point p = new Point();
display.getSize(p);
@@ -338,6 +358,7 @@ public class UiDevice {
* @since API Level 16
*/
public int getDisplayHeight() {
+ Tracer.trace();
Display display = getDefaultDisplay();
Point p = new Point();
display.getSize(p);
@@ -353,6 +374,7 @@ public class UiDevice {
* @since API Level 16
*/
public boolean click(int x, int y) {
+ Tracer.trace(x, y);
if (x >= getDisplayWidth() || y >= getDisplayHeight()) {
return (false);
}
@@ -373,6 +395,7 @@ public class UiDevice {
* @since API Level 16
*/
public boolean swipe(int startX, int startY, int endX, int endY, int steps) {
+ Tracer.trace(startX, startY, endX, endY, steps);
return mUiAutomationBridge.getInteractionController()
.scrollSwipe(startX, startY, endX, endY, steps);
}
@@ -387,6 +410,7 @@ public class UiDevice {
* @since API Level 16
*/
public boolean swipe(Point[] segments, int segmentSteps) {
+ Tracer.trace(segments, segmentSteps);
return mUiAutomationBridge.getInteractionController().swipe(segments, segmentSteps);
}
@@ -396,6 +420,7 @@ public class UiDevice {
* @since API Level 16
*/
public void waitForIdle() {
+ Tracer.trace();
waitForIdle(DEFAULT_TIMEOUT_MILLIS);
}
@@ -405,6 +430,7 @@ public class UiDevice {
* @since API Level 16
*/
public void waitForIdle(long timeout) {
+ Tracer.trace(timeout);
mUiAutomationBridge.waitForIdle(timeout);
}
@@ -416,6 +442,7 @@ public class UiDevice {
*/
@Deprecated
public String getCurrentActivityName() {
+ Tracer.trace();
return mUiAutomationBridge.getQueryController().getCurrentActivityName();
}
@@ -425,6 +452,7 @@ public class UiDevice {
* @since API Level 16
*/
public String getCurrentPackageName() {
+ Tracer.trace();
return mUiAutomationBridge.getQueryController().getCurrentPackageName();
}
@@ -437,6 +465,7 @@ public class UiDevice {
* @since API Level 16
*/
public void registerWatcher(String name, UiWatcher watcher) {
+ Tracer.trace(name, watcher);
if (mInWatcherContext) {
throw new IllegalStateException("Cannot register new watcher from within another");
}
@@ -451,6 +480,7 @@ public class UiDevice {
* @since API Level 16
*/
public void removeWatcher(String name) {
+ Tracer.trace(name);
if (mInWatcherContext) {
throw new IllegalStateException("Cannot remove a watcher from within another");
}
@@ -463,6 +493,7 @@ public class UiDevice {
* @since API Level 16
*/
public void runWatchers() {
+ Tracer.trace();
if (mInWatcherContext) {
return;
}
@@ -492,6 +523,7 @@ public class UiDevice {
* @since API Level 16
*/
public void resetWatcherTriggers() {
+ Tracer.trace();
mWatchersTriggers.clear();
}
@@ -507,6 +539,7 @@ public class UiDevice {
* @since API Level 16
*/
public boolean hasWatcherTriggered(String watcherName) {
+ Tracer.trace(watcherName);
return mWatchersTriggers.contains(watcherName);
}
@@ -518,6 +551,7 @@ public class UiDevice {
* @since API Level 16
*/
public boolean hasAnyWatcherTriggered() {
+ Tracer.trace();
return mWatchersTriggers.size() > 0;
}
@@ -526,6 +560,7 @@ public class UiDevice {
* @param watcherName
*/
private void setWatcherTriggered(String watcherName) {
+ Tracer.trace(watcherName);
if (!hasWatcherTriggered(watcherName)) {
mWatchersTriggers.add(watcherName);
}
@@ -538,6 +573,7 @@ public class UiDevice {
* @since API Level 17
*/
public boolean isNaturalOrientation() {
+ Tracer.trace();
Display display = getDefaultDisplay();
return display.getRotation() == Surface.ROTATION_0 ||
display.getRotation() == Surface.ROTATION_180;
@@ -548,6 +584,7 @@ public class UiDevice {
* @since API Level 17
*/
public int getDisplayRotation() {
+ Tracer.trace();
return getDefaultDisplay().getRotation();
}
@@ -558,6 +595,7 @@ public class UiDevice {
* @since API Level 16
*/
public void freezeRotation() throws RemoteException {
+ Tracer.trace();
getAutomatorBridge().getInteractionController().freezeRotation();
}
@@ -568,6 +606,7 @@ public class UiDevice {
* @throws RemoteException
*/
public void unfreezeRotation() throws RemoteException {
+ Tracer.trace();
getAutomatorBridge().getInteractionController().unfreezeRotation();
}
@@ -581,6 +620,7 @@ public class UiDevice {
* @since API Level 17
*/
public void setOrientationLeft() throws RemoteException {
+ Tracer.trace();
getAutomatorBridge().getInteractionController().setRotationLeft();
}
@@ -594,6 +634,7 @@ public class UiDevice {
* @since API Level 17
*/
public void setOrientationRight() throws RemoteException {
+ Tracer.trace();
getAutomatorBridge().getInteractionController().setRotationRight();
}
@@ -607,6 +648,7 @@ public class UiDevice {
* @since API Level 17
*/
public void setOrientationNatural() throws RemoteException {
+ Tracer.trace();
getAutomatorBridge().getInteractionController().setRotationNatural();
}
@@ -620,6 +662,7 @@ public class UiDevice {
* @since API Level 16
*/
public void wakeUp() throws RemoteException {
+ Tracer.trace();
if(getAutomatorBridge().getInteractionController().wakeDevice()) {
// sync delay to allow the window manager to start accepting input
// after the device is awakened.
@@ -635,6 +678,7 @@ public class UiDevice {
* @since API Level 16
*/
public boolean isScreenOn() throws RemoteException {
+ Tracer.trace();
return getAutomatorBridge().getInteractionController().isScreenOn();
}
@@ -646,6 +690,7 @@ public class UiDevice {
* @since API Level 16
*/
public void sleep() throws RemoteException {
+ Tracer.trace();
getAutomatorBridge().getInteractionController().sleepDevice();
}
@@ -657,6 +702,7 @@ public class UiDevice {
* @since API Level 16
*/
public void dumpWindowHierarchy(String fileName) {
+ Tracer.trace(fileName);
AccessibilityNodeInfo root =
getAutomatorBridge().getQueryController().getAccessibilityRootNode();
if(root != null) {
@@ -681,6 +727,7 @@ public class UiDevice {
* @since API Level 16
*/
public boolean waitForWindowUpdate(final String packageName, long timeout) {
+ Tracer.trace(packageName, timeout);
if (packageName != null) {
if (!packageName.equals(getCurrentPackageName())) {
return false;
@@ -742,6 +789,7 @@ public class UiDevice {
* @since API Level 17
*/
public boolean takeScreenshot(File storePath) {
+ Tracer.trace(storePath);
return takeScreenshot(storePath, 1.0f, 90);
}
@@ -757,6 +805,7 @@ public class UiDevice {
* @since API Level 17
*/
public boolean takeScreenshot(File storePath, float scale, int quality) {
+ Tracer.trace(storePath, scale, quality);
// This is from com.android.systemui.screenshot.GlobalScreenshot#takeScreenshot
// We need to orient the screenshot correctly (and the Surface api seems to take screenshots
// only in the natural orientation of the device :!)
diff --git a/library/src/com/android/uiautomator/core/UiObject.java b/library/src/com/android/uiautomator/core/UiObject.java
index adacf39..2bf6455 100644
--- a/library/src/com/android/uiautomator/core/UiObject.java
+++ b/library/src/com/android/uiautomator/core/UiObject.java
@@ -77,6 +77,7 @@ public class UiObject {
* @since API Level 16
*/
public final UiSelector getSelector() {
+ Tracer.trace();
return new UiSelector(mSelector);
}
@@ -109,6 +110,7 @@ public class UiObject {
* @since API Level 16
*/
public UiObject getChild(UiSelector selector) throws UiObjectNotFoundException {
+ Tracer.trace(selector);
return new UiObject(getSelector().childSelector(selector));
}
@@ -124,6 +126,7 @@ public class UiObject {
* @since API Level 16
*/
public UiObject getFromParent(UiSelector selector) throws UiObjectNotFoundException {
+ Tracer.trace(selector);
return new UiObject(getSelector().fromParent(selector));
}
@@ -136,6 +139,7 @@ public class UiObject {
* @since API Level 16
*/
public int getChildCount() throws UiObjectNotFoundException {
+ Tracer.trace();
AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
if(node == null) {
throw new UiObjectNotFoundException(getSelector().toString());
@@ -190,6 +194,7 @@ public class UiObject {
* @since API Level 16
*/
public boolean swipeUp(int steps) throws UiObjectNotFoundException {
+ Tracer.trace(steps);
Rect rect = getVisibleBounds();
if(rect.height() <= SWIPE_MARGIN_LIMIT * 2)
return false; // too small to swipe
@@ -213,6 +218,7 @@ public class UiObject {
* @since API Level 16
*/
public boolean swipeDown(int steps) throws UiObjectNotFoundException {
+ Tracer.trace(steps);
Rect rect = getVisibleBounds();
if(rect.height() <= SWIPE_MARGIN_LIMIT * 2)
return false; // too small to swipe
@@ -236,6 +242,7 @@ public class UiObject {
* @since API Level 16
*/
public boolean swipeLeft(int steps) throws UiObjectNotFoundException {
+ Tracer.trace(steps);
Rect rect = getVisibleBounds();
if(rect.width() <= SWIPE_MARGIN_LIMIT * 2)
return false; // too small to swipe
@@ -258,6 +265,7 @@ public class UiObject {
* @since API Level 16
*/
public boolean swipeRight(int steps) throws UiObjectNotFoundException {
+ Tracer.trace(steps);
Rect rect = getVisibleBounds();
if(rect.width() <= SWIPE_MARGIN_LIMIT * 2)
return false; // too small to swipe
@@ -323,6 +331,7 @@ public class UiObject {
* @since API Level 16
*/
public boolean click() throws UiObjectNotFoundException {
+ Tracer.trace();
AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
if(node == null) {
throw new UiObjectNotFoundException(getSelector().toString());
@@ -343,6 +352,7 @@ public class UiObject {
* @since API Level 16
*/
public boolean clickAndWaitForNewWindow() throws UiObjectNotFoundException {
+ Tracer.trace();
return clickAndWaitForNewWindow(WAIT_FOR_WINDOW_TMEOUT);
}
@@ -362,6 +372,7 @@ public class UiObject {
* @since API Level 16
*/
public boolean clickAndWaitForNewWindow(long timeout) throws UiObjectNotFoundException {
+ Tracer.trace(timeout);
AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
if(node == null) {
throw new UiObjectNotFoundException(getSelector().toString());
@@ -379,6 +390,7 @@ public class UiObject {
* @since API Level 16
*/
public boolean clickTopLeft() throws UiObjectNotFoundException {
+ Tracer.trace();
AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
if(node == null) {
throw new UiObjectNotFoundException(getSelector().toString());
@@ -395,6 +407,7 @@ public class UiObject {
* @since API Level 16
*/
public boolean longClickBottomRight() throws UiObjectNotFoundException {
+ Tracer.trace();
AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
if(node == null) {
throw new UiObjectNotFoundException(getSelector().toString());
@@ -411,6 +424,7 @@ public class UiObject {
* @since API Level 16
*/
public boolean clickBottomRight() throws UiObjectNotFoundException {
+ Tracer.trace();
AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
if(node == null) {
throw new UiObjectNotFoundException(getSelector().toString());
@@ -427,6 +441,7 @@ public class UiObject {
* @since API Level 16
*/
public boolean longClick() throws UiObjectNotFoundException {
+ Tracer.trace();
AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
if(node == null) {
throw new UiObjectNotFoundException(getSelector().toString());
@@ -443,6 +458,7 @@ public class UiObject {
* @since API Level 16
*/
public boolean longClickTopLeft() throws UiObjectNotFoundException {
+ Tracer.trace();
AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
if(node == null) {
throw new UiObjectNotFoundException(getSelector().toString());
@@ -459,6 +475,7 @@ public class UiObject {
* @since API Level 16
*/
public String getText() throws UiObjectNotFoundException {
+ Tracer.trace();
AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
if(node == null) {
throw new UiObjectNotFoundException(getSelector().toString());
@@ -476,6 +493,7 @@ public class UiObject {
* @since API Level 16
*/
public String getContentDescription() throws UiObjectNotFoundException {
+ Tracer.trace();
AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
if(node == null) {
throw new UiObjectNotFoundException(getSelector().toString());
@@ -501,6 +519,7 @@ public class UiObject {
* @since API Level 16
*/
public boolean setText(String text) throws UiObjectNotFoundException {
+ Tracer.trace(text);
clearTextField();
return getInteractionController().sendText(text);
}
@@ -525,6 +544,7 @@ public class UiObject {
* @since API Level 16
*/
public void clearTextField() throws UiObjectNotFoundException {
+ Tracer.trace();
// long click left + center
AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
if(node == null) {
@@ -549,6 +569,7 @@ public class UiObject {
* @since API Level 16
*/
public boolean isChecked() throws UiObjectNotFoundException {
+ Tracer.trace();
AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
if(node == null) {
throw new UiObjectNotFoundException(getSelector().toString());
@@ -564,6 +585,7 @@ public class UiObject {
* @since API Level 16
*/
public boolean isSelected() throws UiObjectNotFoundException {
+ Tracer.trace();
AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
if(node == null) {
throw new UiObjectNotFoundException(getSelector().toString());
@@ -579,6 +601,7 @@ public class UiObject {
* @since API Level 16
*/
public boolean isCheckable() throws UiObjectNotFoundException {
+ Tracer.trace();
AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
if(node == null) {
throw new UiObjectNotFoundException(getSelector().toString());
@@ -594,6 +617,7 @@ public class UiObject {
* @since API Level 16
*/
public boolean isEnabled() throws UiObjectNotFoundException {
+ Tracer.trace();
AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
if(node == null) {
throw new UiObjectNotFoundException(getSelector().toString());
@@ -609,6 +633,7 @@ public class UiObject {
* @since API Level 16
*/
public boolean isClickable() throws UiObjectNotFoundException {
+ Tracer.trace();
AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
if(node == null) {
throw new UiObjectNotFoundException(getSelector().toString());
@@ -624,6 +649,7 @@ public class UiObject {
* @since API Level 16
*/
public boolean isFocused() throws UiObjectNotFoundException {
+ Tracer.trace();
AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
if(node == null) {
throw new UiObjectNotFoundException(getSelector().toString());
@@ -639,6 +665,7 @@ public class UiObject {
* @since API Level 16
*/
public boolean isFocusable() throws UiObjectNotFoundException {
+ Tracer.trace();
AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
if(node == null) {
throw new UiObjectNotFoundException(getSelector().toString());
@@ -654,6 +681,7 @@ public class UiObject {
* @since API Level 16
*/
public boolean isScrollable() throws UiObjectNotFoundException {
+ Tracer.trace();
AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
if(node == null) {
throw new UiObjectNotFoundException(getSelector().toString());
@@ -669,6 +697,7 @@ public class UiObject {
* @since API Level 16
*/
public boolean isLongClickable() throws UiObjectNotFoundException {
+ Tracer.trace();
AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
if(node == null) {
throw new UiObjectNotFoundException(getSelector().toString());
@@ -684,6 +713,7 @@ public class UiObject {
* @since API Level 16
*/
public String getPackageName() throws UiObjectNotFoundException {
+ Tracer.trace();
AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
if(node == null) {
throw new UiObjectNotFoundException(getSelector().toString());
@@ -703,6 +733,7 @@ public class UiObject {
* @since API Level 17
*/
public Rect getVisibleBounds() throws UiObjectNotFoundException {
+ Tracer.trace();
AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
if(node == null) {
throw new UiObjectNotFoundException(getSelector().toString());
@@ -718,6 +749,7 @@ public class UiObject {
* @since API Level 16
*/
public Rect getBounds() throws UiObjectNotFoundException {
+ Tracer.trace();
AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
if(node == null) {
throw new UiObjectNotFoundException(getSelector().toString());
@@ -740,6 +772,7 @@ public class UiObject {
* @since API Level 16
*/
public boolean waitForExists(long timeout) {
+ Tracer.trace(timeout);
if(findAccessibilityNodeInfo(timeout) != null) {
return true;
}
@@ -765,6 +798,7 @@ public class UiObject {
* @since API Level 16
*/
public boolean waitUntilGone(long timeout) {
+ Tracer.trace(timeout);
long startMills = SystemClock.uptimeMillis();
long currentMills = 0;
while (currentMills <= timeout) {
@@ -789,6 +823,7 @@ public class UiObject {
* @since API Level 16
*/
public boolean exists() {
+ Tracer.trace();
return waitForExists(0);
}
diff --git a/library/src/com/android/uiautomator/core/UiScrollable.java b/library/src/com/android/uiautomator/core/UiScrollable.java
index 710c559..c128ac2 100644
--- a/library/src/com/android/uiautomator/core/UiScrollable.java
+++ b/library/src/com/android/uiautomator/core/UiScrollable.java
@@ -63,6 +63,7 @@ public class UiScrollable extends UiCollection {
* @since API Level 16
*/
public UiScrollable setAsVerticalList() {
+ Tracer.trace();
mIsVerticalList = true;
return this;
}
@@ -73,6 +74,7 @@ public class UiScrollable extends UiCollection {
* @since API Level 16
*/
public UiScrollable setAsHorizontalList() {
+ Tracer.trace();
mIsVerticalList = false;
return this;
}
@@ -110,6 +112,7 @@ public class UiScrollable extends UiCollection {
@Override
public UiObject getChildByDescription(UiSelector childPattern, String text)
throws UiObjectNotFoundException {
+ Tracer.trace(childPattern, text);
return getChildByDescription(childPattern, text, true);
}
@@ -125,6 +128,7 @@ public class UiScrollable extends UiCollection {
*/
public UiObject getChildByDescription(UiSelector childPattern, String text,
boolean allowScrollSearch) throws UiObjectNotFoundException {
+ Tracer.trace(childPattern, text, allowScrollSearch);
if (text != null) {
if (allowScrollSearch) {
scrollIntoView(new UiSelector().descriptionContains(text));
@@ -148,6 +152,7 @@ public class UiScrollable extends UiCollection {
@Override
public UiObject getChildByInstance(UiSelector childPattern, int instance)
throws UiObjectNotFoundException {
+ Tracer.trace(childPattern, instance);
UiSelector patternSelector = UiSelector.patternBuilder(getSelector(),
UiSelector.patternBuilder(childPattern).instance(instance));
return new UiObject(patternSelector);
@@ -172,6 +177,7 @@ public class UiScrollable extends UiCollection {
@Override
public UiObject getChildByText(UiSelector childPattern, String text)
throws UiObjectNotFoundException {
+ Tracer.trace(childPattern, text);
return getChildByText(childPattern, text, true);
}
@@ -187,7 +193,7 @@ public class UiScrollable extends UiCollection {
*/
public UiObject getChildByText(UiSelector childPattern, String text, boolean allowScrollSearch)
throws UiObjectNotFoundException {
-
+ Tracer.trace(childPattern, text, allowScrollSearch);
if (text != null) {
if (allowScrollSearch) {
scrollIntoView(new UiSelector().text(text));
@@ -206,6 +212,7 @@ public class UiScrollable extends UiCollection {
* @since API Level 16
*/
public boolean scrollDescriptionIntoView(String text) throws UiObjectNotFoundException {
+ Tracer.trace(text);
return scrollIntoView(new UiSelector().description(text));
}
@@ -218,6 +225,7 @@ public class UiScrollable extends UiCollection {
* @since API Level 16
*/
public boolean scrollIntoView(UiObject obj) throws UiObjectNotFoundException {
+ Tracer.trace(obj.getSelector());
return scrollIntoView(obj.getSelector());
}
@@ -230,6 +238,7 @@ public class UiScrollable extends UiCollection {
* @since API Level 16
*/
public boolean scrollIntoView(UiSelector selector) throws UiObjectNotFoundException {
+ Tracer.trace(selector);
// if we happen to be on top of the text we want then return here
if (exists(getSelector().childSelector(selector))) {
return (true);
@@ -261,6 +270,7 @@ public class UiScrollable extends UiCollection {
* @since API Level 16
*/
public boolean scrollTextIntoView(String text) throws UiObjectNotFoundException {
+ Tracer.trace(text);
return scrollIntoView(new UiSelector().text(text));
}
@@ -276,6 +286,7 @@ public class UiScrollable extends UiCollection {
* @since API Level 16
*/
public UiScrollable setMaxSearchSwipes(int swipes) {
+ Tracer.trace(swipes);
mMaxSearchSwipes = swipes;
return this;
}
@@ -291,6 +302,7 @@ public class UiScrollable extends UiCollection {
* @since API Level 16
*/
public int getMaxSearchSwipes() {
+ Tracer.trace();
return mMaxSearchSwipes;
}
@@ -301,6 +313,7 @@ public class UiScrollable extends UiCollection {
* @since API Level 16
*/
public boolean flingForward() throws UiObjectNotFoundException {
+ Tracer.trace();
return scrollForward(FLING_STEPS);
}
@@ -311,6 +324,7 @@ public class UiScrollable extends UiCollection {
* @since API Level 16
*/
public boolean scrollForward() throws UiObjectNotFoundException {
+ Tracer.trace();
return scrollForward(SCROLL_STEPS);
}
@@ -326,6 +340,7 @@ public class UiScrollable extends UiCollection {
* @since API Level 16
*/
public boolean scrollForward(int steps) throws UiObjectNotFoundException {
+ Tracer.trace(steps);
Log.d(LOG_TAG, "scrollForward() on selector = " + getSelector());
AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
if(node == null) {
@@ -367,6 +382,7 @@ public class UiScrollable extends UiCollection {
* @since API Level 16
*/
public boolean flingBackward() throws UiObjectNotFoundException {
+ Tracer.trace();
return scrollBackward(FLING_STEPS);
}
@@ -377,6 +393,7 @@ public class UiScrollable extends UiCollection {
* @since API Level 16
*/
public boolean scrollBackward() throws UiObjectNotFoundException {
+ Tracer.trace();
return scrollBackward(SCROLL_STEPS);
}
@@ -392,6 +409,7 @@ public class UiScrollable extends UiCollection {
* @since API Level 16
*/
public boolean scrollBackward(int steps) throws UiObjectNotFoundException {
+ Tracer.trace(steps);
Log.d(LOG_TAG, "scrollBackward() on selector = " + getSelector());
AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
if (node == null) {
@@ -438,6 +456,7 @@ public class UiScrollable extends UiCollection {
* @since API Level 16
*/
public boolean scrollToBeginning(int maxSwipes, int steps) throws UiObjectNotFoundException {
+ Tracer.trace(maxSwipes, steps);
Log.d(LOG_TAG, "scrollToBeginning() on selector = " + getSelector());
// protect against potential hanging and return after preset attempts
for(int x = 0; x < maxSwipes; x++) {
@@ -456,6 +475,7 @@ public class UiScrollable extends UiCollection {
* @since API Level 16
*/
public boolean scrollToBeginning(int maxSwipes) throws UiObjectNotFoundException {
+ Tracer.trace(maxSwipes);
return scrollToBeginning(maxSwipes, SCROLL_STEPS);
}
@@ -467,6 +487,7 @@ public class UiScrollable extends UiCollection {
* @since API Level 16
*/
public boolean flingToBeginning(int maxSwipes) throws UiObjectNotFoundException {
+ Tracer.trace(maxSwipes);
return scrollToBeginning(maxSwipes, FLING_STEPS);
}
@@ -480,6 +501,7 @@ public class UiScrollable extends UiCollection {
* @since API Level 16
*/
public boolean scrollToEnd(int maxSwipes, int steps) throws UiObjectNotFoundException {
+ Tracer.trace(maxSwipes, steps);
// protect against potential hanging and return after preset attempts
for(int x = 0; x < maxSwipes; x++) {
if(!scrollForward(steps)) {
@@ -497,6 +519,7 @@ public class UiScrollable extends UiCollection {
* @since API Level 16
*/
public boolean scrollToEnd(int maxSwipes) throws UiObjectNotFoundException {
+ Tracer.trace(maxSwipes);
return scrollToEnd(maxSwipes, SCROLL_STEPS);
}
@@ -508,6 +531,7 @@ public class UiScrollable extends UiCollection {
* @since API Level 16
*/
public boolean flingToEnd(int maxSwipes) throws UiObjectNotFoundException {
+ Tracer.trace(maxSwipes);
return scrollToEnd(maxSwipes, FLING_STEPS);
}
@@ -526,6 +550,7 @@ public class UiScrollable extends UiCollection {
* @since API Level 16
*/
public double getSwipeDeadZonePercentage() {
+ Tracer.trace();
return mSwipeDeadZonePercentage;
}
@@ -545,6 +570,7 @@ public class UiScrollable extends UiCollection {
* @since API Level 16
*/
public UiScrollable setSwipeDeadZonePercentage(double swipeDeadZonePercentage) {
+ Tracer.trace(swipeDeadZonePercentage);
mSwipeDeadZonePercentage = swipeDeadZonePercentage;
return this;
}
diff --git a/library/src/com/android/uiautomator/testrunner/UiAutomatorTestRunner.java b/library/src/com/android/uiautomator/testrunner/UiAutomatorTestRunner.java
index 02553e6..a376aa0 100644
--- a/library/src/com/android/uiautomator/testrunner/UiAutomatorTestRunner.java
+++ b/library/src/com/android/uiautomator/testrunner/UiAutomatorTestRunner.java
@@ -26,6 +26,8 @@ import android.os.IBinder;
import android.test.RepetitiveTest;
import android.util.Log;
+import com.android.uiautomator.core.Tracer;
+import com.android.uiautomator.core.Tracer.Mode;
import com.android.uiautomator.core.UiDevice;
import junit.framework.AssertionFailedError;
@@ -105,6 +107,21 @@ public class UiAutomatorTestRunner {
Bundle testRunOutput = new Bundle();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
PrintStream writer = new PrintStream(byteArrayOutputStream);
+
+ String traceType = mParams.getString("traceOutputMode");
+ if(traceType != null) {
+ Tracer.Mode mode = Tracer.Mode.valueOf(Tracer.Mode.class, traceType);
+ if (mode == Tracer.Mode.FILE || mode == Tracer.Mode.ALL) {
+ String filename = mParams.getString("traceLogFilename");
+ if (filename == null) {
+ throw new RuntimeException("Name of log file not specified. " +
+ "Please specify it using traceLogFilename parameter");
+ }
+ Tracer.getInstance().setOutputFilename(filename);
+ }
+ Tracer.getInstance().setOutputMode(mode);
+ }
+
try {
StringResultPrinter resultPrinter = new StringResultPrinter(writer);