diff options
author | Kevin Jin <kjin@google.com> | 2014-03-10 17:38:55 -0700 |
---|---|---|
committer | Kevin Jin <kjin@google.com> | 2014-03-10 18:12:51 -0700 |
commit | 1d0f3c02fc3673159f2b6496823fd7b9228b7891 (patch) | |
tree | 0041a4f9f56be50a372cd579bb0388fafda1e836 /src | |
parent | eacc6c8c1f05ad4c6d9ca4c612204240b9dc1d4e (diff) | |
download | droiddriver-1d0f3c02fc3673159f2b6496823fd7b9228b7891.tar.gz |
Wraps calls to UiAutomation API.
Currently supports fail-fast if UiAutomation throws IllegalStateException.
Change-Id: I1e6472d113a63b14d3615ef0084ea00d209f7e63
Diffstat (limited to 'src')
9 files changed, 130 insertions, 33 deletions
diff --git a/src/com/google/android/droiddriver/exceptions/UnrecoverableException.java b/src/com/google/android/droiddriver/exceptions/UnrecoverableException.java new file mode 100644 index 0000000..c3b069f --- /dev/null +++ b/src/com/google/android/droiddriver/exceptions/UnrecoverableException.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2013 DroidDriver committers + * + * 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.google.android.droiddriver.exceptions; + +import com.google.android.droiddriver.helpers.BaseDroidDriverTest; + +/** + * When an {@link UnrecoverableException} occurs, the rest of the tests are + * going to fail as well, therefore running them only adds noise to the report. + * {@link BaseDroidDriverTest} will skip remaining tests when this is thrown. + */ +@SuppressWarnings("serial") +public class UnrecoverableException extends RuntimeException { + public UnrecoverableException(String message) { + super(message); + } + + public UnrecoverableException(Throwable throwable) { + super(throwable); + } +} diff --git a/src/com/google/android/droiddriver/helpers/BaseDroidDriverTest.java b/src/com/google/android/droiddriver/helpers/BaseDroidDriverTest.java index 4f125c8..ca5ac99 100644 --- a/src/com/google/android/droiddriver/helpers/BaseDroidDriverTest.java +++ b/src/com/google/android/droiddriver/helpers/BaseDroidDriverTest.java @@ -22,6 +22,7 @@ import android.os.Debug; import android.util.Log; import com.google.android.droiddriver.DroidDriver; +import com.google.android.droiddriver.exceptions.UnrecoverableException; import com.google.android.droiddriver.util.FileUtils; import com.google.android.droiddriver.util.Logs; @@ -97,7 +98,7 @@ public abstract class BaseDroidDriverTest<T extends Activity> extends } try { - if (failure instanceof UnrecoverableFailure) { + if (failure instanceof UnrecoverableException) { skipRemainingTests = true; } if (failure instanceof OutOfMemoryError) { diff --git a/src/com/google/android/droiddriver/helpers/UnrecoverableFailure.java b/src/com/google/android/droiddriver/helpers/UnrecoverableFailure.java index 9adbac8..e8de98e 100644 --- a/src/com/google/android/droiddriver/helpers/UnrecoverableFailure.java +++ b/src/com/google/android/droiddriver/helpers/UnrecoverableFailure.java @@ -16,11 +16,18 @@ package com.google.android.droiddriver.helpers; +import com.google.android.droiddriver.helpers.BaseDroidDriverTest; + /** * When an {@code UnrecoverableFailure} occurs, the rest of the tests are going * to fail as well, therefore running them only adds noise to the report. * {@link BaseDroidDriverTest} will skip remaining tests when this is thrown. + * + * @deprecated Use + * {@link com.google.android.droiddriver.exceptions.UnrecoverableException} + * instead. */ +@Deprecated @SuppressWarnings("serial") public class UnrecoverableFailure extends RuntimeException { public UnrecoverableFailure(String message) { diff --git a/src/com/google/android/droiddriver/instrumentation/InstrumentationContext.java b/src/com/google/android/droiddriver/instrumentation/InstrumentationContext.java index a549a0f..f68dc6e 100644 --- a/src/com/google/android/droiddriver/instrumentation/InstrumentationContext.java +++ b/src/com/google/android/droiddriver/instrumentation/InstrumentationContext.java @@ -63,7 +63,7 @@ class InstrumentationContext extends DroidDriverContext { return injector; } - public ViewElement getUiElement(View view, ViewElement parent) { + ViewElement getUiElement(View view, ViewElement parent) { ViewElement element = map.get(view); if (element == null) { element = new ViewElement(this, view, parent); diff --git a/src/com/google/android/droiddriver/scroll/AccessibilityEventScrollStepStrategy.java b/src/com/google/android/droiddriver/scroll/AccessibilityEventScrollStepStrategy.java index f945d55..a678d37 100644 --- a/src/com/google/android/droiddriver/scroll/AccessibilityEventScrollStepStrategy.java +++ b/src/com/google/android/droiddriver/scroll/AccessibilityEventScrollStepStrategy.java @@ -23,6 +23,7 @@ import android.view.accessibility.AccessibilityEvent; import com.google.android.droiddriver.DroidDriver; import com.google.android.droiddriver.UiElement; import com.google.android.droiddriver.actions.SwipeAction; +import com.google.android.droiddriver.exceptions.UnrecoverableException; import com.google.android.droiddriver.finders.Finder; import com.google.android.droiddriver.scroll.Direction.Axis; import com.google.android.droiddriver.scroll.Direction.DirectionConverter; @@ -175,6 +176,8 @@ public class AccessibilityEventScrollStepStrategy implements ScrollStepStrategy doScroll(container, direction); } }, filter, scrollEventTimeoutMillis); + } catch (IllegalStateException e) { + throw new UnrecoverableException(e); } catch (TimeoutException e) { // We expect this because LastScrollEventFilter.accept always returns false. } diff --git a/src/com/google/android/droiddriver/uiautomation/UiAutomationContext.java b/src/com/google/android/droiddriver/uiautomation/UiAutomationContext.java index ac114cd..35a4483 100644 --- a/src/com/google/android/droiddriver/uiautomation/UiAutomationContext.java +++ b/src/com/google/android/droiddriver/uiautomation/UiAutomationContext.java @@ -23,6 +23,7 @@ import android.view.accessibility.AccessibilityNodeInfo; import com.google.android.droiddriver.actions.InputInjector; import com.google.android.droiddriver.base.DroidDriverContext; +import com.google.android.droiddriver.exceptions.UnrecoverableException; import com.google.android.droiddriver.finders.ByXPath; import com.google.common.collect.MapMaker; @@ -41,8 +42,13 @@ class UiAutomationContext extends DroidDriverContext { this.driver = driver; this.injector = new InputInjector() { @Override - public boolean injectInputEvent(InputEvent event) { - return uiAutomation.injectInputEvent(event, true /* sync */); + public boolean injectInputEvent(final InputEvent event) { + return callUiAutomation(new UiAutomationCallable<Boolean>() { + @Override + public Boolean call(UiAutomation uiAutomation) { + return uiAutomation.injectInputEvent(event, true /* sync */); + } + }); } }; } @@ -57,7 +63,7 @@ class UiAutomationContext extends DroidDriverContext { return injector; } - public UiAutomationElement getUiElement(AccessibilityNodeInfo node, UiAutomationElement parent) { + UiAutomationElement getUiElement(AccessibilityNodeInfo node, UiAutomationElement parent) { UiAutomationElement element = map.get(node); if (element == null) { element = new UiAutomationElement(this, node, parent); @@ -72,7 +78,20 @@ class UiAutomationContext extends DroidDriverContext { ByXPath.clearData(); } - public UiAutomation getUiAutomation() { - return uiAutomation; + interface UiAutomationCallable<T> { + T call(UiAutomation uiAutomation); + } + + /* + * Wraps calls to UiAutomation API. Currently supports fail-fast if + * UiAutomation throws IllegalStateException, which occurs when the connection + * to UiAutomation service is lost. + */ + <T> T callUiAutomation(UiAutomationCallable<T> uiAutomationCallable) { + try { + return uiAutomationCallable.call(uiAutomation); + } catch (IllegalStateException e) { + throw new UnrecoverableException(e); + } } } diff --git a/src/com/google/android/droiddriver/uiautomation/UiAutomationDriver.java b/src/com/google/android/droiddriver/uiautomation/UiAutomationDriver.java index 5089acf..ad30693 100644 --- a/src/com/google/android/droiddriver/uiautomation/UiAutomationDriver.java +++ b/src/com/google/android/droiddriver/uiautomation/UiAutomationDriver.java @@ -26,6 +26,7 @@ import android.view.accessibility.AccessibilityNodeInfo; import com.google.android.droiddriver.base.BaseDroidDriver; import com.google.android.droiddriver.exceptions.TimeoutException; +import com.google.android.droiddriver.uiautomation.UiAutomationContext.UiAutomationCallable; import com.google.android.droiddriver.util.Logs; import com.google.common.primitives.Longs; @@ -42,11 +43,9 @@ public class UiAutomationDriver extends BaseDroidDriver { private static final long QUIET_TIME_TO_BE_CONSIDERD_IDLE_STATE = 500;// ms private final UiAutomationContext context; - private final UiAutomation uiAutomation; private final UiAutomationUiDevice uiDevice; public UiAutomationDriver(Instrumentation instrumentation) { - this.uiAutomation = instrumentation.getUiAutomation(); this.context = new UiAutomationContext(instrumentation, this); this.uiDevice = new UiAutomationUiDevice(context); } @@ -62,15 +61,28 @@ public class UiAutomationDriver extends BaseDroidDriver { } private AccessibilityNodeInfo getRootNode() { - long timeoutMillis = getPoller().getTimeoutMillis(); - try { - uiAutomation.waitForIdle(QUIET_TIME_TO_BE_CONSIDERD_IDLE_STATE, timeoutMillis); - } catch (java.util.concurrent.TimeoutException e) { - throw new TimeoutException(e); - } + final long timeoutMillis = getPoller().getTimeoutMillis(); + context.callUiAutomation(new UiAutomationCallable<Void>() { + @Override + public Void call(UiAutomation uiAutomation) { + try { + uiAutomation.waitForIdle(QUIET_TIME_TO_BE_CONSIDERD_IDLE_STATE, timeoutMillis); + return null; + } catch (java.util.concurrent.TimeoutException e) { + throw new TimeoutException(e); + } + } + }); + long end = SystemClock.uptimeMillis() + timeoutMillis; while (true) { - AccessibilityNodeInfo root = uiAutomation.getRootInActiveWindow(); + AccessibilityNodeInfo root = + context.callUiAutomation(new UiAutomationCallable<AccessibilityNodeInfo>() { + @Override + public AccessibilityNodeInfo call(UiAutomation uiAutomation) { + return uiAutomation.getRootInActiveWindow(); + } + }); if (root != null) { return root; } diff --git a/src/com/google/android/droiddriver/uiautomation/UiAutomationElement.java b/src/com/google/android/droiddriver/uiautomation/UiAutomationElement.java index 9a56fbe..8b81485 100644 --- a/src/com/google/android/droiddriver/uiautomation/UiAutomationElement.java +++ b/src/com/google/android/droiddriver/uiautomation/UiAutomationElement.java @@ -18,6 +18,7 @@ package com.google.android.droiddriver.uiautomation; import static com.google.android.droiddriver.util.TextUtils.charSequenceToString; +import android.app.UiAutomation; import android.app.UiAutomation.AccessibilityEventFilter; import android.graphics.Rect; import android.view.accessibility.AccessibilityEvent; @@ -26,6 +27,7 @@ import android.view.accessibility.AccessibilityNodeInfo; import com.google.android.droiddriver.actions.InputInjector; import com.google.android.droiddriver.base.BaseUiElement; import com.google.android.droiddriver.finders.Attribute; +import com.google.android.droiddriver.uiautomation.UiAutomationContext.UiAutomationCallable; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -170,21 +172,29 @@ public class UiAutomationElement extends BaseUiElement { } @Override - protected void doPerformAndWait(FutureTask<Boolean> futureTask, long timeoutMillis) { - try { - context.getUiAutomation().executeAndWaitForEvent(futureTask, ANY_EVENT_FILTER, timeoutMillis); - } catch (TimeoutException e) { - // This is for sync'ing with Accessibility API on best-effort because - // it is not reliable. - // Exception is ignored here. Tests will fail anyways if this is - // critical. - // Actions should usually trigger some AccessibilityEvent's, but some - // widgets fail to do so, resulting in stale AccessibilityNodeInfo's. As a - // work-around, force to clear the AccessibilityNodeInfoCache. - // A legitimate case of no AccessibilityEvent is when scrolling has - // reached the end, but we cannot tell whether it's legitimate or the - // widget has bugs, so clearAccessibilityNodeInfoCache anyways. - context.getDriver().clearAccessibilityNodeInfoCacheHack(); - } + protected void doPerformAndWait(final FutureTask<Boolean> futureTask, final long timeoutMillis) { + context.callUiAutomation(new UiAutomationCallable<Void>() { + + @Override + public Void call(UiAutomation uiAutomation) { + try { + uiAutomation.executeAndWaitForEvent(futureTask, ANY_EVENT_FILTER, timeoutMillis); + } catch (TimeoutException e) { + // This is for sync'ing with Accessibility API on best-effort because + // it is not reliable. + // Exception is ignored here. Tests will fail anyways if this is + // critical. + // Actions should usually trigger some AccessibilityEvent's, but some + // widgets fail to do so, resulting in stale AccessibilityNodeInfo's. + // As a work-around, force to clear the AccessibilityNodeInfoCache. + // A legitimate case of no AccessibilityEvent is when scrolling has + // reached the end, but we cannot tell whether it's legitimate or the + // widget has bugs, so clearAccessibilityNodeInfoCache anyways. + context.getDriver().clearAccessibilityNodeInfoCacheHack(); + } + return null; + } + + }); } } diff --git a/src/com/google/android/droiddriver/uiautomation/UiAutomationUiDevice.java b/src/com/google/android/droiddriver/uiautomation/UiAutomationUiDevice.java index bb28c68..a376cb6 100644 --- a/src/com/google/android/droiddriver/uiautomation/UiAutomationUiDevice.java +++ b/src/com/google/android/droiddriver/uiautomation/UiAutomationUiDevice.java @@ -16,10 +16,13 @@ package com.google.android.droiddriver.uiautomation; +import android.app.UiAutomation; import android.graphics.Bitmap; import android.util.Log; import com.google.android.droiddriver.base.BaseUiDevice; +import com.google.android.droiddriver.exceptions.UnrecoverableException; +import com.google.android.droiddriver.uiautomation.UiAutomationContext.UiAutomationCallable; import com.google.android.droiddriver.util.Logs; class UiAutomationUiDevice extends BaseUiDevice { @@ -32,7 +35,14 @@ class UiAutomationUiDevice extends BaseUiDevice { @Override protected Bitmap takeScreenshot() { try { - return context.getUiAutomation().takeScreenshot(); + return context.callUiAutomation(new UiAutomationCallable<Bitmap>() { + @Override + public Bitmap call(UiAutomation uiAutomation) { + return uiAutomation.takeScreenshot(); + } + }); + } catch (UnrecoverableException e) { + throw e; } catch (Throwable e) { Logs.log(Log.ERROR, e); return null; |