diff options
author | Kevin Jin <kjin@google.com> | 2014-06-26 18:56:59 -0700 |
---|---|---|
committer | Kevin Jin <kjin@google.com> | 2014-06-27 11:42:34 -0700 |
commit | cf1203b8078bed407ed0035c201746fae136439a (patch) | |
tree | e2401a8d6aede6b7493336e313a2f3fb6b295e91 /src | |
parent | 27b635e33f18c439d6e511d71c762ae1352b1bc8 (diff) | |
download | droiddriver-cf1203b8078bed407ed0035c201746fae136439a.tar.gz |
add validators for exmpted classes and scroll action
Change-Id: Iad22351b46df771e7a9f92edb9d84df44b5fe572
Diffstat (limited to 'src')
13 files changed, 278 insertions, 31 deletions
diff --git a/src/com/google/android/droiddriver/actions/ScrollAction.java b/src/com/google/android/droiddriver/actions/ScrollAction.java new file mode 100644 index 0000000..6305c83 --- /dev/null +++ b/src/com/google/android/droiddriver/actions/ScrollAction.java @@ -0,0 +1,23 @@ +/* + * 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.actions; + +/** + * Marker interface for a scroll action. + */ +public interface ScrollAction { +} diff --git a/src/com/google/android/droiddriver/actions/SwipeAction.java b/src/com/google/android/droiddriver/actions/SwipeAction.java index a939536..6837741 100644 --- a/src/com/google/android/droiddriver/actions/SwipeAction.java +++ b/src/com/google/android/droiddriver/actions/SwipeAction.java @@ -30,7 +30,7 @@ import com.google.android.droiddriver.util.Strings.ToStringHelper; /** * An action that swipes the touch screen. */ -public class SwipeAction extends EventAction { +public class SwipeAction extends EventAction implements ScrollAction { // Milliseconds between synthesized ACTION_MOVE events. // Note: ACTION_MOVE_INTERVAL is the minimum interval between injected events; // the actual interval typically is longer. diff --git a/src/com/google/android/droiddriver/actions/accessibility/AccessibilityScrollAction.java b/src/com/google/android/droiddriver/actions/accessibility/AccessibilityScrollAction.java index 88e7517..bdeddc8 100644 --- a/src/com/google/android/droiddriver/actions/accessibility/AccessibilityScrollAction.java +++ b/src/com/google/android/droiddriver/actions/accessibility/AccessibilityScrollAction.java @@ -19,13 +19,14 @@ package com.google.android.droiddriver.actions.accessibility; import android.view.accessibility.AccessibilityNodeInfo; import com.google.android.droiddriver.UiElement; +import com.google.android.droiddriver.actions.ScrollAction; import com.google.android.droiddriver.scroll.Direction.PhysicalDirection; import com.google.android.droiddriver.util.Strings; /** * An {@link AccessibilityAction} that scrolls an UiElement. */ -public class AccessibilityScrollAction extends AccessibilityAction { +public class AccessibilityScrollAction extends AccessibilityAction implements ScrollAction { private final PhysicalDirection direction; public AccessibilityScrollAction(PhysicalDirection direction) { diff --git a/src/com/google/android/droiddriver/base/BaseUiElement.java b/src/com/google/android/droiddriver/base/BaseUiElement.java index 3d74bdc..3122ba3 100644 --- a/src/com/google/android/droiddriver/base/BaseUiElement.java +++ b/src/com/google/android/droiddriver/base/BaseUiElement.java @@ -54,7 +54,7 @@ public abstract class BaseUiElement<R, E extends BaseUiElement<R, E>> implements public static final String ATTRIB_NOT_VISIBLE = "NotVisible"; private UiElementActor uiElementActor = EventUiElementActor.INSTANCE; - private Validator[] validators = {}; + private Validator validator = null; @SuppressWarnings("unchecked") @Override @@ -163,13 +163,17 @@ public abstract class BaseUiElement<R, E extends BaseUiElement<R, E>> implements @Override public boolean perform(Action action) { Logs.call(this, "perform", action); - if (getParent() != null) {// don't check root - for (Validator validator : validators) { - if (!validator.isValid(this)) { - throw new DroidDriverException(validator + " failed"); - } + if (validator != null && validator.isApplicable(this, action)) { + String failure = validator.validate(this, action); + if (failure != null) { + throw new DroidDriverException(toString() + " failed validation: " + failure); } } + + // timeoutMillis <= 0 means no need to wait + if (action.getTimeoutMillis() <= 0) { + return doPerform(action); + } return performAndWait(action); } @@ -180,11 +184,6 @@ public abstract class BaseUiElement<R, E extends BaseUiElement<R, E>> implements protected abstract void doPerformAndWait(FutureTask<Boolean> futureTask, long timeoutMillis); private boolean performAndWait(final Action action) { - // timeoutMillis <= 0 means no need to wait - if (action.getTimeoutMillis() <= 0) { - return doPerform(action); - } - FutureTask<Boolean> futureTask = new FutureTask<Boolean>(new Callable<Boolean>() { @Override public Boolean call() { @@ -192,6 +191,7 @@ public abstract class BaseUiElement<R, E extends BaseUiElement<R, E>> implements } }); doPerformAndWait(futureTask, action.getTimeoutMillis()); + try { return futureTask.get(); } catch (ExecutionException e) { @@ -293,7 +293,10 @@ public abstract class BaseUiElement<R, E extends BaseUiElement<R, E>> implements this.uiElementActor = uiElementActor; } - public void setValidators(Validator[] validators) { - this.validators = validators; + /** + * Sets the validator to check when {@link #perform(Action)} is called. + */ + public void setValidator(Validator validator) { + this.validator = validator; } } diff --git a/src/com/google/android/droiddriver/scroll/AccessibilityEventScrollStepStrategy.java b/src/com/google/android/droiddriver/scroll/AccessibilityEventScrollStepStrategy.java index 186bff8..f903746 100644 --- a/src/com/google/android/droiddriver/scroll/AccessibilityEventScrollStepStrategy.java +++ b/src/com/google/android/droiddriver/scroll/AccessibilityEventScrollStepStrategy.java @@ -188,11 +188,13 @@ public class AccessibilityEventScrollStepStrategy implements ScrollStepStrategy @Override public void doScroll(final UiElement container, final PhysicalDirection direction) { - // We do not call container.scroll(direction) because container.scroll - // internally calls UiAutomation.executeAndWaitForEvent which clears the + // We do not call container.scroll(direction) because it uses a SwipeAction + // with positive tTimeoutMillis. That path calls + // UiAutomation.executeAndWaitForEvent which clears the // AccessibilityEvent Queue, preventing us from fetching the last // accessibility event to determine if scrolling has finished. - SwipeAction.toScroll(direction).perform(container); + container + .perform(new SwipeAction(direction, SwipeAction.getScrollSteps(), false /* drag */, 0L/* timeoutMillis */)); } /** diff --git a/src/com/google/android/droiddriver/uiautomation/AccessibilityDriver.java b/src/com/google/android/droiddriver/uiautomation/AccessibilityDriver.java index 63f6a59..6e7441d 100644 --- a/src/com/google/android/droiddriver/uiautomation/AccessibilityDriver.java +++ b/src/com/google/android/droiddriver/uiautomation/AccessibilityDriver.java @@ -20,13 +20,20 @@ import android.app.Instrumentation; import android.view.accessibility.AccessibilityNodeInfo; import com.google.android.droiddriver.validators.DefaultAccessibilityValidator; +import com.google.android.droiddriver.validators.ExemptRootValidator; +import com.google.android.droiddriver.validators.FirstApplicableValidator; +import com.google.android.droiddriver.validators.ExemptedClassesValidator; +import com.google.android.droiddriver.validators.ExemptScrollActionValidator; import com.google.android.droiddriver.validators.Validator; /** * A UiAutomationDriver that validates accessibility. */ public class AccessibilityDriver extends UiAutomationDriver { - private static final Validator[] VALIDATORS = {new DefaultAccessibilityValidator()}; + private Validator validator = new FirstApplicableValidator(new ExemptRootValidator(), + new ExemptScrollActionValidator(), new ExemptedClassesValidator(), + // TODO: ImageViewValidator + new DefaultAccessibilityValidator()); public AccessibilityDriver(Instrumentation instrumentation) { super(instrumentation); @@ -36,7 +43,21 @@ public class AccessibilityDriver extends UiAutomationDriver { protected UiAutomationElement newUiElement(AccessibilityNodeInfo rawElement, UiAutomationElement parent) { UiAutomationElement newUiElement = super.newUiElement(rawElement, parent); - newUiElement.setValidators(VALIDATORS); + newUiElement.setValidator(validator); return newUiElement; } + + /** + * Gets the current validator. + */ + public Validator getValidator() { + return validator; + } + + /** + * Sets the validator to check. + */ + public void setValidator(Validator validator) { + this.validator = validator; + } } diff --git a/src/com/google/android/droiddriver/validators/DefaultAccessibilityValidator.java b/src/com/google/android/droiddriver/validators/DefaultAccessibilityValidator.java index e3c0778..b220a1b 100644 --- a/src/com/google/android/droiddriver/validators/DefaultAccessibilityValidator.java +++ b/src/com/google/android/droiddriver/validators/DefaultAccessibilityValidator.java @@ -19,20 +19,24 @@ package com.google.android.droiddriver.validators; import android.text.TextUtils; import com.google.android.droiddriver.UiElement; +import com.google.android.droiddriver.actions.Action; /** - * Validates accessibility. + * Fall-back Validator for accessibility. */ -// TODO: Treats various types of UiElement as TalkBack does. public class DefaultAccessibilityValidator implements Validator { @Override - public boolean isValid(UiElement element) { - return !TextUtils.isEmpty(element.getContentDescription()) - || !TextUtils.isEmpty(element.getText()); + public boolean isApplicable(UiElement element, Action action) { + return true; } @Override - public String toString() { - return "Check non-empty c ontent description or text"; + public String validate(UiElement element, Action action) { + return hasContentDescriptionOrText(element) ? null : "no content description or text"; + } + + private boolean hasContentDescriptionOrText(UiElement element) { + return !TextUtils.isEmpty(element.getContentDescription()) + || !TextUtils.isEmpty(element.getText()); } } diff --git a/src/com/google/android/droiddriver/validators/ExemptRootValidator.java b/src/com/google/android/droiddriver/validators/ExemptRootValidator.java new file mode 100644 index 0000000..1846e37 --- /dev/null +++ b/src/com/google/android/droiddriver/validators/ExemptRootValidator.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.validators; + +import com.google.android.droiddriver.UiElement; +import com.google.android.droiddriver.actions.Action; + +/** + * Exempts root from validation. + */ +public class ExemptRootValidator implements Validator { + @Override + public boolean isApplicable(UiElement element, Action action) { + return element.getParent() == null; // don't check root + } + + @Override + public String validate(UiElement element, Action action) { + return null; + } +} diff --git a/src/com/google/android/droiddriver/validators/ExemptScrollActionValidator.java b/src/com/google/android/droiddriver/validators/ExemptScrollActionValidator.java new file mode 100644 index 0000000..d6829ed --- /dev/null +++ b/src/com/google/android/droiddriver/validators/ExemptScrollActionValidator.java @@ -0,0 +1,37 @@ +/* + * 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.validators; + +import com.google.android.droiddriver.UiElement; +import com.google.android.droiddriver.actions.Action; +import com.google.android.droiddriver.actions.ScrollAction; + +/** + * {@link ScrollAction} is not validated as TalkBack does not check the + * container. + */ +public class ExemptScrollActionValidator implements Validator { + @Override + public boolean isApplicable(UiElement element, Action action) { + return action instanceof ScrollAction; + } + + @Override + public String validate(UiElement element, Action action) { + return null; + } +} diff --git a/src/com/google/android/droiddriver/validators/ExemptedClassesValidator.java b/src/com/google/android/droiddriver/validators/ExemptedClassesValidator.java new file mode 100644 index 0000000..b04ffc5 --- /dev/null +++ b/src/com/google/android/droiddriver/validators/ExemptedClassesValidator.java @@ -0,0 +1,60 @@ +/* + * 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.validators; + +import android.util.Log; + +import com.google.android.droiddriver.UiElement; +import com.google.android.droiddriver.actions.Action; +import com.google.android.droiddriver.util.Logs; + +/** + * Always validates the classes that TalkBack always has speech. + */ +public class ExemptedClassesValidator implements Validator { + private static final Class<?>[] EXEMPTED_CLASSES = {android.widget.Spinner.class, + android.widget.EditText.class, android.widget.SeekBar.class, + android.widget.AbsListView.class, android.widget.TabWidget.class}; + + @Override + public boolean isApplicable(UiElement element, Action action) { + String className = element.getClassName(); + if (className == null || className.isEmpty()) { + return false; + } + + Class<?> elementClass = null; + try { + elementClass = Class.forName(className); + } catch (ClassNotFoundException e) { + Logs.log(Log.WARN, e); + return false; + } + + for (Class<?> clazz : EXEMPTED_CLASSES) { + if (clazz.isAssignableFrom(elementClass)) { + return true; + } + } + return false; + } + + @Override + public String validate(UiElement element, Action action) { + return null; + } +} diff --git a/src/com/google/android/droiddriver/validators/FirstApplicableValidator.java b/src/com/google/android/droiddriver/validators/FirstApplicableValidator.java new file mode 100644 index 0000000..1c89907 --- /dev/null +++ b/src/com/google/android/droiddriver/validators/FirstApplicableValidator.java @@ -0,0 +1,47 @@ +/* + * 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.validators; + +import com.google.android.droiddriver.UiElement; +import com.google.android.droiddriver.actions.Action; + +/** + * Iterates an array of validators and validates against the first one that is + * applicable. Note the order of validators matters. + */ +public class FirstApplicableValidator implements Validator { + private final Validator[] validators; + + public FirstApplicableValidator(Validator... validators) { + this.validators = validators; + } + + @Override + public boolean isApplicable(UiElement element, Action action) { + return true; + } + + @Override + public String validate(UiElement element, Action action) { + for (Validator validator : validators) { + if (validator.isApplicable(element, action)) { + return validator.validate(element, action); + } + } + return "no applicable validator"; + } +} diff --git a/src/com/google/android/droiddriver/validators/Validator.java b/src/com/google/android/droiddriver/validators/Validator.java index f7812ed..4c0debb 100644 --- a/src/com/google/android/droiddriver/validators/Validator.java +++ b/src/com/google/android/droiddriver/validators/Validator.java @@ -17,6 +17,7 @@ package com.google.android.droiddriver.validators; import com.google.android.droiddriver.UiElement; +import com.google.android.droiddriver.actions.Action; /** * Interface for validating a UiElement, checked when an action is performed. @@ -25,7 +26,14 @@ import com.google.android.droiddriver.UiElement; */ public interface Validator { /** - * Returns true if {@code element} is valid. + * Returns true if this {@link Validator} applies to {@code element} on this + * {@code action}. */ - boolean isValid(UiElement element); + boolean isApplicable(UiElement element, Action action); + + /** + * Returns {@code null} if {@code element} is valid on this {@code action}, + * otherwise a string describing the failure. + */ + String validate(UiElement element, Action action); } diff --git a/src/com/google/android/droiddriver/validators/VisibilityValidator.java b/src/com/google/android/droiddriver/validators/VisibilityValidator.java index 44570cf..df19bd7 100644 --- a/src/com/google/android/droiddriver/validators/VisibilityValidator.java +++ b/src/com/google/android/droiddriver/validators/VisibilityValidator.java @@ -17,13 +17,19 @@ package com.google.android.droiddriver.validators; import com.google.android.droiddriver.UiElement; +import com.google.android.droiddriver.actions.Action; /** * Validates visibility. */ public class VisibilityValidator implements Validator { @Override - public boolean isValid(UiElement element) { - return element.isVisible(); + public boolean isApplicable(UiElement element, Action action) { + return true; + } + + @Override + public String validate(UiElement element, Action action) { + return element.isVisible() ? null : "invisible"; } } |