From c1acae26dc2e89ce7ce32fe60b559788d8d07d3e Mon Sep 17 00:00:00 2001 From: John Richardson Date: Mon, 18 Jul 2016 11:14:35 +0100 Subject: [PATCH 1/4] Minor code maintenance for base class BaseUiAutomation - Rename inconsistently named variables - Avoid long wrapped lines where possible - Fix whitespace around operators --- .../arm/wlauto/uiauto/BaseUiAutomation.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/wlauto/external/uiauto/src/com/arm/wlauto/uiauto/BaseUiAutomation.java b/wlauto/external/uiauto/src/com/arm/wlauto/uiauto/BaseUiAutomation.java index 144fdac5..fe36726f 100644 --- a/wlauto/external/uiauto/src/com/arm/wlauto/uiauto/BaseUiAutomation.java +++ b/wlauto/external/uiauto/src/com/arm/wlauto/uiauto/BaseUiAutomation.java @@ -86,11 +86,11 @@ public class BaseUiAutomation extends UiAutomatorTestCase { public boolean takeScreenshot(String name) { Bundle params = getParams(); - String png_dir = params.getString("workdir"); + String pngDir = params.getString("workdir"); try { - return getUiDevice().takeScreenshot(new File(png_dir, name + ".png")); - } catch(NoSuchMethodError e) { + return getUiDevice().takeScreenshot(new File(pngDir, name + ".png")); + } catch (NoSuchMethodError e) { return true; } } @@ -101,9 +101,9 @@ public class BaseUiAutomation extends UiAutomatorTestCase { public void waitText(String text, int second) throws UiObjectNotFoundException { UiSelector selector = new UiSelector(); - UiObject text_obj = new UiObject(selector.text(text) + UiObject textObj = new UiObject(selector.text(text) .className("android.widget.TextView")); - waitObject(text_obj, second); + waitObject(textObj, second); } public void waitObject(UiObject obj) throws UiObjectNotFoundException { @@ -111,7 +111,7 @@ public class BaseUiAutomation extends UiAutomatorTestCase { } public void waitObject(UiObject obj, int second) throws UiObjectNotFoundException { - if (! obj.waitForExists(second * 1000)){ + if (!obj.waitForExists(second * 1000)) { throw new UiObjectNotFoundException("UiObject is not found: " + obj.getSelector().toString()); } @@ -133,10 +133,10 @@ public class BaseUiAutomation extends UiAutomatorTestCase { long currentTime = System.currentTimeMillis(); boolean found = false; - while ((currentTime - startTime) < timeout){ + while ((currentTime - startTime) < timeout) { sleep(2); // poll every two seconds - while((line=reader.readLine())!=null) { + while ((line = reader.readLine()) != null) { if (line.contains(searchText)) { found = true; break; @@ -152,7 +152,8 @@ public class BaseUiAutomation extends UiAutomatorTestCase { process.destroy(); if ((currentTime - startTime) >= timeout) { - throw new TimeoutException("Timed out waiting for Logcat text \"%s\"".format(searchText)); + throw new TimeoutException(String.format("Timed out waiting for Logcat text \"%s\"", + searchText)); } } From 3a87bf2e4dbcffa417de1d3c19f3e93709c37bd0 Mon Sep 17 00:00:00 2001 From: John Richardson Date: Mon, 18 Jul 2016 11:18:34 +0100 Subject: [PATCH 2/4] Add further convenience methods to BaseUiAutomation Convenience methods to make it easier to get UiObjects and perform UiDevice based gestures and operations. Upgrade API level for BaseUiAutomation from 17 -> 18 and update dependent workloads accordingly. --- wlauto/common/android/BaseUiAutomation.class | Bin 5965 -> 16894 bytes wlauto/external/uiauto/project.properties | 2 +- .../arm/wlauto/uiauto/BaseUiAutomation.java | 386 +++++++++++++++++- .../andebench/uiauto/project.properties | 2 +- .../androbench/uiauto/project.properties | 2 +- .../benchmarkpi/uiauto/project.properties | 2 +- .../caffeinemark/uiauto/project.properties | 2 +- .../cfbench/uiauto/project.properties | 2 +- .../facebook/uiauto/project.properties | 2 +- .../geekbench/uiauto/project.properties | 2 +- .../glbenchmark/uiauto/project.properties | 2 +- .../linpack/uiauto/project.properties | 2 +- .../peacekeeper/uiauto/project.properties | 2 +- .../quadrant/uiauto/project.properties | 2 +- .../real_linpack/uiauto/project.properties | 2 +- .../smartbench/uiauto/project.properties | 2 +- 16 files changed, 398 insertions(+), 16 deletions(-) diff --git a/wlauto/common/android/BaseUiAutomation.class b/wlauto/common/android/BaseUiAutomation.class index e2bf1737c46e8d30c254ff3b650fc067c651d7c8..1f6f8cdd24cbb603b1057fdc8ab3310e172646d9 100644 GIT binary patch literal 16894 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 F{{W0855xcf delta 2746 zcmZ`*33Oa#75@HtZ|1#u^V*V0XPU`6ZMJ463+WOtXrWXKsU$2dw4;GKO@@#*ozyh6 zRIDS=YDy7>N0n|Yr8J6BsDU?W(12)v&YbhF_kQ=@ z@80{*J9CG4u0D10%+YZGjiS;(3wFujvj#55=XBg}U^n*2&0Y=rH0(E^O9>qh=y*`h z4;j$#uz>@3M8iQT`KX3N2J)~=!=Ri$uj31He#}5Vz9=PziWLY#T^pM8^>gM>UKZIEFC;RTwu=i3tgJT&})q;AuP~Z@;GD>pD(o z_=Y_Crh$4K*6}R^C-H3!&l*@LBRGlY3_OqT7`X9yoRZ5I3|xxu%Hexb=KC66)N$It zBI(pZ{6NDG<@_TJ_vrYsfuG=~QurkeKhyAY4KE9ri#z(edy`l6c6BBCxNS)$6Maj1 z;u|+6Hd@D(9WK#gU8dgY(KaNzR(D#b)t#o^pSY!eb?=(B0@c=S#bN8EJg>FOeN55t zigmT8)g_h<@AI4z)+T>tMJw7&tiUf!{1U$s&^Gt>t?TUW)9`B(zrk-!yo%phFZd%R z_&xq0pw`oli9g~s6Rr4@hSyE3!k?{z{IDmEgtRD%%B-?t-Ad#~8awcYz^r((v#+p1-02f5$)M`5F`d#G4xaW#ZrX zkASzQx2q%GAFk^#@n8H;!xT^a-Of$uFF~e#I%&HXUi9AjCO_48Vnj#>ArkEuP zOc4@=rYI7{rYI4m*1AA*i6|36!mW*OiZ}GclU)rh$^HbhW{R+wZ9N^Rn<3_qI#F(l z3MpA-y)vze0T)@%6okcm?y9Y(&~$4Ed{$b*em&a!siiR0S^$UQ4y0v-eG^0X|%Zl~fe0gO~)M%zhejbDkfup|sZeRU7U$ zu05mBVs)d?V`+G!HVnUygAFr{X*Q-0HLL0nAYXV|5IBraxdK zn8vJTH4;*bZ4^w=?F5@b7j53ecdHvk?2S2CmyMQqS^MRa_T`z(f{R*3xRKflXNk~F zA9B50OQg4tJI4n@#t$Tk(EAQ?_Yhh9AM*N1x7J*XH(VQez< z{vu|bJTUT-bNMfwnBu^nA_CBdY1oKT^fQB-96!Pt+-&>}$oL`aJrjR!tVw#GsmB)T zNsoM4kIII)&FzUTIR<>GtOZ}zLZ;4LMBKnG%W!xhXW2x?2*Uo^Hs-9H3L;ZGTj9mW z9Oz8D8I#E8{QNNW`xvEzCzi5aOyZ>mMo@l%Dp4zl+RVPGBzaY_kjF-K8gtXAY1X8S zS84JEy+Jh*zHcAqMcg(bAFxPUL|s=L1OH?6j3U+=R^} z$4!H`aHZ1p7XCiM2d0AMcPl>1Z-pAR_g3C|=}bH6PVrI7CHUiX+bIMQB7#O^TmAD} zYe%qvExC}b+0?AczPzB>9U_Al?oMHWT)gW6)T&LMfXCT9g5r=GatBCS^9U{)!Mkm| zXXQ4J7(Dy(AWBEE=v;jh%J5%&uCgzgpc-Gm#bdbC#**WB?-TSuaUPfmpBUgF?j9xy z2l&Zy5CwQNg}FGy4}(Fz?uW?MW30jQ$#jldvv^Z{6SRt-`7^> z9>e)DB|3to%VR3(Te_T8LJAkf>e9GmLRnN29Y26*v}B%*%W9qRAl&Lu?!f`LT|?Ux zlJ=r9)r0lS81N*6dy0(M{rq1`BZ4EChogL 100) ? 100 : percent; + float percentage = nPercent / 100f; + + Rect rect = view.getVisibleBounds(); + + if (rect.width() <= FINGER_TOUCH_HALF_WIDTH * 2) { + throw new IllegalStateException("Object width is too small for operation"); + } + + // Start at the top-center and bottom-center of the control + Point startPoint1 = new Point(rect.centerX(), rect.centerY() + + (int) ((rect.height() / 2) * percentage)); + Point startPoint2 = new Point(rect.centerX(), rect.centerY() + - (int) ((rect.height() / 2) * percentage)); + + // End at the same point at the center of the control + Point endPoint1 = new Point(rect.centerX(), rect.centerY() + FINGER_TOUCH_HALF_WIDTH); + Point endPoint2 = new Point(rect.centerX(), rect.centerY() - FINGER_TOUCH_HALF_WIDTH); + + view.performTwoPointerGesture(startPoint1, startPoint2, endPoint1, endPoint2, steps); + } + + public void uiObjectVertPinchOut(UiObject view, int steps, int percent) throws Exception { + final int FINGER_TOUCH_HALF_WIDTH = 20; + + // Make value between 1 and 100 + int nPercent = (percent < 0) ? 1 : (percent > 100) ? 100 : percent; + float percentage = nPercent / 100f; + + Rect rect = view.getVisibleBounds(); + + if (rect.width() <= FINGER_TOUCH_HALF_WIDTH * 2) { + throw new IllegalStateException("Object width is too small for operation"); + } + + // Start from the same point at the center of the control + Point startPoint1 = new Point(rect.centerX(), rect.centerY() + FINGER_TOUCH_HALF_WIDTH); + Point startPoint2 = new Point(rect.centerX(), rect.centerY() - FINGER_TOUCH_HALF_WIDTH); + + // End at the top-center and bottom-center of the control + Point endPoint1 = new Point(rect.centerX(), rect.centerY() + + (int) ((rect.height() / 2) * percentage)); + Point endPoint2 = new Point(rect.centerX(), rect.centerY() + - (int) ((rect.height() / 2) * percentage)); + + view.performTwoPointerGesture(startPoint1, startPoint2, endPoint1, endPoint2, steps); + } + + public void setScreenOrientation(ScreenOrientation orientation) throws Exception { + switch (orientation) { + case RIGHT: + getUiDevice().setOrientationRight(); + break; + case NATURAL: + getUiDevice().setOrientationNatural(); + break; + case LEFT: + getUiDevice().setOrientationLeft(); + break; + default: + throw new Exception("No orientation specified"); + } + } + + public void unsetScreenOrientation() throws Exception { + getUiDevice().unfreezeRotation(); + } + + public void uiObjectPerformLongClick(UiObject view, int steps) throws Exception { + Rect rect = view.getBounds(); + UiDevice.getInstance().swipe(rect.centerX(), rect.centerY(), + rect.centerX(), rect.centerY(), steps); + } + + public void uiDeviceSwipeVertical(int startY, int endY, int xCoordinate, int steps) { + getUiDevice().swipe(startY, xCoordinate, endY, xCoordinate, steps); + } + + public void uiDeviceSwipeHorizontal(int startX, int endX, int yCoordinate, int steps) { + getUiDevice().swipe(startX, yCoordinate, endX, yCoordinate, steps); + } + + public void uiObjectPinch(UiObject view, PinchType direction, int steps, + int percent) throws Exception { + if (direction.equals(PinchType.IN)) { + view.pinchIn(percent, steps); + } else if (direction.equals(PinchType.OUT)) { + view.pinchOut(percent, steps); + } + } + + public void uiObjectVertPinch(UiObject view, PinchType direction, + int steps, int percent) throws Exception { + if (direction.equals(PinchType.IN)) { + uiObjectVertPinchIn(view, steps, percent); + } else if (direction.equals(PinchType.OUT)) { + uiObjectVertPinchOut(view, steps, percent); + } + } + + public void repeatClickUiObject(UiObject view, int repeatCount, int intervalInMillis) throws Exception { + int repeatInterval = intervalInMillis > CLICK_REPEAT_INTERVAL_MINIMUM + ? intervalInMillis : CLICK_REPEAT_INTERVAL_DEFAULT; + if (repeatCount < 1 || !view.isClickable()) { + return; + } + + for (int i = 0; i < repeatCount; ++i) { + view.click(); + SystemClock.sleep(repeatInterval); // in order to register as separate click + } + } + + public UiObject clickUiObject(FindByCriteria criteria, String matching) throws Exception { + return clickUiObject(criteria, matching, null, false); + } + + public UiObject clickUiObject(FindByCriteria criteria, String matching, boolean wait) throws Exception { + return clickUiObject(criteria, matching, null, wait); + } + + public UiObject clickUiObject(FindByCriteria criteria, String matching, String clazz) throws Exception { + return clickUiObject(criteria, matching, clazz, false); + } + + public UiObject clickUiObject(FindByCriteria criteria, String matching, String clazz, boolean wait) throws Exception { + UiObject view; + + switch (criteria) { + case BY_ID: + view = (clazz == null) + ? getUiObjectByResourceId(matching) : getUiObjectByResourceId(matching, clazz); + break; + case BY_DESC: + view = (clazz == null) + ? getUiObjectByDescription(matching) : getUiObjectByDescription(matching, clazz); + break; + case BY_TEXT: + default: + view = (clazz == null) + ? getUiObjectByText(matching) : getUiObjectByText(matching, clazz); + break; + } + + if (wait) { + view.clickAndWaitForNewWindow(); + } else { + view.click(); + } + return view; + } + + public UiObject getUiObjectByResourceId(String resourceId, String className) throws Exception { + return getUiObjectByResourceId(resourceId, className, uiAutoTimeout); + } + + public UiObject getUiObjectByResourceId(String resourceId, String className, long timeout) throws Exception { + UiObject object = new UiObject(new UiSelector().resourceId(resourceId) + .className(className)); + if (!object.waitForExists(timeout)) { + throw new UiObjectNotFoundException(String.format("Could not find \"%s\" \"%s\"", + resourceId, className)); + } + return object; + } + + public UiObject getUiObjectByResourceId(String id) throws Exception { + UiObject object = new UiObject(new UiSelector().resourceId(id)); + + if (!object.waitForExists(uiAutoTimeout)) { + throw new UiObjectNotFoundException("Could not find view with resource ID: " + id); + } + return object; + } + + public UiObject getUiObjectByDescription(String description, String className) throws Exception { + return getUiObjectByDescription(description, className, uiAutoTimeout); + } + + public UiObject getUiObjectByDescription(String description, String className, long timeout) throws Exception { + UiObject object = new UiObject(new UiSelector().descriptionContains(description) + .className(className)); + if (!object.waitForExists(timeout)) { + throw new UiObjectNotFoundException(String.format("Could not find \"%s\" \"%s\"", + description, className)); + } + return object; + } + + public UiObject getUiObjectByDescription(String desc) throws Exception { + UiObject object = new UiObject(new UiSelector().descriptionContains(desc)); + + if (!object.waitForExists(uiAutoTimeout)) { + throw new UiObjectNotFoundException("Could not find view with description: " + desc); + } + return object; + } + + public UiObject getUiObjectByText(String text, String className) throws Exception { + return getUiObjectByText(text, className, uiAutoTimeout); + } + + public UiObject getUiObjectByText(String text, String className, long timeout) throws Exception { + UiObject object = new UiObject(new UiSelector().textContains(text) + .className(className)); + if (!object.waitForExists(timeout)) { + throw new UiObjectNotFoundException(String.format("Could not find \"%s\" \"%s\"", + text, className)); + } + return object; + } + + public UiObject getUiObjectByText(String text) throws Exception { + UiObject object = new UiObject(new UiSelector().textContains(text)); + + if (!object.waitForExists(uiAutoTimeout)) { + throw new UiObjectNotFoundException("Could not find view with text: " + text); + } + return object; + } +} diff --git a/wlauto/workloads/andebench/uiauto/project.properties b/wlauto/workloads/andebench/uiauto/project.properties index a3ee5ab6..ce39f2d0 100644 --- a/wlauto/workloads/andebench/uiauto/project.properties +++ b/wlauto/workloads/andebench/uiauto/project.properties @@ -11,4 +11,4 @@ #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt # Project target. -target=android-17 +target=android-18 diff --git a/wlauto/workloads/androbench/uiauto/project.properties b/wlauto/workloads/androbench/uiauto/project.properties index a3ee5ab6..ce39f2d0 100644 --- a/wlauto/workloads/androbench/uiauto/project.properties +++ b/wlauto/workloads/androbench/uiauto/project.properties @@ -11,4 +11,4 @@ #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt # Project target. -target=android-17 +target=android-18 diff --git a/wlauto/workloads/benchmarkpi/uiauto/project.properties b/wlauto/workloads/benchmarkpi/uiauto/project.properties index a3ee5ab6..ce39f2d0 100644 --- a/wlauto/workloads/benchmarkpi/uiauto/project.properties +++ b/wlauto/workloads/benchmarkpi/uiauto/project.properties @@ -11,4 +11,4 @@ #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt # Project target. -target=android-17 +target=android-18 diff --git a/wlauto/workloads/caffeinemark/uiauto/project.properties b/wlauto/workloads/caffeinemark/uiauto/project.properties index a3ee5ab6..ce39f2d0 100644 --- a/wlauto/workloads/caffeinemark/uiauto/project.properties +++ b/wlauto/workloads/caffeinemark/uiauto/project.properties @@ -11,4 +11,4 @@ #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt # Project target. -target=android-17 +target=android-18 diff --git a/wlauto/workloads/cfbench/uiauto/project.properties b/wlauto/workloads/cfbench/uiauto/project.properties index a3ee5ab6..ce39f2d0 100644 --- a/wlauto/workloads/cfbench/uiauto/project.properties +++ b/wlauto/workloads/cfbench/uiauto/project.properties @@ -11,4 +11,4 @@ #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt # Project target. -target=android-17 +target=android-18 diff --git a/wlauto/workloads/facebook/uiauto/project.properties b/wlauto/workloads/facebook/uiauto/project.properties index a3ee5ab6..ce39f2d0 100644 --- a/wlauto/workloads/facebook/uiauto/project.properties +++ b/wlauto/workloads/facebook/uiauto/project.properties @@ -11,4 +11,4 @@ #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt # Project target. -target=android-17 +target=android-18 diff --git a/wlauto/workloads/geekbench/uiauto/project.properties b/wlauto/workloads/geekbench/uiauto/project.properties index a3ee5ab6..ce39f2d0 100644 --- a/wlauto/workloads/geekbench/uiauto/project.properties +++ b/wlauto/workloads/geekbench/uiauto/project.properties @@ -11,4 +11,4 @@ #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt # Project target. -target=android-17 +target=android-18 diff --git a/wlauto/workloads/glbenchmark/uiauto/project.properties b/wlauto/workloads/glbenchmark/uiauto/project.properties index a3ee5ab6..ce39f2d0 100644 --- a/wlauto/workloads/glbenchmark/uiauto/project.properties +++ b/wlauto/workloads/glbenchmark/uiauto/project.properties @@ -11,4 +11,4 @@ #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt # Project target. -target=android-17 +target=android-18 diff --git a/wlauto/workloads/linpack/uiauto/project.properties b/wlauto/workloads/linpack/uiauto/project.properties index a3ee5ab6..ce39f2d0 100644 --- a/wlauto/workloads/linpack/uiauto/project.properties +++ b/wlauto/workloads/linpack/uiauto/project.properties @@ -11,4 +11,4 @@ #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt # Project target. -target=android-17 +target=android-18 diff --git a/wlauto/workloads/peacekeeper/uiauto/project.properties b/wlauto/workloads/peacekeeper/uiauto/project.properties index a3ee5ab6..ce39f2d0 100644 --- a/wlauto/workloads/peacekeeper/uiauto/project.properties +++ b/wlauto/workloads/peacekeeper/uiauto/project.properties @@ -11,4 +11,4 @@ #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt # Project target. -target=android-17 +target=android-18 diff --git a/wlauto/workloads/quadrant/uiauto/project.properties b/wlauto/workloads/quadrant/uiauto/project.properties index a3ee5ab6..ce39f2d0 100644 --- a/wlauto/workloads/quadrant/uiauto/project.properties +++ b/wlauto/workloads/quadrant/uiauto/project.properties @@ -11,4 +11,4 @@ #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt # Project target. -target=android-17 +target=android-18 diff --git a/wlauto/workloads/real_linpack/uiauto/project.properties b/wlauto/workloads/real_linpack/uiauto/project.properties index a3ee5ab6..ce39f2d0 100644 --- a/wlauto/workloads/real_linpack/uiauto/project.properties +++ b/wlauto/workloads/real_linpack/uiauto/project.properties @@ -11,4 +11,4 @@ #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt # Project target. -target=android-17 +target=android-18 diff --git a/wlauto/workloads/smartbench/uiauto/project.properties b/wlauto/workloads/smartbench/uiauto/project.properties index a3ee5ab6..ce39f2d0 100644 --- a/wlauto/workloads/smartbench/uiauto/project.properties +++ b/wlauto/workloads/smartbench/uiauto/project.properties @@ -11,4 +11,4 @@ #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt # Project target. -target=android-17 +target=android-18 From 6711847cbf37d62cd1296fa7b1a6d530d539bd97 Mon Sep 17 00:00:00 2001 From: John Richardson Date: Tue, 19 Jul 2016 14:21:36 +0100 Subject: [PATCH 3/4] Add UxPerfUiAutomation class UxPerfUiAutomation contains methods specific to UX performance testing. --- .../common/android/BaseUiAutomation$1.class | Bin 0 -> 1786 bytes .../BaseUiAutomation$ActionLogger.class | Bin 0 -> 1571 bytes .../android/BaseUiAutomation$Direction.class | Bin 0 -> 1329 bytes .../BaseUiAutomation$FindByCriteria.class | Bin 0 -> 1278 bytes .../android/BaseUiAutomation$PinchType.class | Bin 0 -> 1233 bytes .../BaseUiAutomation$ScreenOrientation.class | Bin 0 -> 1296 bytes .../common/android/UxPerfUiAutomation$1.class | Bin 0 -> 917 bytes .../UxPerfUiAutomation$ActionLogger.class | Bin 0 -> 1584 bytes .../UxPerfUiAutomation$Direction.class | Bin 0 -> 1345 bytes ...UxPerfUiAutomation$GestureTestParams.class | Bin 0 -> 1554 bytes .../UxPerfUiAutomation$GestureType.class | Bin 0 -> 1287 bytes .../UxPerfUiAutomation$PinchType.class | Bin 0 -> 1249 bytes .../UxPerfUiAutomation$SurfaceLogger.class | Bin 0 -> 1429 bytes .../android/UxPerfUiAutomation$Timer.class | Bin 0 -> 983 bytes .../UxPerfUiAutomation$UxPerfLogger.class | Bin 0 -> 800 bytes .../common/android/UxPerfUiAutomation.class | Bin 0 -> 777 bytes wlauto/external/uiauto/build.sh | 8 ++- .../arm/wlauto/uiauto/UxPerfUiAutomation.java | 49 ++++++++++++++++++ 18 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 wlauto/common/android/BaseUiAutomation$1.class create mode 100644 wlauto/common/android/BaseUiAutomation$ActionLogger.class create mode 100644 wlauto/common/android/BaseUiAutomation$Direction.class create mode 100644 wlauto/common/android/BaseUiAutomation$FindByCriteria.class create mode 100644 wlauto/common/android/BaseUiAutomation$PinchType.class create mode 100644 wlauto/common/android/BaseUiAutomation$ScreenOrientation.class create mode 100644 wlauto/common/android/UxPerfUiAutomation$1.class create mode 100644 wlauto/common/android/UxPerfUiAutomation$ActionLogger.class create mode 100644 wlauto/common/android/UxPerfUiAutomation$Direction.class create mode 100644 wlauto/common/android/UxPerfUiAutomation$GestureTestParams.class create mode 100644 wlauto/common/android/UxPerfUiAutomation$GestureType.class create mode 100644 wlauto/common/android/UxPerfUiAutomation$PinchType.class create mode 100644 wlauto/common/android/UxPerfUiAutomation$SurfaceLogger.class create mode 100644 wlauto/common/android/UxPerfUiAutomation$Timer.class create mode 100644 wlauto/common/android/UxPerfUiAutomation$UxPerfLogger.class create mode 100644 wlauto/common/android/UxPerfUiAutomation.class create mode 100644 wlauto/external/uiauto/src/com/arm/wlauto/uiauto/UxPerfUiAutomation.java diff --git a/wlauto/common/android/BaseUiAutomation$1.class b/wlauto/common/android/BaseUiAutomation$1.class new file mode 100644 index 0000000000000000000000000000000000000000..fea7e68c5eebaa1773fadd5986d9762a78ca104e GIT binary patch literal 1786 zcmbVNTTc@~7(LS#mev*OUA&7cg0~8YB6y+ng4J!Sv_*{?vuroAy6l>5tMSc5-%T{} z(L`T-GEq#3F-GI7|HHpwJhNCO$U`yN{myr0=6pNfd^5A(e}4J`U;tNQ*pJ>QHse5) zUZ14>3JywgNSebj9KlgJAQ7c^OwxYo4M;N>!w`{6Oyub2q&c(k>->%qYB0p zoDmq)N)PP1Ih!}`X{I-)8UCF1z%d$iPixo#Oc*t*Vkg-$XVh)a)zY?anbH#oUC#-m zRv)5d`j+JuecN*DK~RD5)kn$NZgt{e%D3y5ZyN#~r%lIp?fO}Pa9{tlKqTc=ErBlG zcCA8VZpQM<#*D+3^}1&o&a^=p`R~S9$!qwgm9^!FP0Q0hddIlWZ**l`)A4GydpmE{ zXT7R|vkK0sNMb_8c66&qA*~{VtiU!YNjQdkJ5lgT4RbbYTTV6O`<}02QVu(hw1S*e zUsSP8s&(X56r?F4ui%pGn^Lh=_LWdpQIV!7jV{eJ$^w0?T!NKLELJYDSh>XV6tn>W zHRrmPpK^>^&8i8+|4y+ubBARW=xr^Sz;J5=wdG~%T;9-r-*6goHluz0*Y*F3IwsII zaqU(vEpYO`BPgCyY9>pSk#zA& zfzvNkbUnU@x8D|Crs4Qjd2RqA&sKmuq3q#RfgRXM+(nW^m>%%$BgB`_u@4B%%O|{m zNTm1%k&6LsCvE#n71H*Aendy0iq3;iYyoSUDpHlSsUlrV+EjH0s;+tPi7#MXQ$@O- zw5cN9K)R?325%%)e$Rxxn@BqX`W|Z##YQII#>Drbmv{h!7$%;>1>}hZT*D3GP0Zph zv4KZ;Onic;ct(7Vmv}{djkkD5e2>rgN(^B)m#C9V1@QwX#0xlL1bf&P;v2@^!0X{x ife19((|mjAZzdw%!C#k#@$qi{q%>S04iPVpcmDzs$Kj9w literal 0 HcmV?d00001 diff --git a/wlauto/common/android/BaseUiAutomation$ActionLogger.class b/wlauto/common/android/BaseUiAutomation$ActionLogger.class new file mode 100644 index 0000000000000000000000000000000000000000..53c3882847eab403cf217a22bf96479a6cf44ffb GIT binary patch literal 1571 zcma)6>r&G|6#lkcQexy%K)j%$0=DH65b;(-DT0C(5NJVVWVX#t4U;CFBvpA7AHx~B zjL!G~K9u9xP$<&Q&_A0!XU~4$cP`mKe}Dc4Fpf+MA{bWDi4nOQ4MSHkhH(}5Fd;qn z!)xo&Ygudn&8 zY3CDkENq^&gu%exGc^03Os{{8A)3lL1)aME{fos*zN44S;FIK@NSia{DRAF(Y(i+B zGHuhJW>~5FQnip2w++`Z4c+ndWXU!xk%(m&8fP6tFtnviTP&3dS>dirwK5kc$E^%^ zO=*{%jZ!^>Hj|UmQcgZETrwAITe!0p_dG$TlXYg+e;S3-REyl@1>p;qKssyaWoYtz z?)nT3(LlTBJ4L!T_cbSqQjn)$mh3gBuMZ;ALYUn~YqZDR4Vb+NU6; z;SH9gds)K@-YQttu!eO?hR6(xNHa{-Hw*t&jG?`vVL7`aa#WbkitD5*3>{UyXK2X_ z|7b=T2BX#U603F*Pz65eIF{fxb)VYii4(KC7M2`*UMy0+3?sDys>sUaq_H@a<=U?2 zivmN~=C-qL7O1~835yK9(Zy=L6^O9!9H-h9tqznV#}z91rlr$h)89&3@7B;-B{dOS zpc2;CLENG@rjwqc2886~(JmxM3utebGZ~yP8q8~;tjH=-WFvih<3oF3@dJe7!+U6m zk3<^x(R7IBJt*G;l;jgHSecgZ%L$4BW(RvIs5UV!=+T2nD-4=qEOY z0SuPex9FaXipPII`G$c=_!nB<9Y7_!VILPF8ZC#oxR2I-v>l>-qoSpiuz(Lxu!R^t z20FAOw6YGWFENFJJkMq7IFE@FQy*u;u4NN5W&?v@6astu(mUIC(-rq{`~ni%kM^ZGD>|_I%^XQ$l64Vww!< z~&Z@p$%&zO6B zpXXif)$>*>aJ+gkdl9io)30%NhX;-*aa>37c!SC%|E|FhFFBsmT4EUfhlGvn4z;oM z#-Y^TI~y{Mwi39(BtPgPOhWi0%yidF3;rMFuX3 z8B7}};h_o=K6~DQF))Eg2BvV+zyO8~j0iI-OiGwB3^Vkc-iJ64O;yeHJ(4w;$#xue zYj=+ZnWRtsp9(QW9oyyD7dsYC?*44~V!1#sC`r#TUQP+o03qp?NYW*edWm!Zpl?ic zigqYS;4-d&;VOC82*szj!9F5#gy?(n7{PaQ{39%z6B0q8_ z|2nN0zp^li8!}Gar7XcvK$Lz!%}u8d5&s~=<79uAJKe4#BcvlMb8^ryjT{jRns;;% z@zuXTsV3FvA+!S|q@yd+>5`5SlTNpEdg9XQl}?{3oqp{rF+|%m(N(35E~^@AquZ*+ m+vv5bY8(C50ho|^wky$NS+{VTViC+>h)}021#=iIr+xzDT`|D` literal 0 HcmV?d00001 diff --git a/wlauto/common/android/BaseUiAutomation$FindByCriteria.class b/wlauto/common/android/BaseUiAutomation$FindByCriteria.class new file mode 100644 index 0000000000000000000000000000000000000000..97c948680f6592515c65f5d74f83a03a92e91336 GIT binary patch literal 1278 zcmbVL+iuf95IvhXapJlc(hKy$J(MJcnm|h{QdMo!REmh`gWHOT2ee5|*~+mc$1dWp zAeATrf%?oxA!fG`O@Id!TiWsL9M3s3JNx73m+t_ISWQDi-ar8hI;M0yG%$iixmYQT z8Wgl7;!z6Y(iT&gkoK{TCk%C4!7Qy0}(ob;0$Kf^PHgU@^;&4Gpy8_ ze#_!P%X;VXDDlRG ztAP_IPq-UNZO?M~!Lw*(|IlF=tqIS05w#jlu*VxN?dU4~?i+^Zx!N&5;g-w2Bg+m0 z;T@Io1Iy0T{3dq~ICYtLx@4NiTf~|9n}{K~COi?YGfe$MM>T)IpoMP*^&xGGBaeqs zK$+q>8Az=)-R{rTp41gDYOM`ez&ahNVf#_gbasU7)8wBcuqc~lVp7iHl!+xQ>v+nr zJkS7{n8k{TTexjv6d4oaa+yGeM)vYhWI;5`P1pBG(L^raQ|xZz*lC8Odj9_ekkZ~h zs~b@2$@hBz`sqs>MY>cYbR)HmX(<&TOKO!R!NAyzY>Gr6jjOl@hH3Kdkd=?o0sDm5 zIpQD4W0;{esbVF<&tk5NUqVdn8X2wd8Hsaf5+CcsU#A`O0)-p6sp8Z_LP^FV;`9P~ zVPWnJ$&V`BOLzIZPVz`P?kY?cDcnQ0E2K_^$Y=8_w0cI5pCNUMv~mnhIYY`Z6UrG* uej{AGLvvc!IvBF+i4KPCQ!pu@zutJ4a31$57DEnK$Qp#w@Bm{Q)4u>cVlor} literal 0 HcmV?d00001 diff --git a/wlauto/common/android/BaseUiAutomation$PinchType.class b/wlauto/common/android/BaseUiAutomation$PinchType.class new file mode 100644 index 0000000000000000000000000000000000000000..19a2ab462d417596d1edb77d2f6563c4a3307abc GIT binary patch literal 1233 zcmbVLZBNrs6n^g7ty?KCjxpuM_let3%6L(e5i&$bNE$F1BU$`(SL-P0TGO={|CNqJ zj7Gy}f0Xgu>jFdlAhgLnIp;k0oaemUA3wi*2T;MY3{;eKlrg6vui>$d5zLFjOk-3h zrzsE%X^ab5Nh2rZqJ|}g#P$wD=B4Y~2Yc_|It<3P?>pg!$Gcsp%dlwKLC54_$9(7U zI10?zmEt+?It_Q7EFB)XfnPWaQDsne8+#1uPQ$Vow8H+n)!3{vEWNr)u+$OvVnJo4 zSh`XGbt8C9g^gOS@9e~#mJ{yrmPZy%M&5nHuvN4U`4Klg?zhc)6uN%9TDl6^tQFYY z+vlMxQuOnvKJQSv?B6*UQmd}-Mr#cDe^A&i?Nb}01DC4L)ZMnvqd25UN>Mn{tG3s_ z{9+*VrXP1!FVB6A=BC$!IJBKDS9E9M&k@gy78#fjJC--Fj1>(}7#1$OAO@!K)W99w zH86^-fpKxv#A3bmQlloONS?>d$#G#e=%DmuhZ^y6ENlRTUo=a6_(6?%ho$Z>Eu80K$S(WBk5?GR{~=|wyi4tk-}%&yyyNc+9w!lH zN|$6Hx9kSp%XUxdb&<4|FJ8k6{aeEclgRZpeA%tBzsF%-w#>ws9L;eP3s}_ggkkBT z!(?I#Pfgs!Efa$%m>7}UC<+V%r+-J5MbqsDp&(JCrE*Wn^}PenjY;^-|7l=Y?CrLC z5Y?W1e`T%`) zZu%J6k1E_tcY0kXc_bZo6sCwA?xNTg(xgJTDkA3%J@9YfI}_wQCEgU1|Jy@FO+_l(+>Vr9Sj&Sk^MlvSS9e_0Rb1 zvzi!VOn-ns%6NyG(4;TbWapfFX7Aj&ckcc5`^OQ03Mx6wqmV@o3t6%jwY*~DsutHY zT-R_z!;*=cCYBkVIh{i>_6J-1z2nQF!`09^40$}3PAn$W{m>57yRO)vy&)e9897y< z0$*DUiQNW6=BXcwNQ|E|B#WgT24hq91BMw-L_sqi_5!uddm-)QJ?Zmshby7=-_2Y{ z#>x-sLVL`gzHe=xA8?9Ri~LZIM6|aRj0duB!ZC5%!YYauES$GcLfOI{++|qQN8FG{ zdu~&9Vt-HMp3`6m(;TYD>>U#3ovnN`RKHy=j^LSh=?Rx(n?Fn_U+h%z1e`igp`Xb`t zr1vxoMz!^}N#xb)+YG5UjhC;8jb_*L>?NAbb2O{WF6fSF3mII%46#k>Ba&nR-S1#W zKXYG^_@ck$HyB3i6O7jrnI@Tv%}~ z2pgyp*U(0XIDw0lMp`GsOytQ<&`RRc#G1urVgqx8w`k?*Z;lvthN915<;B7utOeqZ literal 0 HcmV?d00001 diff --git a/wlauto/common/android/UxPerfUiAutomation$ActionLogger.class b/wlauto/common/android/UxPerfUiAutomation$ActionLogger.class new file mode 100644 index 0000000000000000000000000000000000000000..a8ea0822b6e5b1fba830ebbcdcf30113080f73cc GIT binary patch literal 1584 zcma)6>r&G|6#ljaQexy%K)j%$0=9t?P`PLkrCb!OfI#sw&M-^6)-Y+(Nm7MJ@iCkc zWOT*{@Sz;fhCxf^ikiNXca)hDj9<@kknJxlE~ejOiGjD40=@VNiVG`5U~*5YO)LPu#G$T{PBx z*R+di`Ymj}ZHWQ{d&kh^e>ATHk|FlaM*NiORYi^3&y(YA#7|_s( zF6u#{QYw3U-d1&J3g$G-V?n_)4bQQt;S#!Ornxs;tMh9Mg#7=+GZoV+2v@@rUQpmx zz_nLFR>MmyOZSR~RlHKLrePf$G+c^t$`q1g7(1^h{%IRSTaCub_KwI?cRFgWlh!b_ zha|wzTonG1Tp0!uVc8_ZE&{5-XB@{8+$Isw>^yN|cGbg@cYv46!lnz6suvJSR_!Qh z<5ZRFd!8>!3^AMA&W2f{A(I@I7GK!SwHl z=tKL6>Zy3c0U8g{v=8NLfO4JIcn~{APu4hEFhMAjxIupX2m?29n=C>La?%c%B_ByVk*heZVkh z)ct1O3Yz)1t`)ZYeCP)ZEM%16-97w?lG)q zjD73S%Da}=$eXRe@fyW!cY+g!U$@*HD{y#`(@K(OHK}0Yauo)-h_w$J2ZOv@~3&D zbX?{0n9@JUih(S!s@Q?k+k7V zw&Tc~yZbcOBtP@7I%p(2`z@Y9v4in!3N8vLmJ9Tv67)Rfa+1pi2uZm>5-*VS3#20e z1LM3~v_nE1*Ki#SH^{q5$Um(O_6d;_L_d(nFhN!pv02KW6nQ*<4iT}cC8XSE#7-db z{K$p;TePD8&O#cuMV$IeS)8GODE)wvn@Sx+{wTr|WdD>q->x9Tr6VhHa!@ga91(Mx z_jC~cHNHZsC6wqf)FZ@&qe;T)5sn@cPOotKWa0D+XFw6op!$s%qHUV&n$$**S&OyN pYu4m8`pufs#-Mow#${gYO7v9LJ=~{Q1k<=es8N=L84Q<`zW}(dIL!b6 literal 0 HcmV?d00001 diff --git a/wlauto/common/android/UxPerfUiAutomation$GestureTestParams.class b/wlauto/common/android/UxPerfUiAutomation$GestureTestParams.class new file mode 100644 index 0000000000000000000000000000000000000000..d793d013fc00403942e06813619fc9326d0fb2ed GIT binary patch literal 1554 zcmcIkTTc^F5dKattXozJR79*GMZ|WwEFdBRF)BvWq(Ksa_d_|tl6Ko{w_e_S_t!|$ znE2oi@JAWHvn`7XMR~AEXU@zyGxN=5=lhSZ-vBINErk^FCQKAGu}utNTAyY#nbl;@ z!~h%v3kDVidJj}1X!>gVLtPoTPsPhqg@9G`JmqgxWTT-P0?Vaxt?Ed>>b$GSW>9mQ zZpe1tZz=!Hj{B6lstnwkH~EX3HG%$4nCGsq%KF~G0|7G<1s1!wekL30H^-3;-I{gR zD<8J8FH_t)5ij8{vY|htuKcp{0s&DJNHzjhXVyLIuIC1i1uFS2Apeqo7pn_WD;9PI z5*xLBC6F$;p4x0y_msaa_bSv3mTF~L*_FPl<&H8D9J&NMa8h`-aZCELO5J)F02jcH ztaVj7&0~T0Px>cN{m;cX?MpGNM(TB~$>zLpbp?jcLZ3U5Z>5DS9$M%{+Co1v7FIB9 z;hH8lF=F9{CM(DocqFiT>0yql!im=5oQm`goL75C9BP53OZy8yx>bSs^DVm%$iJIJ zWfX|#!>QWbDU}5B=Z8F7SAnVXpZZXYAjLed^>p~sH5V0;cO$4JLukmhJL!Ps}uheR! literal 0 HcmV?d00001 diff --git a/wlauto/common/android/UxPerfUiAutomation$GestureType.class b/wlauto/common/android/UxPerfUiAutomation$GestureType.class new file mode 100644 index 0000000000000000000000000000000000000000..8cc30f68d657406ecb7468e201c73005d2173529 GIT binary patch literal 1287 zcmbVL+iuf95IvhXapJlc($YKk07(lqfnGqVs+JIFs;G_RBq-tmZG&4}IW}_a0)GXm zL=gzoXFdusy9Ao5JfPUpj%MfVoHMhtKYo7s4j_kzNhru_n8Un^aTRwp3}HbmMgqec z1r33?m%ym7xdg_9y|3Z{!%(gCq*yN%iZ9LQrAm=u;F;A9Lf@*se`7J|CD*n5g2UTw ztIe=nZh9?)`z_WFI35C2znQY%ll!Et)3LY-ouC*Pub}hfkcOBYMW%ivthOJEb zfFE+h;qJa+2EOg?=d=BaPL;hTck0}?1x=Srae0dbP(lLV-9anKp$1qYlM#VCQ6phyDorr=MVok?$Nzzy*+f(e$?t#?| zNOtCb`5BgaJ1rkUz9-|~2{$^BkNPY E05`)n3jhEB literal 0 HcmV?d00001 diff --git a/wlauto/common/android/UxPerfUiAutomation$PinchType.class b/wlauto/common/android/UxPerfUiAutomation$PinchType.class new file mode 100644 index 0000000000000000000000000000000000000000..b4b09cda6ba579d4199b89f8b6710df8c771a779 GIT binary patch literal 1249 zcmbVLZBNrs6n^g7ty?KC4jl60`_v7TIvij!LLdYpNdqK%Nftj{YJrljHC>DPuXH40 zG#Wnpqm1WX7ubRybZv4^&N}D0~JLbbC}nV)9_5k2o}U)rZK9M z(-es3X^ab5N@GIEMGY?)5<9yLnSIx{k82;^It<2+?>ph9$J=eE%}}=Npk?x~Wxn%x z90g|VN>P8m?}UeScY|y#9=U;^zYJ1gQ1G>stil72Md=v16DL$|wU&z|R;_w44^?;k$_jN?rgVi-};hm1cPRiLYgAQQ!yf^iiJ zGXC&5iYF?bV!~fc`oolpX*`Q!M!}qd=M1VNT+ftQyCN9a3PX3+wp__Axxr0OgbBwO z6e(P}&Z`XZ{5Id=y2-7oUXYGqRc9GG1`~xhHMiGlIY3?wWA6 zipC<@YFrw&<)4}}EJMySEF{}M){0MUFhm;p_vQ^ty!C3E!dd5=CRG&A+huNUaL4f1 zO=HAQmntoI&K56=yj`sdhe2DhEa7BL?z)1ur`t{{aUu{^)PwzgF$FnCTxr*-5xL#6sQc2m3P=>SV0%tkEE|3qgRKIN`hC!F71uOe zM?%8@1{wNV2-Ljd$I@Ev{W66)-Mb~vFe}2*uz(ljSPJqkDOl97gsg9VsUe4D1+O%` z23?PilCUb|vC1&nZm&=3!w@@8rP=tKT$?McJb=@!ug+F^KfMy@TK z*%WD+Y*#?srAOXJPcclbj-wxx6RkqHLO<#|5R3UYA@E@2bAvpFXjP*6Qv^=aJcMCt2B4BHhFQ{z{Q-snP`3a8 literal 0 HcmV?d00001 diff --git a/wlauto/common/android/UxPerfUiAutomation$Timer.class b/wlauto/common/android/UxPerfUiAutomation$Timer.class new file mode 100644 index 0000000000000000000000000000000000000000..da53c6821542d7c78d7ef82066d64a6080e32913 GIT binary patch literal 983 zcmb7>NpBND6ojkC%XlV}m6(Km3*h*WfeRN95(-L)m4tv3A2=Yj?I!7H7HJliAH@MF z2*iOOz>h*ykE6i^AK+52`>E^edfh*Me)|sKK2}}WxZ`$B3>`12qHA77Vysaow$$B8uRIz<;dk}}dOhW%%AoDEtb6u2!_j@XN zbD*EF7Zx#A3cy2>ipUJZr~-{cm37SwMLJI4u(83s>5;$`*H6na+-{sF1GS+|)}_C1 zSUZ+)r3Vkw9vW~wxJIVn*tp~2E|zVy1lIm9u)v(b`hkoN{pbB-HDJ=QR`)}isZd~k zkSB?Xim%wwL15mj^{-d)N9+7)paRQ8=gKnai^>65>)_jCnmWT>hbIN=V*3k3+xZO3 zNX1C?Q{kGWE}%-?q&_2Ki_Q+_*zrbQ%wv%|fo92laLR48zoJI!ZZU-w6~tX%CSCtWDEnFL_jPVgFOO96?Nomz*Oi%$qYup5)sfB`0`F?0r@c87u$%E$ zB~WVVNFDUMZIztJc1WhuiUS#*$wV9fp0tfP0dJt86?Zx+p}QYNDrtr?O%=cHjI5yg zxDKZ%q?t@Ifw7vAR+NF6jvCV}?g_YGE(6swRO+Bgx);iyq>tl%5~w|GRL}jtosA3m zU3z$eaStx0Jxm#xF))j12WtY`qi*^i!2)Gd+7D&a@ekV=Hk+e1j>U^5)(k>Av}} Uw1wx4xA1~Zj3oYJtfFj-ziFVd8UO$Q literal 0 HcmV?d00001 diff --git a/wlauto/common/android/UxPerfUiAutomation.class b/wlauto/common/android/UxPerfUiAutomation.class new file mode 100644 index 0000000000000000000000000000000000000000..1be53c1fea49f0f16764876678617a925da57df2 GIT binary patch literal 777 zcmb7C$w~u35PcP6#*Cveao=~1IXDQOR9rz31`%BDZER!9EYdUM@>w2S@ZbmdQDV(R zK}ZBWOuw$z^=gWG_5Sht24D&ECUWREFkoU3LnekXl1B!k2F47G6N-B>jAAX184iUO zUP!R^eP8MwSA?Mq4NMT)zq9VIB_ZbqwVKp~A?Hfm2s@6HYbTuYYqpcVt#a0y@>R4> zXelq860$qNIcp1!^5sG7ok@Kx&Rk|nPEZx@sn9BkzsPKKskoDhQw=;@XwSZNMH~fo ztWr3+JCyq3L~SwWiAV)L|Ij=NVqKNHDyiDvn6j8OYoQYbLiw+C4NO{?!nA=I3$vKB zV4;IB*Id6XLiw{R!bZYa7 zN0DaFcyku>P^FZ4 xLdy%Xk7#{hGJ_(YCL_>;Ec#I5n^hN}o4F*XH$D51XDo4S@YlvsqHEtA`~>3N(J}x4 literal 0 HcmV?d00001 diff --git a/wlauto/external/uiauto/build.sh b/wlauto/external/uiauto/build.sh index 074f245a..95f9872f 100755 --- a/wlauto/external/uiauto/build.sh +++ b/wlauto/external/uiauto/build.sh @@ -16,6 +16,12 @@ +# Build and return appropriate exit code if failed ant build +exit_code=$? +if [ $exit_code -ne 0 ]; then + echo "ERROR: 'ant build' exited with code $exit_code" + exit $exit_code +fi -cp bin/classes/com/arm/wlauto/uiauto/BaseUiAutomation.class ../../common/android +cp bin/classes/com/arm/wlauto/uiauto/*.class ../../common/android diff --git a/wlauto/external/uiauto/src/com/arm/wlauto/uiauto/UxPerfUiAutomation.java b/wlauto/external/uiauto/src/com/arm/wlauto/uiauto/UxPerfUiAutomation.java new file mode 100644 index 00000000..10367015 --- /dev/null +++ b/wlauto/external/uiauto/src/com/arm/wlauto/uiauto/UxPerfUiAutomation.java @@ -0,0 +1,49 @@ +/* Copyright 2013-2016 ARM Limited + * + * 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.arm.wlauto.uiauto; + +import java.util.logging.Logger; + +public class UxPerfUiAutomation extends BaseUiAutomation { + + private Logger logger = Logger.getLogger(UxPerfUiAutomation.class.getName()); + + public enum GestureType { UIDEVICE_SWIPE, UIOBJECT_SWIPE, PINCH }; + + public static class GestureTestParams { + public GestureType gestureType; + public Direction gestureDirection; + public PinchType pinchType; + public int percent; + public int steps; + + public GestureTestParams(GestureType gesture, Direction direction, int steps) { + this.gestureType = gesture; + this.gestureDirection = direction; + this.pinchType = PinchType.NULL; + this.steps = steps; + this.percent = 0; + } + + public GestureTestParams(GestureType gesture, PinchType pinchType, int steps, int percent) { + this.gestureType = gesture; + this.gestureDirection = Direction.NULL; + this.pinchType = pinchType; + this.steps = steps; + this.percent = percent; + } + } +} From 1fd8a14ca9e7d4d7cd176b6a6d5aaa1b8a30f2d5 Mon Sep 17 00:00:00 2001 From: John Richardson Date: Tue, 19 Jul 2016 14:23:48 +0100 Subject: [PATCH 4/4] Add googlephotos workload The googlephotos workload demonstrates the use of the ActionLogger for measuring performance metrics when used in conjunction with the appropriate instruments and result processors. --- wlauto/workloads/googlephotos/__init__.py | 131 ++++++ .../com.arm.wlauto.uiauto.googlephotos.jar | Bin 0 -> 15382 bytes wlauto/workloads/googlephotos/uiauto/build.sh | 39 ++ .../workloads/googlephotos/uiauto/build.xml | 92 ++++ .../googlephotos/uiauto/project.properties | 14 + .../com/arm/wlauto/uiauto/UiAutomation.java | 440 ++++++++++++++++++ 6 files changed, 716 insertions(+) create mode 100755 wlauto/workloads/googlephotos/__init__.py create mode 100644 wlauto/workloads/googlephotos/com.arm.wlauto.uiauto.googlephotos.jar create mode 100755 wlauto/workloads/googlephotos/uiauto/build.sh create mode 100644 wlauto/workloads/googlephotos/uiauto/build.xml create mode 100644 wlauto/workloads/googlephotos/uiauto/project.properties create mode 100755 wlauto/workloads/googlephotos/uiauto/src/com/arm/wlauto/uiauto/UiAutomation.java diff --git a/wlauto/workloads/googlephotos/__init__.py b/wlauto/workloads/googlephotos/__init__.py new file mode 100755 index 00000000..ce733478 --- /dev/null +++ b/wlauto/workloads/googlephotos/__init__.py @@ -0,0 +1,131 @@ +# Copyright 2014-2016 ARM Limited +# +# 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. +# + +import os +import wlauto.common.resources + +from wlauto import AndroidUiAutoBenchmark, Parameter +from wlauto.exceptions import ValidationError +from wlauto.utils.types import list_of_strings + +__version__ = '0.1.1' + + +class Googlephotos(AndroidUiAutoBenchmark): + + name = 'googlephotos' + package = 'com.google.android.apps.photos' + activity = 'com.google.android.apps.photos.home.HomeActivity' + view = [package + '/com.google.android.apps.consumerphotoeditor.fragments.ConsumerPhotoEditorActivity', + package + '/com.google.android.apps.photos.home.HomeActivity', + package + '/com.google.android.apps.photos.localmedia.ui.LocalPhotosActivity', + package + '/com.google.android.apps.photos.onboarding.AccountPickerActivity', + package + '/com.google.android.apps.photos.onboarding.IntroActivity'] + description = """ + A workload to perform standard productivity tasks with Google Photos. The workload carries out + various tasks, such as browsing images, performing zooms, post-processing and saving a selected + image to file. + + Test description: + 1. Four images are copied to the device + 2. The application is started in offline access mode + 3. Gestures are performed to swipe between images and pinch zoom in and out of the selected + image + 4. The Colour of a selected image is edited by selecting the colour menu, incrementing the + colour, resetting the colour and decrementing the colour using the seek bar. + 5. A Crop test is performed on a selected image. UiAutomator does not allow the selection of + the crop markers so the image is tilted positively, reset and then negatively to get a + similar cropping effect. + 6. A Rotate test is performed on a selected image, rotating anticlockwise 90 degrees, 180 + degrees and 270 degrees. + """ + + default_test_images = [ + 'uxperf_1200x1600.png', 'uxperf_1600x1200.jpg', + 'uxperf_2448x3264.png', 'uxperf_3264x2448.jpg', + ] + + parameters = [ + Parameter('dumpsys_enabled', kind=bool, default=True, + description=""" + If ``True`` turns on the action logger which outputs + timestamps to logcat for actions recorded in the workload. + """), + Parameter('test_images', kind=list_of_strings, default=default_test_images, + description=""" + A list of four image files to be pushed to the device. + Absolute file paths may be used but tilde expansion is not supported. + """), + ] + + def validate(self): + super(Googlephotos, self).validate() + self.uiauto_params['package'] = self.package + self.uiauto_params['output_dir'] = self.device.working_directory + self.uiauto_params['dumpsys_enabled'] = self.dumpsys_enabled + + self._check_image_numbers() + self._check_image_extensions() + self._check_image_duplicates() + self._info_image_used() + + def initialize(self, context): + super(Googlephotos, self).initialize(context) + for image in self.test_images: + if os.path.exists(image): + image_path = image + else: + image_path = context.resolver.get(wlauto.common.resources.File(self, image)) + self.device.push_file(image_path, self.device.working_directory, timeout=300) + + # Force a re-index of the mediaserver cache to pick up new files + self.device.execute('am broadcast -a android.intent.action.MEDIA_MOUNTED -d file:///sdcard') + + def teardown(self, context): + super(Googlephotos, self).teardown(context) + self.device.execute('am broadcast -a android.intent.action.MEDIA_MOUNTED -d file:///sdcard') + + def finalize(self, context): + super(Googlephotos, self).finalize(context) + + for entry in self.device.listdir(self.device.working_directory): + if entry in self.test_images: + self.device.delete_file(self.device.path.join(self.device.working_directory, entry)) + + self.device.execute('am broadcast -a android.intent.action.MEDIA_MOUNTED -d file:///sdcard') + + # ------------------------------------------------------------------------- + # Internal methods + # ------------------------------------------------------------------------- + + def _check_image_extensions(self): + for image in self.test_images: + if not image.endswith(('jpg', 'jpeg', 'png')): + raise ValidationError('{} must be a jpeg or png file'.format(image)) + + def _check_image_numbers(self): + if len(self.test_images) != 4: + message = "This workload requires four test images - only {} specified" + raise ValidationError(message.format(len(self.test_images))) + + def _check_image_duplicates(self): + if len(self.test_images) != len(set(self.test_images)): + raise ValidationError('Duplicate image names not allowed') + + def _info_image_used(self): + if set(self.test_images) & set(self.default_test_images): + self.logger.info('Using default test images') + else: + self.logger.warning('Using custom test images') diff --git a/wlauto/workloads/googlephotos/com.arm.wlauto.uiauto.googlephotos.jar b/wlauto/workloads/googlephotos/com.arm.wlauto.uiauto.googlephotos.jar new file mode 100644 index 0000000000000000000000000000000000000000..acb01ecec29b62ed4bf8eefcb133cdd4e0e3d7ad GIT binary patch literal 15382 zcmZ|01ymiuvp0xC@E`$#1Sdg*yA%B4?(Xg`!QI{6-TgvvyTHZW-QD^4zqkAL+wbi5 zOixeuR8@CZ)#+0`Ge0>gXc$BY2m}a-QIF+ zop}?1cpZTu<@e0$-saZ!e~hK2x7FH$3j!iF2m%7>b1a5d`i_pqj`T*xZe89;dWi!~ zA88jWYo|6k82~7Kce*c<0{-YQ{xA~(5F{l1JOY2Mwc=ST$vgC2h5HJ^Rbc7(mbWYT zEUHv2o94n*Y>%pxJu9j-n(oV!s5P3Fp9Lbi<_|txO-x*S0760QFASF}%#QoxAZ=4$*5;ZG75v}x8v6)fK>a11HTnw5n2mr6^RGh z3rgCWq8vKik1ZEd4b2L=8%_)D6`~dG@zq$LAw1&y`{)^(Br(6$~_*-1w!WLC73bTogRrq z_-8<yYnoUNBy8xOTV5#Q2v`hW=qdB|W&`NNPx# zaG9tu{(O3vzagw(Ct>L!d=R~WcYj5Afp@?Lfk`}EGGYiTxKD*bX`y~98~GKz73Xu( zu$LT{JeS`u`7Xa+a>mXHv*4woH9~X&=|6|&v&pzgvB|sn14!lP4ebO$2~`BN%Ec&! z?S{F9V$`Ft`+8};DIuU2%>ZQpv<8BKV!&Hw#&w`E(A96n@5MjFZ^-`*cmjL@9{9PE z8Nk@l+VRw)n?cP(ltA(d@Y@mDQCt#!Du8cOa5Dv%0QA`;zWlm*NHGeF=r;mH)}yaQ z=fEq4mw}vz$Pz$5gx&_K`bqg)`d>hLewqRVbpbIemIyBmRSb>fUnGE8`%Mk)7E;p> z$#2XL-=Ev>10ox89qy;}E*vEU1<*={`meYex80g zkZ2GwP(9Gu&^nOo5bN+(UvJ?fAqSx;fLB1FO`}{$Ggv5~C^4!Qj2Gr3n;rip#3jw9 z#3mZh64(J0gJ3|HA;!l=!h&}6qt3;9#6XA22f71;fHs?mn;x5#n?#?^>;7rp3%|B6 zAPiijL>J^d{F3%k?9=^)fuSTmkg!0bO^!{NTtu}mm5?5OxORM(&YPM* z6d;;^4g^26&leu(*Dr23kLc+3mEHm*zll;IX<@$m6aR)eg{y|XhmwWxg;|HK`wPfu6?`a-|>_dw-9o==z|{e;|yrh*0ta9bD3?f_dzN98g6M-;V84S&ym~sTI8y z;qiMdek;m<`8x3>`sMFSsm&fg-77w1H>^k2OZ-hn-fE!>aP-0vI9=h$rqJ}*sfL2k zRsr;H%6ZJmgZt4@z&agd-a&CTd-Md+?CV6di*tBuQ;2hXt1QkibCeOAT>`F@&8I(c z%$M0HdzqLy1I- z$)Xo}rq8-3>ryR=5ql=e($U*uP*1W5$??q3GC}%KD!iCG0@FEr(pjUb?}CJ%ZL-#_ z%Q}WgeFOfyyGVJ|P^?)`oBnv@mpL>y|6r6#e|x3+;**emKy$nJ9wKwI_^wpqZuM|< z%WQUF$~9s5;wRIC_zrSTAG+svevV~bOO*6A*rM_(nBK;xp*y0K@<^gsvy#X$kZ4hx zsp6XGo)!J|V<&aI+d}wOoepaaIz6Iv@+!0}uWY0n8bI}>82gq+0_ljc*ReB>Db$=O z$xNgAh$3C5?8r522PpaLBFPtfVjFurz64Ez5Y#j+oS=I)ky~ z^X0ZJJ9YU;I*t9%S^~C<%x$AKuv)Y&%q$gP@#Y~6W@oj=3V#>AOjn`bJ5`co9p*;Y zwuPS%K%xhH(7JVT2d$G7(<%L&eRlm@d_(R$EhM2jt)8A;b%Y84P2Xw(dZ^9Gya6*F z%XH?P-cn#qmM&%R1=8GyEQK}z&HOzFrY0+*h>*VVCA#=Ra=HA7nmWk=k40>ThfDn+ z1xb0G+8qB61#b`>%NngY^BMToH2uA#m>;J+=Yi!7Viv_yEl0Xam2$a9<&vvvkQ?d! z8Ak%?uqNGP>{^IZw*EImM$ope_D#*glNgJ)(v0DeYc6N|8o}GELmVjnszCCZclbm6 z=QAcAzmhxAzA;-FuW-z6OddPMS#A$PG@9gO#a^ezWCY%8ap6g!R(JB)g2U6IJrT{* z9|ig!+%m~DQx32i)dDT5erYeog}U($LCZwNwo=E%0r4gl=|$$v#ca=+vt62Pa!%F7 zRAyYV3RHB-vwxc6_opx<99i$o(uJMVbxWR{(|eR|QY_vTsxJQD+AF($5juMaT13&V z9P6Pt8ga@fblkh4?Vo%NiLXxb(G7iit4;nWDx{kBwR|$A$Pxn`z22n13xaW+o_r*5 zrhZUV<>F9ii!dC$LRKw|rFZAQcq-4pdFaky9@(~M^_Y92O0)}sU7kBOodtLw>Nw_% zq{?6IEANL$wRH;L^2J~NeBHjwi1$%{9^lA~uixX6BP)|#xH*S~O>OL6PAj@{; zpU-4~wzZLV?P8(J=iJ+j_(Dn0ujTAtrBB6$kY%=v^J!LXI?{z3C(b)PtllcsXysWL zYqDSFA9{{nNdVSD@-@XftaJ%V6A2i+vXZy2Kc{cRFWR5Pd8yyb+odnn%_y6Y*4a4@ z&3j!@N6(8muvHQGFst&9;$;dv@+QS;U~*#ihoNsBA`i{~K1>bV5?#HkPg4R^&6g{W zex)~?Eti|qkNlx_SJ`!2Tl9N!j~*=$KaZ)}E6MXpra!o&sgXSE!rZe@WB~82%a6Ji zcs0<2eiXe@ChC@qA&LIl2)PacN$jcj5J){S|NNjCy#bN5Ei^1YcyBTi@MQCyY=UspbzGvp)J9#Kpp1g7ce@)DuynZ{^tAzrhQWO_xN|oRVwB;3Lr9 ziyWUs3XWxW(>wD|-SROhr;h}>y_$TU!m%gm`+p7ge09nSFf?yX$Gn!?3oo;zwPf6A1C|3b;5Td@*ur_ zUqo7k@)>^|cA=M^6Vw`bh*6-Dlpc-u=#f;0-nGtf|$Osc}6P&Sf zSIr1hnT(&Mzc65aItqJAL1O%n+_c#IzR9(@@)<#2K(Rx&Ky)S+6a$|72s4y3wGJk7 zeL$LK=GxuRai)<7FNCop`G9QIP>^Ve`)O#Ri+F9%E7o1Hf3xZmBY#Q$Ab>+tH9x9H zFu!V0Q2ztImPYC^k}BZ{a7%Jx890VIjm0b#$m<)z^5{ba^(T5(s_ElWzVV}9z7cfb zX7h11af~|BG0P3&p3Ask4QCwXyV5>ps342^+NVY%6Skd764}0;NE1yLC@-H$gFQsl z=k{xoB76s7>&PubY6|CNPf`%hleBcC8{Wdu7P_gy*FQfPGd=y9VH%t<*VfZ>s%(}N zU`n~`=;_mNUG(M9I^ly=Ed^G0G{LfbiX+!}zF?p>#Fd8@{L}c@f&(`|>pD4!*uc(9?tahw#i7WXL@lBIfFY#rpsSEqCxUmK z#gk8ZIRVyBiuZZb~olMMafcG^=$Vo0mp>@JoHEBMn!E5!_Wuq|B89# z8B%YLik`YOmt9PlO6atYoK8S_v+{?vYf z_mR|V5IGUUI)6;dp@)y;LNl1dCyyP6Wv}k>Ik!FF!J}(G@iFmcY=zQ*=R80+k=9%+ z@*S%n)>zlPB`woMA-Qd)5ijHQVh1aZB0Q>X_TqzCBiqEyT+&NtPD#TepgWs=$znyJ zL$Ua5w>f9gM4{Tgb;jMA?Ydjn0^UZrRO}ItZ324|IHB5S%Lr6OV+;E;59`pe=76C%|jpsI7RD$L6=Set1(q#MwiNepOoV&y>sFlt%3; zpGJ2{*%|n?WzaeSxw|f}LJq|3mzRUy^gx|TFe=Uoi^}mXGi7{Bp^*)@yEP4A@q}hA z)4#?mM><(Bg+6yrE8)a_keGhQ$4{#VRCQ~9$G|hpV9^%utq=H>1ERtE(SlmGKcs*Z?aKd^S!IDd+z3YE24>>;875ve0GkC z&sOL)l)4T0WYYNG*cjqfq=V7z&ZFzZrNakqCW9kT@ntb1c-DPx#bLYAiU6f^9W#2b z)R|U{&MlbX-@g)0+_F{GGgg$ydO;K1nGX^ilMq_}WWXZd5AkcM(xq z;W>v}RIT|BU*VTku(56Gfn4FK5tB&tEoae6FUg(v*PN}s^4Opx$}!sJESQZKc*_&e zw+*9>n^qXoT5o^C_w$j&qk;RY-8*uJn+#K-MUQBT3#W(`JypeFWcNApS?z5 zjiproXCkAvBtn&SiM^XQJd$@dyc0?^iS$x8Fep8lGDcTGM~P;|6rShNbp4dot` zdVACwu}00Gurjx0=NBBOzCyWDt`l3$jFrCg?bUxtnRV;^9m!o%*vQFS1`rBS`Uqot zASj-|sTIRtk1w8y=e9)K*pJfvEmL`LIXyl5{&&r0b&1U(^PK0ey~95wwfh6j&)|9S zU(20(bRv<^iN0A!;C!!KvR1amSl&WPNlPjB;9V$&zAy6&-sEU%ka5wh!5C!ERNu$Y z!B+jzW(twRVd~TIZ~msJOK{raM19Vr9wI+)^sm=jw_>rq64bV_fu~bDE>&%D#bwdy zwqw#uDYU(Owiw-Wl{>euW!vtJ8nkhEul5G^cg?bIyw0 zU5Web2b*a*0(>v*c_;X@$l3=vcL$(>94TG8WKUlbWA{qI>EMysx4$danVu_TIA@(_ zlNm2#i=75L;)NISGr?Vp1i#)g*K~kI#a)YR7Dt*|77eC6#6CYUJELFiabMsnGBf5ZDKFi-kNi+Z$1}{$3E<`cy7nc*s6&oK+k8kfdbuNbGck8-TQQs>U{G_k(i>G%S^Vr=l3fWXSnea#VETaip9M>;+lD?6}G#zu96J*vjf5c z6z+i_rN30C&8yK*krx!U{e9VGRy5lq25k|5tGNl>3v%~!4d>wvlesOXGU4EP{Z}?t z-w25_)41&nWnWkxnz75Y8uSaxIU;8ai+U7T)Fw8Ew`=QTPCo4-t5iVoCOD%#pjmN` z?M{0Ma7$OeekLPg1kqoIdsZ=X?vnNyTYW2OrTAsz9_}d}u9>hc1F-)XS+m)GXVcu+ zXuo^2hMS0;rQG1X_O8?(ulaUkIq=HG=$n?Ro6~ArwUpdydq;JlN%dpD+c|!(yUAU1 zo6l*c^dT?dBjusWGA9461{%%Edz2)1{%RJ4V_IzC-2SvDHaI9*k!$4h7VF}I= zel5+O_X%UVW7sx_&{WoiW=2s>=}WbN_l9!*g=Wn3LzUv4_X*b6n<}Vz`l6?A1>-^Y zPx4Dme&b7xzjfFP!@9EP-J7c8HD9=KvyI{f?_I+~^7fOga1@lrL7Jc?QZyMu)t$6# z^^Mkn<;pBqdU*p8U-Kk0inDT7QxN#Gv$}2{&F8ET3YoS)QBTtr>w8B9u6FL6oUs6tA;m-N?lZqgK<$L4n0bi`Z&+*}^eeb6M90?pXtz^CJ!v zk{_nC^bUWHjpEBvzvQBPlb;42E0TFmPr#E?9_@9 zQqYC~isw7IYCV9cLN_3^Q}Njv83=koX6|0eFSVix}kEI zvJ>KD*$M1M5t4ff-@tBaX6v5E!wo@KH<#I!%N;XW#x8ra;>n4`rJ8_J!QBgZUUh%I zuyx>)(w!%6Wqrors1z zjnnxH1?Jlhr3{}G#FK$n7e?Qx)E<@bq1Bi8)Qz-A#){^Wq10aut}mqW&VsTE8{lW>YbX^9BN-m@@VM<&q< zxuH}QLgu;Ct<1TW3b(U@g+HOmMQ3!?>(%FV&%B=cj(piu%jMYDYqC;zh+IP%9xsXvCs&a> zb#0J$)thWm7u>h$#~oEw7UDYaTOB#L@m2^$9`TaCbNb( zX`#yN;k6&Xp7AsuL%q})1drKo8)(m0??;&=X2T8N16+{MwhXP+A)WzzZq;g5TM zgbIgRvai>ixVj`yg#5beBJ!tY`xnAUXiuFLBKAIvRSczhuR@vVLs)tgqf{lhQZfa8 z$PKZ`_+IVSKio&%(GIY55_iX@Z*lo9jN5&%k9W$V^9m|2=I<&#ir#mGfZ?gcmw7trwq>k zqXPpQMaoZ4JpXRKwycbV-R55L-84jK6PW|>>8MIl;*;yx+bQeW+sSKsjwQG*zjziz zNcIep?Zh^l49D3|IfZ-coH`piQ7ZUA)-=JEGX&b)lWnA4H9ANO>ht|gvmJ*_GaY)fNPF^#0WKy>;NRDm-7oJ{-rbZ4-w zRO|P|UuYp;F&Z_SrKQFG3Y2DdqAi)Yw+tCUQQy7(2wP>K^)`uGrV^tVztf7AY)dru zpyJ-^T2OR~T~w6jxq@wTm5^op3QNhISi&J^>Tu3s@zTh``|;j*`x?SLb4~YyFeUpV z=SV3N`UgmvJ6db>0l{rNOx$>{-nXH}z}3GAe7+@Ky)L(h;CkMt#eTe{zU?&%qD(2? z`*u)nmO{gO(qi`SK%OY{p6ArR`~Z;^E|xQtM~vs3VdLNWXZx1?I(&D<;(nY);~srV zV%Ow7+C3M36-b-b6>(|zo_1v7jv$3w_SXJbEwWotfqmyXO{j_3j+*2IzcZ1;4YEq&*#6EtxNUyFIB}o+yjn9O+4Ap5ds6lRL!6keZ4S{fjJxfegTK}R z^CbG_LZzB>PuhO2Q&Tp#$R&i?+j}XwyW5CK+f-dqvKmnRv)KTeW>j}B{CZS}2q1iK zIy%jK$XTM8$T!J+`bX_7-u5uGeNtf~>5}b+jx4)a%{G(#!543rEd@8&J(DqO_N7Yf zse0T@kidPC<;-h?>hW9GIAE`NCZNorckc%p4Sz1F=#h_PbPpirO{uLz$u$ortH?)Y zxg*_nrmoNQGCT3kp31haBH@{MSz(OwkSuT(o(rFj@g4`i1}!A?2f zD6@%ti^3%nOvr(f$UCT2aQD}PlV)Ko>2tq;$-G=`!lFVpRCLV^C-|gu`o}$ZlLd6H zA~}0DDhJ|&uhI?Tsv*~Cp&)NB3XGD>sXyX9&M+;zphV+u@S@&0iE)vsvs`i5<;xt} z6mGOf$r;kJx&C%VXiz8Iv`efzi0uBnM7u#=4hES#!P#R>($j5dLUz3)XwQ|3)6qJA zkTi)8=VVXy)dB0oE#<8W7aqyg|D{|hXxN?@TMN%OkJ&=tS$yb~y;X0nfJCD76naqM z2WS27hyCm#b`QXhrw^}^KJO{7UTQvk zBvKD2_k_>dH^&Wgpk36508qS-oYhZyQsl{l>lLhbOwl`Zu^Hj$!lnc1mxEPbZ}daV37*ipRaS+W-6;*0(l6=PL&8}s_1ss5pK?6u7^#BbX} z(7CVg_5ML818@89_dDXmkMD!JMNb{US1T+Z)Dt;ttlmoQ7`5TMPih+$|Hp zmXPh;Gb9SM*PaC*d{r~MbH7{r{1P&vU9-3Teek1t-=7#<(>pw~LUK7>P zxVo19OjX?C7_*;(YIzW-@9<#Gn~a=bIsasp>%OIplg} zE)3&l%qi-YU66h4j3O!f9x~F7I+irtob)NeH7$)mHm@wtyt&e}N$k#=@?sQ^`RA3R zr2Bi?yZ_~&ozd@8elmrMK`xxN;vg|-&N%k0jz?7z`tqzMru6fi?! zSA^%ph4`?($ZBJR!*y`{Q5Ms6H~+b+-B85#JsiFu4`PnU8>vcrTJLu!^>@VJop|Ah zp4zzkynVgf$bV|~*F6f}>c7AJ2!&f|zJ-qghGvTwmas4OkYL307Z*2y3BrO3zu_sW zsv z($rYaI0MFvjPGi1VP_J@W3}W?nQfYnCk%V0VA@b|wZO+l{jsiY3daUNEU&pRJFg&y zX^DSgiO;2ZoPJWi!gZ3de5{d6k*=Y!w!Y}nx}9_fxT9H7RZy|ECI5B{-ywX3pi~xh z``1W;>c(@)!{lhCPOs{<~8oI^WmvCx;qOL^Pqws1-la zAe^u3?|E#Ly9PkS>xT&a_eeqi>f9cmFT0dKROpEa!i9+qCA=-IoyRch>C~Hw!?b>} zV@R2w&O8?zJJV%ZS1enmxYF0yXf3xD`$()fjNHA4?bKEq7zvypy8#Xqkj<4$PR^;5iF2mCB zrEJoOnP9svkkxsKh!i4GF z<{S})V2Z%^ZRt6=IH-u`pHeoZmSVFA&qdx(s6dLUX7iFtPL}1mi&0xf;V#bpFmEb6 zQJo4C)1dMuBKTuEk!QC6myM6zcqo1nsrD-#bcl4KGIbRaM_^2s>i7tW`-jWBQI_L6 zJo&1c=URh|6aYKH|Gez586%`RwxeOU+oS=XZwk}?H>~a#_jS5;9$&RRR*YaBhBl#% zPE@6upd%WYrS`5VnrrMo&a*8V3KuwRCC^!B&4Au0R{zqwJP_xyD0OLT9rsiRXXDp6 zM5KJ9$!a85eR;GXPYX|IMiy>!tTc~k-FC0hpL;X`-X~20=%kopU(HeO0-;acv3K!{ zTCIgym~eOggtpK$2&V#}O*6kn!gXl=^|$fKvMjm_f2 z;*yW@`F*!{L^X3=pw-j`Kt6+y9GD;}ZgLhZ(8$_qPK>)hKnv2yA0%7oD@Q`RrQnia ziZ7>@u~|&nh^hBCXbH=l@Nl`7 z1?%*zIzrUlJcA?O$@q6SN&L@yjd0A9hNhH#vqGBTdCfFB=q(NtYY%H4h({IJDE!_E zJcPSZ(^yLS2f`7OgqNseGnU#SMy!aNN}?cb&hRHHiVVPr&>9p{4H1osK=sXv=$49x zn-_8^(olgWwb=Ljp0E|Fx^&I=3vT|u=;R%Is;}HHM0kCL7(29YtigTw0c&H>jC}6O za))+*J`OK74i4uxe62K(k1W27(8v0gdE)#T zBcuWvjPviIqF?`+-h3-29CGJ_CR8D2(nGoDi_l|mVUqK3F%UD%`Y8|7Mv__{^MfoR z9ol}}v@vyBX>ER8A&S{Zky|Dv4d6eKM@0MM{odz080*TF>OS{f&9|1uNfhA#OnOOH z252to*P2@w1X4)iqToi5hl3$`j%3 z%WPTf%Nf54-exEF+mnkD*{`FGt+qo)4M`aY9y8mAWet}N1>FyT=`elo0;)KY+lj_& z_4&`CzlxTzD@N3`W%rMtJ?eL$`_;Oj%wTHjvaKFBw+FFbr-z?JldX){Ouv(?2!d7N z3m93x-?)kudU&7mJulnEt@w^-mNDK*55 zX*+ojy4m|nR?YJ2=f1n99!-lare&T;w%_?r`Tanw9Wy3hW4Y|q?3o(_KW0}%q1L96 z*Qh%wV->y{A5TrfDZYyt_xzq_MglMEx_K-|+$kP9OuQ#Y#E$^dx#`H{djx=vhDNIR zKy7AE+_e05W?euwl4cY_53apKzE3re75a)J1jhS`DT3kAq-qczn^FxyiMRKWmhK3D zu=)b&7RFh9+RV7VZmUTHYAr9cn$<)tEn0@f1-A#~s~8)c@lGCnw-!F~z~3y#Z;2?T zNjYkw*-rC)OsN|iG1Ge&aHdYZf&tTtm|;zcYfX^&$Ngv6ZS9XF(Q8A4Xi>m`C3ly7ww9uuE}nRQ8Jl{T6BW!XrLc{f_5=EbVD=U#jHTb+Rh-J>dd^ z7|fVTAzC7oKo7S5fUQY2TLjbSf>K=Mhkkaa!AlYDg`K#`m+kPiIJ-{f;2j&&pQ~*; z?!$GuT2TyhF(UNivQGP7hkKVW)^aWfw%=Y)x%LdDXSx0k`Rld7>R_Mks*AOiPJIRe zm`EsIn8=y)TheitczI^JO_^i6B5XtmL7Yjgf*=!qv;Aau<%7TCzWAO&np~l$nDmv+ zGM##g*%S>-vn%+0eRH3`NeBko@rk&)j67hn`Pd7jB`?4y+K$XpDvccOS1b>IW2&Pf zRdp~7{3};UgMLQIfZ?5|W(BCl2`+1mT_7|M?|*VB;H&)SpTJA}ZIM7^VOju<<@@1D zjCB~v?`v!)b{M!@R0)`o0W_U1345w|X!&ag4b%`?MZ-RxrHaH^!}vI}`Q53_zL3uM zoCL|_2I&?8W_*m2QBadvbj!$MZ#>!OiaUi5?fY{vXcb*%Zuklb)8X(T+@&oeFOsN9 zDNc+^;X;bZla_%elMAz;QbcljQ;4{7E7^brZiFIF&DT~>imn1fi)dF}GU(rvrCY%W zA?2dRZ}tIs(zJ2$NV3E`j3{tmH?TJREW&%fu3>{Me3hFM@*#NE5F1LExQIMYhl zQ*FraP(mtmkQ-$tFEOZCt&}g^8fjX|LsG=msjgH4!;M6P=F{n2^?{5AB#-_?TAs2jOjBr8X|tneCWFBOm0>5GY^7J2Buql7jElmB z6S&;jo_3*`c7$tQp(&{o{_D@R*=A{ZYIHSdr|xDj?SZ#%kV_r`ru|oVeGXfP$`w&k z@*m^$BioGMyc4HOP=D_xej%c0V3-%4{C&^aSs4cqI&j~0Kp3%t83>e7v=joKk- zds1{9AAV*Y`6@rAB1*nYhzRfT3+#|fqUj5vxVi?CWkcwyt2tSP|ZdjQ}HWTbGJVMfy=b}&J z1=lu7Rs=XdaSr4~67-aMXbP-aHH;j8$4$h*y#)BXSudNADKZzW?3YS+xd_rn!eUk* zM;DtQa7Cls4pN{svdu)rMN5tmSb!j`$pLiC>K@E_!sbrIvv`>9s&8^xmY4`dgVC$|)LI-bA8C9v`hqlMnj*Z6fq8Sx5 zspZmG;|@io6O9*N;0iQ{@uUJt7~lpRtm4pGe`hW9<6PqQzB)B~Jeu+IT^CwM1T`CA z8FuyR@f(kV&4HZ%GsGqu5@ z&~e*8N{|(c@%Jzx+U<#PFi%;dMuuE^d*JY<6MnK6zAnAoH{arWoXmiL0y}8Z(g6OF zWO)~!S^NuR_^)$9r^-wf``0EmSq(Eg(|+v?LZqY&jd_kDLxzR7T!s;jv%^HO>lL=C z92F|rDx5m*=IWd_hG4a^uXHW-+g<;R_JAH>H48sSup91S9}CP*v}@x4y7JgcaqBNgIcWc>OW5!>~wXJ$nUboEx!&ZRSW$*ifA1NF!WF zZ5uA~IDry#hLxAf$kKhpN>OCY z1eucbDtCG4SllM%!To12Q781|JR)7xrR8@FVI$-%4n8G(c}wcPvPGdZP1>gg>Cs`v zb1u#CVMd$89ENzaVMZS*{L(eARj>Zninus1;|+-hd@r2*X`TS7Kj2t{njOKN8ixU2 znnPO<4_hhk5j$MB_o!_=r{d8O+n2uN;J0t|Hc0-JGc!9ZX5iIQoXMd&*A%eBv-5}E zbA*NSu36xj65Qr5UP1% zL06G(!bSuezrk1xcuA!E57HgzeT7uW1=p+_V?`5mRjJ(idqc~dl$ zZ%cL;SUWsUZ4<4FZV}iW%O(!Tzi#VX#SsHV^oh=z!#0XlpT=3b^+%}>mZfD-YOa5< zU6rP(?73uIm6kh{On5mZQrp(X3p>fh35R=?4r_*YmJd7jxh*|T?e0R7g!qK|Sf|@& zq@?!<7f)lQqzyU`?uWU^pNS)njgRiVO7W=OBPw>9567{?G5ltNkIDE8z@gg+QcB0R^kan1d~S zkjjnH2NWnlsclW0JBGZY=ok{!u|5IPV^}lA)n)hTlil!andY~rc*mVaI^+`m-H@km zD_og=pWfWH4;bWR+vgS800(*E3!Tl~l4T*Wp8m0s%{M6B;;%T80lUPwH(a%qQLmeP zw;eMIIC@Ms_ca34Z|`lkGzU}2V+6@0Z)lcuQuS$rN%5<`sW1mp5e@CJ%hig z3DP?*1thaSt%{#AELlT03OIp_NaA#oV2V}Y#1kSB0EQ;lU%?y4J&8SaPM>)tHMMchqdlg_o_jc z2cy2(NY*m9cK6sdsd$H5n2{N&7RE(~F4E|i>gw!+){I?On9tPjtvsW7*&8Q+V!acAyS3rJ1Fy1BaW91vY0FnW^=MRo`%+Jrz{Yfn=|rMV|N4Or4LE1R7;cB)Xa z6w*zymQpgXtdW}Q5uC$eE3{R%!2Pom?p3~lA$6jvmT@A^Vw$_VEYh`||0}p^)kKWp zx<|Talwr50T$|epL`RA#`wu<1bzIkb5_QFpYA*Cf=G*pM!cz~Pqt6&a^*Twg;TH=& zEsDuCeb0(zyDU{Qt(N&k;_L)^9kjY33pFd~b+5?>?&AJ+8|M{NKDk={Y zk19hy$!0km$@wQ_iLZC?AXt=yUh`Lf7O)9W+JNZnSCJmH=p2sT0_CX@xcG^zBY?VZ zBt`r0@{2vh;&e2dzojZ!51e->djoV&#!zn~cC&Qx*EZ4S`Jo*6mq$i|ob_4f)qWIxo9+70t#(#Yt(ZS* z)wJ5@qRHI2`fbo}Zr8x3jNBUgzqUk{Z@lMQ{(u%>KR}2FJxK5#bBTE8Max0>lnq%1 znGLdIM`#u1G$H}L`A^D|Z$IhFj%zrf42JuUK?+zoTI$FyEsryZ`+*pgOSCrnp) zCoc6fp+lSr{-^vOxa@x;YyWT7|8H#Ve;EFg3jJw5um8jPzaee^pU(eV!vBeg{V(?a dlF;&b{onAha#FBx|9J@gc{G2PlHC8P{V)C#f>Hng literal 0 HcmV?d00001 diff --git a/wlauto/workloads/googlephotos/uiauto/build.sh b/wlauto/workloads/googlephotos/uiauto/build.sh new file mode 100755 index 00000000..fefef54e --- /dev/null +++ b/wlauto/workloads/googlephotos/uiauto/build.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +# CD into build dir if possible - allows building from any directory +script_path='.' +if `readlink -f $0 &>/dev/null`; then + script_path=`readlink -f $0 2>/dev/null` +fi +script_dir=`dirname $script_path` +cd $script_dir + +# Ensure build.xml exists before starting +if [[ ! -f build.xml ]]; then + echo 'Ant build.xml file not found! Check that you are in the right directory.' + exit 9 +fi + +# Copy base classes from wlauto dist +class_dir=bin/classes/com/arm/wlauto/uiauto +base_classes=`python -c "import os, wlauto; print os.path.join(os.path.dirname(wlauto.__file__), 'common', 'android', '*.class')"` +mkdir -p $class_dir +cp $base_classes $class_dir + +# Build and return appropriate exit code if failed +ant build +exit_code=$? +if [[ $exit_code -ne 0 ]]; then + echo "ERROR: 'ant build' exited with code $exit_code" + exit $exit_code +fi + +# If successful move JAR file to workload folder (overwrite previous) +package=com.arm.wlauto.uiauto.googlephotos.jar +rm -f ../$package +if [[ -f bin/$package ]]; then + cp bin/$package .. +else + echo 'ERROR: UiAutomator JAR could not be found!' + exit 9 +fi diff --git a/wlauto/workloads/googlephotos/uiauto/build.xml b/wlauto/workloads/googlephotos/uiauto/build.xml new file mode 100644 index 00000000..648161d0 --- /dev/null +++ b/wlauto/workloads/googlephotos/uiauto/build.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wlauto/workloads/googlephotos/uiauto/project.properties b/wlauto/workloads/googlephotos/uiauto/project.properties new file mode 100644 index 00000000..ce39f2d0 --- /dev/null +++ b/wlauto/workloads/googlephotos/uiauto/project.properties @@ -0,0 +1,14 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-18 diff --git a/wlauto/workloads/googlephotos/uiauto/src/com/arm/wlauto/uiauto/UiAutomation.java b/wlauto/workloads/googlephotos/uiauto/src/com/arm/wlauto/uiauto/UiAutomation.java new file mode 100755 index 00000000..3189e8a1 --- /dev/null +++ b/wlauto/workloads/googlephotos/uiauto/src/com/arm/wlauto/uiauto/UiAutomation.java @@ -0,0 +1,440 @@ +/* Copyright 2014-2016 ARM Limited + * + * 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.arm.wlauto.uiauto.googlephotos; + +import android.os.Bundle; +import android.graphics.Rect; + +// Import the uiautomator libraries +import com.android.uiautomator.core.UiObject; +import com.android.uiautomator.core.UiObjectNotFoundException; +import com.android.uiautomator.core.UiSelector; +import com.android.uiautomator.core.UiScrollable; + +import com.arm.wlauto.uiauto.UxPerfUiAutomation; + +import java.util.concurrent.TimeUnit; +import java.util.LinkedHashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +public class UiAutomation extends UxPerfUiAutomation { + + public static String TAG = "uxperf_googlephotos"; + + public Bundle parameters; + private int viewTimeoutSecs = 10; + private long viewTimeout = TimeUnit.SECONDS.toMillis(viewTimeoutSecs); + + public void runUiAutomation() throws Exception { + parameters = getParams(); + + pauseForSplashScreen(); + setScreenOrientation(ScreenOrientation.NATURAL); + dismissWelcomeView(); + closePromotionPopUp(); + selectWorkingGallery(); + gesturesTest(); + editPhotoColorTest(); + cropPhotoTest(); + rotatePhotoTest(); + unsetScreenOrientation(); + } + + public void pauseForSplashScreen() { + sleep(5); // Pause while splash screen loads + } + + public void dismissWelcomeView() throws Exception { + + // Click through the first two pages and make sure that we don't sign + // in to our google account. This ensures the same set of photographs + // are placed in the camera directory for each run. + + UiObject getStartedButton = + new UiObject(new UiSelector().textContains("Get started") + .className("android.widget.Button")); + + waitObject(getStartedButton, viewTimeoutSecs); + getStartedButton.click(); + + // A network connection is not required for this workload. However, + // when the Google Photos app is invoked from the multiapp workload a + // connection is required for sharing content. Handle the different UI + // pathways when dismissing welcome views here. + UiObject doNotSignInButton = + new UiObject(new UiSelector().resourceId("com.google.android.apps.photos:id/dont_sign_in_button")); + + if (doNotSignInButton.exists()) { + doNotSignInButton.click(); + } else { + UiObject welcomeButton = + getUiObjectByResourceId("com.google.android.apps.photos:id/name", + "android.widget.TextView"); + welcomeButton.click(); + + UiObject useWithoutAccount = + getUiObjectByText("Use without an account", "android.widget.TextView"); + useWithoutAccount.clickAndWaitForNewWindow(); + + // Dismiss welcome views promoting app features + sleep(1); + uiDeviceSwipeLeft(10); + sleep(1); + uiDeviceSwipeLeft(10); + sleep(1); + uiDeviceSwipeLeft(10); + sleep(1); + } + + UiObject nextButton = + new UiObject(new UiSelector().resourceId("com.google.android.apps.photos:id/next_button") + .className("android.widget.ImageView")); + + if (nextButton.exists()) { + nextButton.clickAndWaitForNewWindow(); + } + + UiObject workingFolder = new UiObject(new UiSelector().text("wa-working")); + waitObject(workingFolder, viewTimeoutSecs); + } + + private void gesturesTest() throws Exception { + String testTag = "gestures"; + + // Perform a range of swipe tests while browsing photo gallery + LinkedHashMap testParams = new LinkedHashMap(); + testParams.put("swipe_left", new GestureTestParams(GestureType.UIDEVICE_SWIPE, Direction.LEFT, 10)); + testParams.put("pinch_out", new GestureTestParams(GestureType.PINCH, PinchType.OUT, 100, 50)); + testParams.put("pinch_in", new GestureTestParams(GestureType.PINCH, PinchType.IN, 100, 50)); + testParams.put("swipe_right", new GestureTestParams(GestureType.UIDEVICE_SWIPE, Direction.RIGHT, 10)); + + Iterator> it = testParams.entrySet().iterator(); + + // Select first photograph + selectPhoto(1); + + while (it.hasNext()) { + Map.Entry pair = it.next(); + GestureType type = pair.getValue().gestureType; + Direction dir = pair.getValue().gestureDirection; + PinchType pinch = pair.getValue().pinchType; + int steps = pair.getValue().steps; + int percent = pair.getValue().percent; + + UiObject view = new UiObject(new UiSelector().enabled(true)); + + if (!view.waitForExists(viewTimeout)) { + throw new UiObjectNotFoundException("Could not find \"photo view\"."); + } + + String runName = String.format(testTag + "_" + pair.getKey()); + ActionLogger logger = new ActionLogger(runName, parameters); + logger.start(); + + switch (type) { + case UIDEVICE_SWIPE: + uiDeviceSwipe(dir, steps); + break; + case UIOBJECT_SWIPE: + uiObjectSwipe(view, dir, steps); + break; + case PINCH: + uiObjectVertPinch(view, pinch, steps, percent); + break; + default: + break; + } + + logger.stop(); + } + + UiObject navigateUpButton = + getUiObjectByDescription("Navigate Up", "android.widget.ImageButton"); + navigateUpButton.click(); + } + + public enum Position { LEFT, RIGHT, CENTRE }; + + private class SeekBarTestParams { + + private Position seekBarPosition; + private int percent; + private int steps; + + SeekBarTestParams(final Position position, final int steps, final int percent) { + this.seekBarPosition = position; + this.steps = steps; + this.percent = percent; + } + } + + private void editPhotoColorTest() throws Exception { + String testTag = "edit_photo"; + + // Perform a range of swipe tests while browsing photo gallery + LinkedHashMap testParams = new LinkedHashMap(); + testParams.put("increment_color", new SeekBarTestParams(Position.RIGHT, 10, 20)); + testParams.put("reset_color", new SeekBarTestParams(Position.CENTRE, 0, 0)); + testParams.put("decrement_color", new SeekBarTestParams(Position.LEFT, 10, 20)); + + Iterator> it = testParams.entrySet().iterator(); + + // Select second photograph + selectPhoto(2); + UiObject editView = getUiObjectByResourceId("com.google.android.apps.photos:id/edit", + "android.widget.ImageView"); + editView.click(); + + // Manage potential different spelling of UI element + UiObject editColor = new UiObject(new UiSelector().text("Color")); + UiObject editColour = new UiObject(new UiSelector().text("Colour")); + + if (editColor.exists()) { + editColor.click(); + } else if (editColour.exists()) { + editColour.click(); + } else { + throw new UiObjectNotFoundException(String.format("Could not find \"%s\" \"%s\"", + "Color/Colour", "android.widget.RadioButton")); + } + + UiObject seekBar = getUiObjectByResourceId("com.google.android.apps.photos:id/cpe_strength_seek_bar", + "android.widget.SeekBar"); + + while (it.hasNext()) { + Map.Entry pair = it.next(); + Position pos = pair.getValue().seekBarPosition; + int steps = pair.getValue().steps; + int percent = pair.getValue().percent; + + String runName = String.format(testTag + "_" + pair.getKey()); + ActionLogger logger = new ActionLogger(runName, parameters); + + logger.start(); + seekBarTest(seekBar, pos, steps); + logger.stop(); + } + + closeAndReturn(true); + } + + private void cropPhotoTest() throws Exception { + String testTag = "crop_photo"; + + // To improve travel accuracy perform the slide bar operation slowly + final int steps = 500; + + // Perform a range of swipe tests while browsing photo gallery + LinkedHashMap testParams = new LinkedHashMap(); + testParams.put("tilt_positive", Position.LEFT); + testParams.put("tilt_reset", Position.RIGHT); + testParams.put("tilt_negative", Position.RIGHT); + + Iterator> it = testParams.entrySet().iterator(); + + // Select third photograph + selectPhoto(3); + UiObject editView = getUiObjectByResourceId("com.google.android.apps.photos:id/edit", + "android.widget.ImageView"); + editView.click(); + + UiObject cropTool = getUiObjectByResourceId("com.google.android.apps.photos:id/cpe_crop_tool", + "android.widget.ImageView"); + cropTool.click(); + + UiObject straightenSlider = getUiObjectByResourceId("com.google.android.apps.photos:id/cpe_straighten_slider", + "android.view.View"); + + while (it.hasNext()) { + Map.Entry pair = it.next(); + Position pos = pair.getValue(); + + String runName = String.format(testTag + "_" + pair.getKey()); + ActionLogger logger = new ActionLogger(runName, parameters); + + logger.start(); + slideBarTest(straightenSlider, pos, steps); + logger.stop(); + } + + closeAndReturn(true); + } + + private void rotatePhotoTest() throws Exception { + String testTag = "rotate_photo"; + + String[] subTests = {"anticlockwise_90", "anticlockwise_180", "anticlockwise_270"}; + + // Select fourth photograph + selectPhoto(4); + UiObject editView = getUiObjectByResourceId("com.google.android.apps.photos:id/edit", + "android.widget.ImageView"); + editView.click(); + + UiObject cropTool = getUiObjectByResourceId("com.google.android.apps.photos:id/cpe_crop_tool", + "android.widget.ImageView"); + cropTool.click(); + + UiObject rotate = getUiObjectByResourceId("com.google.android.apps.photos:id/cpe_rotate_90", + "android.widget.ImageView"); + + for (String subTest : subTests) { + String runName = String.format(testTag + "_" + subTest); + ActionLogger logger = new ActionLogger(runName, parameters); + + logger.start(); + rotate.click(); + logger.stop(); + } + + closeAndReturn(true); + } + + // Helper to slide the seekbar during photo edit. + private void seekBarTest(final UiObject view, final Position pos, final int steps) throws Exception { + final int SWIPE_MARGIN_LIMIT = 5; + Rect rect = view.getVisibleBounds(); + + switch (pos) { + case LEFT: + getUiDevice().click(rect.left + SWIPE_MARGIN_LIMIT, rect.centerY()); + break; + case RIGHT: + getUiDevice().click(rect.right - SWIPE_MARGIN_LIMIT, rect.centerY()); + break; + case CENTRE: + view.click(); + break; + default: + break; + } + } + + // Helper to slide the slidebar during photo edit. + private void slideBarTest(final UiObject view, final Position pos, final int steps) throws Exception { + final int SWIPE_MARGIN_LIMIT = 5; + Rect rect = view.getBounds(); + + switch (pos) { + case LEFT: + getUiDevice().drag(rect.left + SWIPE_MARGIN_LIMIT, rect.centerY(), + rect.left + rect.width() / 4, rect.centerY(), + steps); + break; + case RIGHT: + getUiDevice().drag(rect.right - SWIPE_MARGIN_LIMIT, rect.centerY(), + rect.right - rect.width() / 4, rect.centerY(), + steps); + break; + default: + break; + } + } + + public void closePromotionPopUp() throws Exception { + UiObject promoCloseButton = + new UiObject(new UiSelector().resourceId("com.google.android.apps.photos:id/promo_close_button")); + + if (promoCloseButton.exists()) { + promoCloseButton.click(); + } + } + + // Helper to click on the wa-working gallery. + public void selectWorkingGallery() throws Exception { + UiObject workdir = new UiObject(new UiSelector().text("wa-working") + .className("android.widget.TextView")); + + UiScrollable scrollView = new UiScrollable(new UiSelector().scrollable(true)); + + while (!workdir.exists()) { + scrollView.scrollForward(); + } + + workdir.clickAndWaitForNewWindow(); + } + + // Helper to click on an individual photograph based on index in wa-working gallery. + public void selectPhoto(final int index) throws Exception { + UiObject photo = + new UiObject(new UiSelector().resourceId("com.google.android.apps.photos:id/recycler_view") + .childSelector(new UiSelector() + .index(index))); + + // On some versions of the app a non-zero index is used for the + // photographs position while on other versions a zero index is used. + // Try both possiblities before throwing an exception. + if (photo.exists()) { + photo.click(); + } else { + photo = new UiObject(new UiSelector().resourceId("com.google.android.apps.photos:id/recycler_view") + .childSelector(new UiSelector() + .index(index - 1))); + photo.click(); + } + } + + // Helper that accepts, closes and navigates back to application home screen after an edit operation. + // dontsave - True will discard the image. False will save the image + public void closeAndReturn(final boolean dontsave) throws Exception { + + UiObject accept = getUiObjectByDescription("Accept", "android.widget.ImageView"); + accept.click(); + + if (dontsave) { + UiObject close = getUiObjectByDescription("Close editor", "android.widget.ImageView"); + close.click(); + + UiObject discard = getUiObjectByText("DISCARD", "android.widget.Button"); + discard.waitForExists(viewTimeout); + discard.click(); + } else { + UiObject save = getUiObjectByText("SAVE", "android.widget.TextView"); + save.waitForExists(viewTimeout); + save.click(); + } + + UiObject navigateUpButton = + new UiObject(new UiSelector().descriptionContains("Navigate Up") + .className("android.widget.ImageButton")); + navigateUpButton.waitForExists(viewTimeout); + navigateUpButton.click(); + } + + // Helper to tag an individual photograph based on the index in wa-working + // gallery. After long clicking it tags the photograph with a tick in the + // corner of the image to indicate that the photograph has been selected + public void tagPhoto(final int index) throws Exception { + UiObject photo = + new UiObject(new UiSelector().resourceId("com.google.android.apps.photos:id/recycler_view") + .childSelector(new UiSelector() + .index(index))); + + // On some versions of the app a non-zero index is used for the + // photographs position while on other versions a zero index is used. + // Try both possiblities before throwing an exception. + if (photo.exists()) { + uiObjectPerformLongClick(photo, 100); + } else { + photo = new UiObject(new UiSelector().resourceId("com.google.android.apps.photos:id/recycler_view") + .childSelector(new UiSelector() + .index(index - 1))); + uiObjectPerformLongClick(photo, 100); + } + } +}