diff options
author | Maxim Siniavine <siniavine@google.com> | 2012-11-28 17:50:09 -0800 |
---|---|---|
committer | Maxim Siniavine <siniavine@google.com> | 2012-11-30 16:14:50 -0800 |
commit | b04f3c526835516b098227342e741b7ce944c2f3 (patch) | |
tree | 007259843f17cd1424df21c062309f3efb07f30f /library | |
parent | 5478840ad89ace2474b9b4d3ab14b92d9e06bf47 (diff) | |
download | uiautomator-b04f3c526835516b098227342e741b7ce944c2f3.tar.gz |
Added tracing of UiAutomator calls.
Bug: 7565311
Change-Id: I4e426c68ce929c1dd0c8ee47b053045fa58e63aa
Diffstat (limited to 'library')
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); |