Skip to content

Commit

Permalink
more eventto hacking
Browse files Browse the repository at this point in the history
  • Loading branch information
brettchabot committed Sep 11, 2024
1 parent 41506ff commit 17233ad
Show file tree
Hide file tree
Showing 28 changed files with 746 additions and 102 deletions.
Binary file added .DS_Store
Binary file not shown.
1 change: 1 addition & 0 deletions BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ maven_repository(
"//espresso/intents/java/androidx/test/espresso/intent:espresso_intents_maven_artifact",
"//espresso/remote/java/androidx/test/espresso/remote:espresso_remote_maven_artifact",
"//espresso/web/java/androidx/test/espresso/web:espresso_web_maven_artifact",
"//eventto/java/androidx/test/eventto:eventto_maven_artifact",
"//ext/junit/java/androidx/test/ext/junit:junit_maven_artifact",
"//ext/truth/java/androidx/test/ext/truth:truth_maven_artifact",
"//ktx/core/java/androidx/test/core:core_maven_artifact",
Expand Down
21 changes: 11 additions & 10 deletions build_extensions/axt_versions.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@ Use tools/release/validate_and_propagate_versions.sh to propagate these versions
//:axt_m2_repository and gradle-tests/settings.gradle
"""

RUNNER_VERSION = "1.7.0-alpha01"
RULES_VERSION = "1.7.0-alpha01"
MONITOR_VERSION = "1.8.0-alpha01"
ESPRESSO_VERSION = "3.7.0-alpha01"
CORE_VERSION = "1.7.0-alpha01"
ESPRESSO_DEVICE_VERSION = "1.1.0-alpha01"
ANDROIDX_JUNIT_VERSION = "1.3.0-alpha01"
ANDROIDX_TRUTH_VERSION = "1.7.0-alpha01"
ORCHESTRATOR_VERSION = "1.6.0-alpha01"
SERVICES_VERSION = "1.6.0-alpha01"
RUNNER_VERSION = "1.7.0-alpha02"
RULES_VERSION = "1.7.0-alpha02"
MONITOR_VERSION = "1.8.0-alpha02"
ESPRESSO_VERSION = "3.7.0-alpha02"
CORE_VERSION = "1.7.0-alpha02"
ESPRESSO_DEVICE_VERSION = "1.1.0-alpha02"
EVENTTO_VERSION = "0.0.1-alpha02"
ANDROIDX_JUNIT_VERSION = "1.3.0-alpha02"
ANDROIDX_TRUTH_VERSION = "1.7.0-alpha02"
ORCHESTRATOR_VERSION = "1.6.0-alpha02"
SERVICES_VERSION = "1.6.0-alpha02"

# Full maven artifact strings for apks.
SERVICES_APK_ARTIFACT = "androidx.test.services:test-services:%s" % SERVICES_VERSION
Expand Down
2 changes: 2 additions & 0 deletions build_extensions/maven/maven_registry.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ load(
"CORE_VERSION",
"ESPRESSO_DEVICE_VERSION",
"ESPRESSO_VERSION",
"EVENTTO_VERSION",
"MONITOR_VERSION",
"ORCHESTRATOR_VERSION",
"RULES_VERSION",
Expand Down Expand Up @@ -36,6 +37,7 @@ _TARGET_TO_MAVEN_ARTIFACT = {
"//espresso/intents/java/": "androidx.test.espresso:espresso-intents:%s" % ESPRESSO_VERSION,
"//espresso/remote/java/": "androidx.test.espresso:espresso-remote:%s" % ESPRESSO_VERSION,
"//espresso/web/java/": "androidx.test.espresso:espresso-web:%s" % ESPRESSO_VERSION,
"//eventto/java/": "androidx.test.eventto:eventto:%s" % EVENTTO_VERSION,
"//ext/junit/java/": "androidx.test.ext:junit:%s" % ANDROIDX_JUNIT_VERSION,
"//ktx/ext/junit/java/": "androidx.test.ext:junit-ktx:%s" % ANDROIDX_JUNIT_VERSION,
"//ext/truth/java/": "androidx.test.ext:truth:%s" % ANDROIDX_TRUTH_VERSION,
Expand Down
1 change: 1 addition & 0 deletions espresso/core/java/androidx/test/espresso/base/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ android_library(
"Interrogator.java",
"LooperIdlingResourceInterrogationHandler.java",
],
visibility = ["//visibility:public"],
deps = [
"//espresso/core/java/androidx/test/espresso:interface",
"//espresso/core/java/androidx/test/espresso/util",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ public ActiveRootLister provideActiveRootLister(RootsOracle rootsOracle) {
@Provides
@Singleton
public EventInjector provideEventInjector() {
// Adroid uses input manager to inject events.
// Android uses input manager to inject events.
// Instrumentation does not check if the event presses went through by checking the
// boolean return value of injectInputEvent, which is why we created this class to better
// handle lost/dropped press events. Instrumentation cannot be used as a fallback strategy,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
* Responsible for selecting the proper strategy for injecting MotionEvents to the application under
* test.
*/
final class EventInjector {
public final class EventInjector {
private static final String TAG = EventInjector.class.getSimpleName();
private final EventInjectionStrategy injectionStrategy;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
* An {@link EventInjectionStrategy} that uses the input manager to inject Events. This strategy
* supports API level 16 and above.
*/
final class InputManagerEventInjectionStrategy implements EventInjectionStrategy {
public final class InputManagerEventInjectionStrategy implements EventInjectionStrategy {
private static final String TAG = "EventInjectionStrategy";
// The delay time to allow the soft keyboard to dismiss.
private static final long KEYBOARD_DISMISSAL_DELAY_MILLIS = 1000L;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
import java.lang.reflect.Method;

/** Isolates the nasty details of touching the message queue. */
final class Interrogator {
public final class Interrogator {

private static final String TAG = "Interrogator";
private static final Method messageQueueNextMethod;
Expand Down Expand Up @@ -106,7 +106,7 @@ interface QueueInterrogationHandler<R> {
* Informed of the state of the looper/queue and controls whether to continue interrogation or
* quit.
*/
interface InterrogationHandler<R> extends QueueInterrogationHandler<R> {
public interface InterrogationHandler<R> extends QueueInterrogationHandler<R> {
/**
* Notifies that the queue is about to dispatch a task.
*
Expand All @@ -129,7 +129,7 @@ interface InterrogationHandler<R> extends QueueInterrogationHandler<R> {
*
* @param handler an interrogation handler that controls whether to continue looping or not.
*/
static <R> R loopAndInterrogate(InterrogationHandler<R> handler) {
public static <R> R loopAndInterrogate(InterrogationHandler<R> handler) {
checkSanity();
interrogating.set(Boolean.TRUE);
boolean stillInterested = true;
Expand Down
25 changes: 25 additions & 0 deletions eventto/java/androidx/test/eventto/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2018 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="androidx.test.eventto" >


<uses-sdk
android:minSdkVersion="21"
android:targetSdkVersion="34" />

</manifest>
26 changes: 25 additions & 1 deletion eventto/java/androidx/test/eventto/BUILD
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
load("@build_bazel_rules_android//android:rules.bzl", "android_library")
load("//build_extensions/maven:axt_android_aar.bzl", "axt_android_aar")
load("//build_extensions/maven:maven_artifact.bzl", "maven_artifact")

package(
default_applicable_licenses = ["//espresso:license"],
default_visibility = ["//visibility:public"],
)

android_library(
name = "eventto",
testonly = 1,
srcs = glob(["*.java"]),
manifest = "AndroidManifest.xml",
visibility = [
"//eventto:__pkg__",
],
Expand All @@ -13,15 +21,31 @@ android_library(
"//espresso/core/java/androidx/test/espresso:framework",
"//espresso/core/java/androidx/test/espresso:interface",
"//espresso/core/java/androidx/test/espresso/base",
"//espresso/core/java/androidx/test/espresso/base:idling_resource_registry",
"//espresso/core/java/androidx/test/espresso/matcher",
"//espresso/core/java/androidx/test/espresso/util",
"//opensource/androidx:annotation",
"//runner/monitor",
"@maven//:com_google_code_findbugs_jsr305",
"@maven//:com_google_guava_guava",
"@maven//:javax_inject_javax_inject",
"@maven//:junit_junit",
"@maven//:org_hamcrest_hamcrest_core",
"@maven_listenablefuture//:com_google_guava_listenablefuture",
],
)

axt_android_aar(
name = "eventto_aar",
testonly = 1,
expected_class_prefixes = [
"androidx.test.eventto",
],
included_dep = ":eventto",
)

maven_artifact(
name = "eventto_maven_artifact",
testonly = 1,
last_updated = "20240709000000",
target = ":eventto_aar",
)
61 changes: 61 additions & 0 deletions eventto/java/androidx/test/eventto/EventtoUiControlller.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package androidx.test.eventto;

import androidx.test.espresso.UiController;
import androidx.test.espresso.InjectEventSecurityException;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.os.Handler;
import static android.os.Looper.getMainLooper;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import android.util.Log;
import java.util.concurrent.atomic.AtomicBoolean;
import androidx.test.espresso.base.Interrogator;
import androidx.test.espresso.base.EventInjector;

class EventtoUiController {
// implements UiController {
// private final Handler mainHandler;
// private final QueueIdler queueIdler;
// private final ExecutorService keyEventExecutor =
// Executors.newSingleThreadExecutor(
// new ThreadFactoryBuilder().setNameFormat("Eventto Key Event #%d").build());
// private final EventInjector eventInjector;
//
// EventtoUiController() {
// this.mainHandler = new Handler(getMainLooper());
// this.queueIdler = new QueueIdler();
// this.eventInjector = new EventInjector(new InputManagerEventInjectionStrategy());
// }
//
// @Override
// public boolean injectMotionEvent(MotionEvent event) throws InjectEventSecurityException {
// Log.i("EventtoUiController", "injectMotionEvent using ui automation");
//
// getInstrumentation().getUiAutomation().injectInputEvent(event, false);
// return true;
// }
//
// @Override
// public boolean injectKeyEvent(KeyEvent event) {
// throw new UnsupportedOperationException();
// }
//
// @Override
// public boolean injectString(String str) throws InjectEventSecurityException {
// throw new UnsupportedOperationException();
// }
//
// @Override
// public void loopMainThreadUntilIdle() {
// queueIdler.idle();
// }
//
// @Override
// public void loopMainThreadForAtLeast(long millisDelay) {
// AtomicBoolean elapsedTimeReached = new AtomicBoolean(false);
// mainHandler.postDelayed(() -> elapsedTimeReached.set(true), millisDelay);
// queueIdler.idleUntil(() -> elapsedTimeReached.get()) ;
// loopMainThreadUntilIdle();
// }
}
102 changes: 102 additions & 0 deletions eventto/java/androidx/test/eventto/EventtoViewFinderImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package androidx.test.eventto;

import static androidx.test.espresso.util.TreeIterables.breadthFirstViewTraversal;
import static androidx.test.internal.util.Checks.checkMainThread;
import static androidx.test.internal.util.Checks.checkNotNull;

import android.view.View;
import android.widget.AdapterView;
import androidx.test.espresso.AmbiguousViewMatcherException;
import androidx.test.espresso.NoMatchingViewException;
import androidx.test.espresso.ViewFinder;
import androidx.test.espresso.matcher.ViewMatchers;
import androidx.test.espresso.util.IterablesKt;
import androidx.test.espresso.util.Iterators;
import androidx.test.espresso.util.StringJoinerKt;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import javax.inject.Inject;
import javax.inject.Provider;
import org.hamcrest.Matcher;

/** Implementation of {@link ViewFinder}. */
// TODO: in the future we may want to collect stats here about the size of the view
// hierarchy, average matcher execution time, warn when matchers take too long to execute, etc.
public final class EventtoViewFinderImpl {

private final Matcher<View> viewMatcher;

@Inject
public EventtoViewFinderImpl(Matcher<View> viewMatcher) {
this.viewMatcher = viewMatcher;
}

public View getView(View root) throws AmbiguousViewMatcherException, NoMatchingViewException {
checkMainThread();
checkNotNull(viewMatcher);

Iterator<View> matchedViewIterator =
IterablesKt.filter(breadthFirstViewTraversal(root), viewMatcher).iterator();
View matchedView = null;

while (matchedViewIterator.hasNext()) {
if (matchedView != null) {
// Ambiguous!
throw new AmbiguousViewMatcherException.Builder()
.withViewMatcher(viewMatcher)
.withRootView(root)
.withView1(matchedView)
.withView2(matchedViewIterator.next())
.withOtherAmbiguousViews(Iterators.toArray(matchedViewIterator, View.class))
.build();
} else {
matchedView = matchedViewIterator.next();
}
}
if (null == matchedView) {
List<View> adapterViews =
IterablesKt.filterToList(
breadthFirstViewTraversal(root), ViewMatchers.isAssignableFrom(AdapterView.class));

if (adapterViews.isEmpty()) {
throw new NoMatchingViewException.Builder()
.withViewMatcher(viewMatcher)
.withRootView(root)
.build();
}

String warning =
String.format(
Locale.ROOT,
"\n"
+ "If the target view is not part of the view hierarchy, you may need to use"
+ " Espresso.onData to load it from one of the following AdapterViews:%s",
StringJoinerKt.joinToString(adapterViews, "\n- "));
throw new NoMatchingViewException.Builder()
.withViewMatcher(viewMatcher)
.withRootView(root)
.withAdapterViews(adapterViews)
.withAdapterViewWarning(warning)
.build();
} else {
return matchedView;
}
}
}
Loading

0 comments on commit 17233ad

Please sign in to comment.