-
Notifications
You must be signed in to change notification settings - Fork 314
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
17 changed files
with
550 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
alias( | ||
name = "eventto", | ||
testonly = 1, | ||
actual = "//eventto/java/androidx/test/eventto", | ||
visibility = ["//visibility:public"], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
load("@build_bazel_rules_android//android:rules.bzl", "android_library") | ||
|
||
android_library( | ||
name = "eventto", | ||
testonly = 1, | ||
srcs = glob(["*.java"]), | ||
visibility = [ | ||
"//eventto:__pkg__", | ||
], | ||
deps = [ | ||
"//core", | ||
"//espresso/core/java/androidx/test/espresso:espresso_compiletime", | ||
"//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/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", | ||
], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package androidx.test.eventto; | ||
|
||
import android.view.View; | ||
import androidx.annotation.CheckResult; | ||
import java.time.Duration; | ||
import javax.annotation.CheckReturnValue; | ||
import org.hamcrest.Matcher; | ||
|
||
public class Eventto { | ||
private Eventto() {} | ||
|
||
@CheckReturnValue | ||
@CheckResult | ||
public static EventtoViewInteraction onView(Matcher<View> matcher) { | ||
return new EventtoViewInteraction(matcher); | ||
} | ||
|
||
public static void setDefaultTimeout(Duration duration) { | ||
EventtoViewInteraction.setDefaultTimeout(duration); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package androidx.test.eventto; | ||
|
||
import android.view.View; | ||
import org.hamcrest.Matcher; | ||
|
||
public interface EventtoViewAction { | ||
|
||
/** | ||
* A mechanism for ViewActions to specify what type of views they can operate on. | ||
* | ||
* <p>A ViewAction can demand that the view passed to perform meets certain constraints. For | ||
* example it may want to ensure the view is already in the viewable physical screen of the device | ||
* or is of a certain type. | ||
* | ||
* @return a <a href="http://hamcrest.org/JavaHamcrest/javadoc/1.3/org/hamcrest/Matcher.html"> | ||
* <code>Matcher</code></a> that will be tested prior to calling perform. | ||
*/ | ||
public Matcher<View> getConstraints(); | ||
|
||
/** | ||
* Returns a description of the view action. The description should not be overly long and should | ||
* fit nicely in a sentence like: "performing %description% action on view with id ..." | ||
*/ | ||
public String getDescription(); | ||
|
||
/** | ||
* Performs this action on the given view. | ||
* | ||
* @param view the view to act upon. never null. | ||
*/ | ||
public void perform(View view); | ||
} |
164 changes: 164 additions & 0 deletions
164
eventto/java/androidx/test/eventto/EventtoViewInteraction.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
package androidx.test.eventto; | ||
|
||
|
||
import android.os.Handler; | ||
import android.os.Looper; | ||
import android.os.SystemClock; | ||
import android.util.Log; | ||
import android.view.View; | ||
import androidx.test.espresso.NoMatchingViewException; | ||
import androidx.test.espresso.UiController; | ||
import androidx.test.espresso.ViewAction; | ||
import androidx.test.espresso.ViewAssertion; | ||
import androidx.test.espresso.ViewFinder; | ||
import androidx.test.espresso.base.ViewFinderImpl; | ||
import androidx.test.internal.util.Checks; | ||
import androidx.test.platform.view.inspector.WindowInspectorCompat; | ||
import androidx.test.platform.view.inspector.WindowInspectorCompat.ViewRetrievalException; | ||
import java.time.Duration; | ||
import java.util.List; | ||
import java.util.concurrent.Callable; | ||
import java.util.concurrent.ExecutionException; | ||
import java.util.concurrent.FutureTask; | ||
import java.util.concurrent.TimeUnit; | ||
import java.util.concurrent.TimeoutException; | ||
import javax.inject.Provider; | ||
import org.hamcrest.Matcher; | ||
|
||
public class EventtoViewInteraction { | ||
// private final Matcher<View> viewMatcher; | ||
private final ViewFinder viewFinder; | ||
private long timeoutMillis; | ||
private final Handler mainHandler = new Handler(Looper.getMainLooper()); | ||
|
||
private static long defaultTimeoutMillis = Duration.ofSeconds(5).toMillis(); | ||
|
||
EventtoViewInteraction(Matcher<View> viewMatcher) { | ||
// this.viewMatcher = viewMatcher; | ||
this.viewFinder = new ViewFinderImpl(viewMatcher, new SimpleRootViewPicker()); | ||
this.timeoutMillis = defaultTimeoutMillis; | ||
} | ||
|
||
static void setDefaultTimeout(Duration duration) { | ||
defaultTimeoutMillis = duration.toMillis(); | ||
} | ||
|
||
public EventtoViewInteraction withTimeout(Duration timeout) { | ||
timeoutMillis = timeout.toMillis(); | ||
return this; | ||
} | ||
|
||
public void check(ViewAssertion assertion) { | ||
ViewAssertionCallable viewAssertionCallable = new ViewAssertionCallable(viewFinder, assertion); | ||
performRetryingTaskOnUiThread(viewAssertionCallable); | ||
} | ||
|
||
private void performRetryingTaskOnUiThread(Callable<ExecutionStatus> viewInteractionCallable) { | ||
long startTime = SystemClock.uptimeMillis(); | ||
long remainingTimeout = timeoutMillis; | ||
long pollDelay = 0; | ||
while (remainingTimeout > 0) { | ||
|
||
FutureTask<ExecutionStatus> uiTask = new FutureTask<>(viewInteractionCallable); | ||
mainHandler.postDelayed(uiTask, pollDelay); | ||
try { | ||
if (uiTask.get(remainingTimeout, TimeUnit.MILLISECONDS) == ExecutionStatus.SUCCESS) { | ||
return; | ||
} | ||
remainingTimeout = SystemClock.uptimeMillis() - startTime - timeoutMillis; | ||
pollDelay = 100; | ||
} catch (InterruptedException ie) { | ||
throw new RuntimeException("Interrupted running UI task", ie); | ||
} catch (ExecutionException ee) { | ||
Throwable cause = ee.getCause(); | ||
if (cause instanceof RuntimeException) { | ||
throw (RuntimeException) cause; | ||
} else if (cause instanceof Error) { | ||
throw (Error) cause; | ||
} | ||
throw new RuntimeException(cause); | ||
} catch (TimeoutException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
} | ||
|
||
public void perform(ViewAction action) { | ||
ViewActionCallable callable = | ||
new ViewActionCallable(viewFinder, action, new EventtoUiController()); | ||
performRetryingTaskOnUiThread(callable); | ||
} | ||
|
||
private static class SimpleRootViewPicker implements Provider<View> { | ||
|
||
@Override | ||
public View get() { | ||
|
||
try { | ||
List<View> views = WindowInspectorCompat.getGlobalWindowViews(); | ||
// TODO: handle more than 1 view | ||
Checks.checkArgument(views.size() <= 1); | ||
if (views.size() == 1) { | ||
return views.get(0); | ||
} | ||
} catch (ViewRetrievalException e) { | ||
// | ||
} | ||
return null; | ||
} | ||
} | ||
|
||
private enum ExecutionStatus { | ||
SUCCESS, | ||
RESCHEDULE | ||
} | ||
|
||
private static class ViewAssertionCallable implements Callable<ExecutionStatus> { | ||
private final ViewFinder viewFinder; | ||
private final ViewAssertion viewAssertion; | ||
|
||
ViewAssertionCallable(ViewFinder viewFinder, ViewAssertion viewAssertion) { | ||
this.viewFinder = viewFinder; | ||
this.viewAssertion = viewAssertion; | ||
} | ||
|
||
@Override | ||
public ExecutionStatus call() throws Exception { | ||
Log.i("Eventto", "Running ViewAssertionCallable"); | ||
try { | ||
View matchedView = viewFinder.getView(); | ||
viewAssertion.check(matchedView, null); | ||
return ExecutionStatus.SUCCESS; | ||
} catch (NoMatchingViewException e) { | ||
Log.i("Eventto", "Could not find view, retrying", e); | ||
return ExecutionStatus.RESCHEDULE; | ||
} | ||
} | ||
} | ||
|
||
private static class ViewActionCallable implements Callable<ExecutionStatus> { | ||
private final ViewFinder viewFinder; | ||
private final ViewAction viewAction; | ||
private final UiController uiController; | ||
|
||
ViewActionCallable( | ||
ViewFinder viewFinder, EventoViewAction viewAction, UiController uiController) { | ||
this.viewFinder = viewFinder; | ||
this.viewAction = viewAction; | ||
this.uiController = uiController; | ||
} | ||
|
||
@Override | ||
public ExecutionStatus call() throws Exception { | ||
Log.i("Eventto", "Running ViewAssertionCallable"); | ||
try { | ||
View matchedView = viewFinder.getView(); | ||
viewAction.perform(uiController, matchedView); | ||
return ExecutionStatus.SUCCESS; | ||
} catch (NoMatchingViewException e) { | ||
Log.i("Eventto", "Could not find view, retrying", e); | ||
return ExecutionStatus.RESCHEDULE; | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
load("@build_bazel_rules_android//android:rules.bzl", "android_local_test") | ||
load("//build_extensions:android_library_test.bzl", "axt_android_library_test") | ||
load("//build_extensions:phone_devices.bzl", "devices") | ||
|
||
java_library( | ||
name = "robolectric_config", | ||
resources = ["robolectric.properties"], | ||
) | ||
|
||
android_local_test( | ||
name = "EventtoTest", | ||
srcs = ["EventtoTest.java"], | ||
jvm_flags = ["-Drobolectric.looperMode=INSTRUMENTATION_TEST"], | ||
deps = [ | ||
":robolectric_config", | ||
"//espresso/core/java/androidx/test/espresso", | ||
"//eventto", | ||
"//eventto/javatests/androidx/test/eventto/fixtures", | ||
"//ext/junit", | ||
"@maven//:junit_junit", | ||
], | ||
) | ||
|
||
axt_android_library_test( | ||
name = "EventtoTest_android", | ||
srcs = ["EventtoTest.java"], | ||
device_list = devices([34]), | ||
deps = [ | ||
"//espresso/core/java/androidx/test/espresso", | ||
"//eventto", | ||
"//eventto/javatests/androidx/test/eventto/fixtures", | ||
"//ext/junit", | ||
"@maven//:junit_junit", | ||
], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package androidx.test.eventto; | ||
|
||
import static androidx.test.espresso.action.ViewActions.click; | ||
import static androidx.test.espresso.matcher.ViewMatchers.withText; | ||
|
||
import androidx.test.eventto.fixtures.SimpleActivity; | ||
import androidx.test.ext.junit.rules.ActivityScenarioRule; | ||
import androidx.test.ext.junit.runners.AndroidJUnit4; | ||
import org.junit.Rule; | ||
import org.junit.Test; | ||
import org.junit.runner.RunWith; | ||
|
||
@RunWith(AndroidJUnit4.class) | ||
public class EventtoTest { | ||
|
||
@Rule | ||
public ActivityScenarioRule<SimpleActivity> activityScenarioRule = | ||
new ActivityScenarioRule<>(SimpleActivity.class); | ||
|
||
// @Test | ||
// public void eventto_find() throws Throwable { | ||
// Eventto.onView(withText("Text View")).check(matches(isDisplayed())); | ||
// } | ||
|
||
@Test | ||
public void eventto_click() throws Throwable { | ||
Eventto.onView(withText("Launch delayed")).perform(click()); | ||
} | ||
|
||
// | ||
// @Test | ||
// public void espresso_click() throws Throwable { | ||
// onView(withText("Launch delayed")).perform(click()); | ||
// | ||
// // expect another activity launch | ||
// onView(withText("Delayed Activity")).check(matches(isDisplayed())); | ||
// } | ||
|
||
// @Test | ||
// public void eventto_findFails() throws Throwable { | ||
// onViewWithText("Text View not there").assertThat(isDisplayed()); | ||
// } | ||
|
||
// @Test | ||
// public void espressoFind() { | ||
// onView(withText("Text View")).check(matches(isDisplayed())); | ||
// } | ||
|
||
} |
26 changes: 26 additions & 0 deletions
26
eventto/javatests/androidx/test/eventto/fixtures/AndroidManifest.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<!-- | ||
Manifest for ATSL integration tests | ||
--> | ||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" > | ||
|
||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="34" /> | ||
|
||
<application> | ||
<activity android:name="androidx.test.eventto.fixtures.SimpleActivity" | ||
android:label="Activity Label" | ||
android:exported="true" > | ||
<intent-filter > | ||
<action android:name="android.intent.action.VIEW" /> | ||
<category android:name="android.intent.category.DEFAULT" /> | ||
</intent-filter> | ||
</activity> | ||
<activity android:name="androidx.test.eventto.fixtures.DelayedActivity" | ||
android:exported="true" > | ||
<intent-filter > | ||
<action android:name="android.intent.action.VIEW" /> | ||
<category android:name="android.intent.category.DEFAULT" /> | ||
</intent-filter> | ||
</activity> | ||
</application> | ||
</manifest> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
load("@build_bazel_rules_android//android:rules.bzl", "android_library") | ||
|
||
android_library( | ||
name = "fixtures", | ||
srcs = glob(["*.java"]), | ||
exports_manifest = 1, | ||
manifest = "AndroidManifest.xml", | ||
resource_files = glob(["res/**"]), | ||
visibility = [ | ||
"//eventto/javatests/androidx/test/eventto:__subpackages__", | ||
], | ||
deps = [ | ||
], | ||
) |
Oops, something went wrong.