and assertions. • espresso-contrib Support for DatePicker, RecyclerView, Drawer and more. • espresso-idling-resource Mechanism that allows additional test- app synchronisation.
and assertions. • espresso-intents Adds possibility to to validate and stub Intents. • espresso-contrib Support for DatePicker, RecyclerView, Drawer and more. • espresso-idling-resource Mechanism that allows additional test- app synchronisation.
and assertions. • espresso-intents Adds possibility to to validate and stub Intents. • espresso-contrib Support for DatePicker, RecyclerView, Drawer and more. • espresso-web WebView support. • espresso-idling-resource Mechanism that allows additional test- app synchronisation.
Matcher<? extends View> constraints = checkNotNull(viewAction.getConstraints()); runSynchronouslyOnUiThread(new Runnable() { @Override public void run() { uiController.loopMainThreadUntilIdle(); View targetView = viewFinder.getView(); Log.i(TAG, String.format( "Performing '%s' action on view %s", viewAction.getDescription(), viewMatcher)); if (!constraints.matches(targetView)) { // TODO(user): update this to describeMismatch once hamcrest is updated to new StringDescription stringDescription = new StringDescription(new StringBuilder( "Action will not be performed because the target view " + "does not match one or more of the following constraints:\n")); constraints.describeTo(stringDescription); stringDescription.appendText("\nTarget view: ") .appendValue(HumanReadables.describe(targetView)); if (viewAction instanceof ScrollToAction && isDescendantOfA(isAssignableFrom((AdapterView.class))).matches(targetView)) { stringDescription.appendText( "\nFurther Info: ScrollToAction on a view inside an AdapterView will not work. " + "Use Espresso.onData to load the view."); } throw new PerformException.Builder() .withActionDescription(viewAction.getDescription()) .withViewDescription(viewMatcher.toString()) .withCause(new RuntimeException(stringDescription.toString())) .build(); } else { viewAction.perform(uiController, targetView); } } }); }
task to tested application main thread executor. 2. Has reference to sent task and waits for result. 3. Sent task monitors tested application and waits until it becomes “idle”. After that it executes code sent from test package.
check()) IdlingConditions handled: • ASYNC_TASK_HAVE_IDLED // waits for asyncTask • COMPAT_TASKS_HAVE_IDLED // waits for asyncTask from AppCompat library • DYNAMIC_TASKS_HAVE_IDLED // waits for IdlingResources - user created waits Method which perform waits: uiController.loopUntil(EnumSet<IdleCondition> conditions);
|| queueState == QueueState.TASK_DUE_LONG) { return; } Inside loopUntil(); - waits for sent IdlingConditions - has access to applications main looper - can see task queue - 15 milliseconds ahead (QueueInterrogator.java) - allows task sent by Espresso to run • queue state is empty • pending tasks will start in very long time in the future void loopMainThreadUntilIdle(); // Called always (during perform() or check()) IdlingConditions handled: • ASYNC_TASK_HAVE_IDLED // waits for asyncTask • COMPAT_TASKS_HAVE_IDLED // waits for asyncTask from AppCompat library • DYNAMIC_TASKS_HAVE_IDLED // waits for IdlingResources - user created waits Method which perform waits: uiController.loopUntil(EnumSet<IdleCondition> conditions);
- there is no AsyncCompatTask running - all IdlingResources sent by test author has idled - main looper’s queue is empty - main looper’s queue has pending tasks scheduled for distant future
ViewAction (which performs MotionEvent) to perform() method: * uiController.loopMainThreadUntilIdle(); called - wait for AsyncTask - wait for AsyncCompatTask - wait for registered IdlingResources - wait for main looper queue (empty or no pending tasks in near future) 2. Schedules task with MotionEvent code to executor 3. MotionEvent is currently in progress, and in background: * uiController.loopUntil(IdleCondition condition); called - wait until MotionEvent is finished ( enum: MOTION_INJECTION_HAS_COMPLETED ) - wait for main looper queue (empty or no pending tasks in near future) 4. Calls .get() result on scheduled task only after it finished performing to avoid blocking main thread. Additionally after gets result: * uiController.loopMainThreadUntilIdle(); called - wait for AsyncTask - wait for AsyncCompatTask - wait for registered IdlingResources - wait for main looper queue (empty or no pending tasks in near future)
ViewAction (which performs text injection) to perform() method: * uiController.loopMainThreadUntilIdle(); called - wait for AsyncTask - wait for AsyncCompatTask - wait for registered IdlingResources - wait for main looper queue (empty or no pending tasks in near future) 2. Maps sent String into array of KeyEvents (one KeyEvent per char) 3. Invokes in for loop injectKeyEvent(KeyEvent event) method for each KeyEvent
meant to be used in loop, so it needs to keep checking if something haven’t changed since finish of last iteration: * uiController.loopMainThreadUntilIdle(); called - wait for AsyncTask - wait for AsyncCompatTask - wait for registered IdlingResources - wait for main looper queue (empty or no pending tasks in near future) 2. Schedules task with KeyEvent code to executor 3. KeyEvent is currently in progress, and in background: * uiController.loopUntil(IdleCondition condition); called - wait until MotionEvent is finished ( enum: KEY_INJECT_HAS_COMPLETED ) - wait for main looper queue (empty or no pending tasks in near future) 4. Calls .get() result on scheduled task only after it finished performing to avoid blocking main thread.
main thread for long millisDelay milliseconds which prolongs until test thread receives back the control 2. Calls: * uiController.loopMainThreadUntilIdle(); called - wait for AsyncTask - wait for AsyncCompatTask - wait for registered IdlingResources - wait for main looper queue (empty or no pending tasks in near future)
IdlingPolicy masterIdlingPolicy = new IdlingPolicy.Builder() .withIdlingTimeout(60) .withIdlingTimeoutUnit(TimeUnit.SECONDS) .throwAppNotIdleException() .build(); /** * Updates the IdlingPolicy used in UiController.loopUntil to detect AppNotIdleExceptions. * * @param timeout the timeout before an AppNotIdleException is created. * @param unit the unit of the timeout value. */ public static void setMasterPolicyTimeout(long timeout, TimeUnit unit) { checkArgument(timeout > 0); checkNotNull(unit); masterIdlingPolicy = masterIdlingPolicy.toBuilder() .withIdlingTimeout(timeout) .withIdlingTimeoutUnit(unit) .build(); }
names. for (IdlingResource resource : resourceList) { checkNotNull(resource.getName(), "IdlingResource.getName() should not be null"); boolean duplicate = false; for (IdlingResource oldResource : resources) { if (resource.getName().equals(oldResource.getName())) { // This does not throw an error to avoid leaving tests that register resource in test // setup in an undeterministic state (we cannot assume that everyone clears vm state // between each test run) Log.e(TAG, String.format("Attempted to register resource with same names:" + " %s. R1: %s R2: %s.\nDuplicate resource registration will be ignored.", resource.getName(), resource, oldResource)); duplicate = true; break; } } if (!duplicate) { resources.add(resource); final int position = resources.size() - 1; registerToIdleCallback(resource, position); idleState.set(position, resource.isIdleNow()); } else { allRegisteredSuccesfully = false; }
new IdlingPolicy.Builder() .withIdlingTimeout(26) .withIdlingTimeoutUnit(TimeUnit.SECONDS) .throwIdlingResourceTimeoutException() .build(); /** * Updates the IdlingPolicy used by IdlingResourceRegistry to determine when IdlingResources * timeout. * * @param timeout the timeout before an IdlingResourceTimeoutException is created. * @param unit the unit of the timeout value. */ public static void setIdlingResourceTimeout(long timeout, TimeUnit unit) { checkArgument(timeout > 0); checkNotNull(unit); dynamicIdlingResourceErrorPolicy = dynamicIdlingResourceErrorPolicy.toBuilder() .withIdlingTimeout(timeout) .withIdlingTimeoutUnit(unit) .build(); }