Commit 22c2cd98 authored by Michael Thiessen's avatar Michael Thiessen Committed by Commit Bot

Move CriteriaHelper to base/

This will be a 3-sided multi-part CL.
In this change I move CriteriaHelper to base/, leaving a copy in
content/
I will then migrate the downstream and upstream content/ imports to
base/ imports.
After downstream has rolled I'll remove the content/ copies.

Bug: 1134178
Change-Id: I07c646a1cc9aebfcd2c2a7cd3e0446f0581f6e82
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2518120Reviewed-by: default avatarYaron Friedman <yfriedman@chromium.org>
Commit-Queue: Michael Thiessen <mthiesse@chromium.org>
Cr-Commit-Position: refs/heads/master@{#823669}
parent 7b3ca597
......@@ -3874,6 +3874,8 @@ if (is_android) {
"test/android/javatests/src/org/chromium/base/test/util/CallbackHelper.java",
"test/android/javatests/src/org/chromium/base/test/util/CloseableOnMainThread.java",
"test/android/javatests/src/org/chromium/base/test/util/CommandLineFlags.java",
"test/android/javatests/src/org/chromium/base/test/util/Criteria.java",
"test/android/javatests/src/org/chromium/base/test/util/CriteriaHelper.java",
"test/android/javatests/src/org/chromium/base/test/util/CriteriaNotSatisfiedException.java",
"test/android/javatests/src/org/chromium/base/test/util/DisableIf.java",
"test/android/javatests/src/org/chromium/base/test/util/DisableIfSkipCheck.java",
......@@ -3886,6 +3888,7 @@ if (is_android) {
"test/android/javatests/src/org/chromium/base/test/util/InstrumentationUtils.java",
"test/android/javatests/src/org/chromium/base/test/util/IntegrationTest.java",
"test/android/javatests/src/org/chromium/base/test/util/JniMocker.java",
"test/android/javatests/src/org/chromium/base/test/util/LooperUtils.java",
"test/android/javatests/src/org/chromium/base/test/util/Manual.java",
"test/android/javatests/src/org/chromium/base/test/util/Matchers.java",
"test/android/javatests/src/org/chromium/base/test/util/MetricsUtils.java",
......
// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.base.test.util;
import android.text.TextUtils;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.StringDescription;
/**
* Provides a means for validating whether some condition/criteria has been met.
* <p>
* See {@link CriteriaHelper} for usage guidelines.
*/
public final class Criteria {
private Criteria() {}
/**
* Validates that a expected condition has been met, and throws an
* {@link CriteriaNotSatisfiedException} if not.
*
* @param <T> The type of value whose being tested.
* @param actual The actual value being tested.
* @param matcher Determines if the current value matches the desired expectation.
*/
public static <T> void checkThat(T actual, Matcher<T> matcher) {
checkThat("", actual, matcher);
}
/**
* Validates that a expected condition has been met, and throws an
* {@link CriteriaNotSatisfiedException} if not.
*
* @param <T> The type of value whose being tested.
* @param reason Additional reason description for the failure.
* @param actual The actual value being tested.
* @param matcher Determines if the current value matches the desired expectation.
*/
public static <T> void checkThat(String reason, T actual, Matcher<T> matcher) {
if (matcher.matches(actual)) return;
Description description = new StringDescription();
if (!TextUtils.isEmpty(reason)) {
description.appendText(reason).appendText(System.lineSeparator());
}
description.appendText("Expected: ")
.appendDescriptionOf(matcher)
.appendText(System.lineSeparator())
.appendText(" but: ");
matcher.describeMismatch(actual, description);
throw new CriteriaNotSatisfiedException(description.toString());
}
}
// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.base.test.util;
import android.os.Handler;
import android.os.Looper;
import org.hamcrest.Matchers;
import org.chromium.base.ThreadUtils;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
/**
* Helper methods for creating and managing criteria.
*
* <p>
* If possible, use callbacks or testing delegates instead of criteria as they
* do not introduce any polling delays. Should only use criteria if no suitable
* other approach exists.
*
* <p>
* The Runnable variation of the CriteriaHelper methods allows a flexible way of verifying any
* number of conditions are met prior to proceeding.
*
* <pre>
* Example:
* <code>
* private void verifyMenuShown() {
* CriteriaHelper.pollUiThread(() -> {
* Criteria.checkThat("App menu was null", getActivity().getAppMenuHandler(),
* Matchers.notNullValue());
* Criteria.checkThat("App menu was not shown",
* getActivity().getAppMenuHandler().isAppMenuShowing(), Matchers.is(true));
* });
* }
* </code>
* </pre>
*
* <p>
* To verify simple conditions, the Callback variation can be less verbose.
*
* <pre>
* Example:
* <code>
* private void assertMenuShown() {
* CriteriaHelper.pollUiThread(() -> getActivity().getAppMenuHandler().isAppMenuShowing(),
* "App menu was not shown");
* }
* </code>
* </pre>
*/
public class CriteriaHelper {
/** The default maximum time to wait for a criteria to become valid. */
public static final long DEFAULT_MAX_TIME_TO_POLL = 3000L;
/** The default polling interval to wait between checking for a satisfied criteria. */
public static final long DEFAULT_POLLING_INTERVAL = 50;
private static final long DEFAULT_JUNIT_MAX_TIME_TO_POLL = 1000;
private static final long DEFAULT_JUNIT_POLLING_INTERVAL = 1;
/**
* Checks whether the given Runnable completes without exception at a given interval, until
* either the Runnable successfully completes, or the maxTimeoutMs number of ms has elapsed.
*
* <p>
* This evaluates the Criteria on the Instrumentation thread, which more often than not is not
* correct in an InstrumentationTest. Use
* {@link #pollUiThread(Runnable, long, long)} instead.
*
* @param criteria The Runnable that will be attempted.
* @param maxTimeoutMs The maximum number of ms that this check will be performed for
* before timeout.
* @param checkIntervalMs The number of ms between checks.
*/
public static void pollInstrumentationThread(
Runnable criteria, long maxTimeoutMs, long checkIntervalMs) {
assert !ThreadUtils.runningOnUiThread();
pollThreadInternal(criteria, maxTimeoutMs, checkIntervalMs, false);
}
private static void pollThreadInternal(
Runnable criteria, long maxTimeoutMs, long checkIntervalMs, boolean shouldNest) {
Throwable throwable;
try {
criteria.run();
return;
} catch (Throwable e) {
// Espresso catches, wraps, and re-throws the exception we want the CriteriaHelper
// to catch.
if (e instanceof CriteriaNotSatisfiedException
|| e.getCause() instanceof CriteriaNotSatisfiedException) {
throwable = e;
} else {
throw e;
}
}
TimeoutTimer timer = new TimeoutTimer(maxTimeoutMs);
while (!timer.isTimedOut()) {
if (shouldNest) {
nestThread(checkIntervalMs);
} else {
sleepThread(checkIntervalMs);
}
try {
criteria.run();
return;
} catch (Throwable e) {
if (e instanceof CriteriaNotSatisfiedException
|| e.getCause() instanceof CriteriaNotSatisfiedException) {
throwable = e;
} else {
throw e;
}
}
}
throw new AssertionError(throwable);
}
private static void sleepThread(long checkIntervalMs) {
try {
Thread.sleep(checkIntervalMs);
} catch (InterruptedException e) {
// Catch the InterruptedException. If the exception occurs before maxTimeoutMs
// and the criteria is not satisfied, the while loop will run again.
}
}
private static void nestThread(long checkIntervalMs) {
AtomicBoolean called = new AtomicBoolean(false);
// Ensure we pump the message handler in case no new tasks arrive.
new Handler(Looper.myLooper()).postDelayed(() -> { called.set(true); }, checkIntervalMs);
TimeoutTimer timer = new TimeoutTimer(checkIntervalMs);
// To allow a checkInterval of 0ms, ensure we at least run a single task, which allows a
// test to check conditions between each task run on the thread.
do {
try {
LooperUtils.runSingleNestedLooperTask();
} catch (IllegalArgumentException | IllegalAccessException | SecurityException
| InvocationTargetException e) {
throw new RuntimeException(e);
}
} while (!timer.isTimedOut() && !called.get());
}
/**
* Checks whether the given Runnable completes without exception at the default interval.
*
* <p>
* This evaluates the Runnable on the test thread, which more often than not is not correct
* in an InstrumentationTest. Use {@link #pollUiThread(Runnable)} instead.
*
* @param criteria The Runnable that will be attempted.
*
* @see #pollInstrumentationThread(Criteria, long, long)
*/
public static void pollInstrumentationThread(Runnable criteria) {
pollInstrumentationThread(criteria, DEFAULT_MAX_TIME_TO_POLL, DEFAULT_POLLING_INTERVAL);
}
/**
* Checks whether the given Callable<Boolean> is satisfied at a given interval, until either
* the criteria is satisfied, or the specified maxTimeoutMs number of ms has elapsed.
*
* <p>
* This evaluates the Callable<Boolean> on the test thread, which more often than not is not
* correct in an InstrumentationTest. Use {@link #pollUiThread(Callable)} instead.
*
* @param criteria The Callable<Boolean> that will be checked.
* @param failureReason The static failure reason
* @param maxTimeoutMs The maximum number of ms that this check will be performed for
* before timeout.
* @param checkIntervalMs The number of ms between checks.
*/
public static void pollInstrumentationThread(final Callable<Boolean> criteria,
String failureReason, long maxTimeoutMs, long checkIntervalMs) {
pollInstrumentationThread(
toNotSatisfiedRunnable(criteria, failureReason), maxTimeoutMs, checkIntervalMs);
}
/**
* Checks whether the given Callable<Boolean> is satisfied at a given interval, until either
* the criteria is satisfied, or the specified maxTimeoutMs number of ms has elapsed.
*
* <p>
* This evaluates the Callable<Boolean> on the test thread, which more often than not is not
* correct in an InstrumentationTest. Use {@link #pollUiThread(Callable)} instead.
*
* @param criteria The Callable<Boolean> that will be checked.
* @param maxTimeoutMs The maximum number of ms that this check will be performed for
* before timeout.
* @param checkIntervalMs The number of ms between checks.
*/
public static void pollInstrumentationThread(
final Callable<Boolean> criteria, long maxTimeoutMs, long checkIntervalMs) {
pollInstrumentationThread(criteria, null, maxTimeoutMs, checkIntervalMs);
}
/**
* Checks whether the given Callable<Boolean> is satisfied polling at a default interval.
*
* <p>
* This evaluates the Callable<Boolean> on the test thread, which more often than not is not
* correct in an InstrumentationTest. Use {@link #pollUiThread(Callable)} instead.
*
* @param criteria The Callable<Boolean> that will be checked.
* @param failureReason The static failure reason
*/
public static void pollInstrumentationThread(Callable<Boolean> criteria, String failureReason) {
pollInstrumentationThread(
criteria, failureReason, DEFAULT_MAX_TIME_TO_POLL, DEFAULT_POLLING_INTERVAL);
}
/**
* Checks whether the given Callable<Boolean> is satisfied polling at a default interval.
*
* <p>
* This evaluates the Callable<Boolean> on the test thread, which more often than not is not
* correct in an InstrumentationTest. Use {@link #pollUiThread(Callable)} instead.
*
* @param criteria The Callable<Boolean> that will be checked.
*/
public static void pollInstrumentationThread(Callable<Boolean> criteria) {
pollInstrumentationThread(criteria, null);
}
/**
* Checks whether the given Runnable completes without exception at a given interval on the UI
* thread, until either the Runnable successfully completes, or the maxTimeoutMs number of ms
* has elapsed.
*
* @param criteria The Runnable that will be attempted.
* @param maxTimeoutMs The maximum number of ms that this check will be performed for
* before timeout.
* @param checkIntervalMs The number of ms between checks.
*
* @see #pollInstrumentationThread(Runnable)
*/
public static void pollUiThread(
final Runnable criteria, long maxTimeoutMs, long checkIntervalMs) {
assert !ThreadUtils.runningOnUiThread();
pollInstrumentationThread(() -> {
AtomicReference<Throwable> throwableRef = new AtomicReference<>();
ThreadUtils.runOnUiThreadBlocking(() -> {
try {
criteria.run();
} catch (Throwable t) {
throwableRef.set(t);
}
});
Throwable throwable = throwableRef.get();
if (throwable != null) {
if (throwable instanceof CriteriaNotSatisfiedException) {
throw new CriteriaNotSatisfiedException(throwable);
} else if (throwable instanceof RuntimeException) {
throw(RuntimeException) throwable;
} else {
throw new RuntimeException(throwable);
}
}
}, maxTimeoutMs, checkIntervalMs);
}
/**
* Checks whether the given Runnable completes without exception at the default interval on
* the UI thread.
* @param criteria The Runnable that will be attempted.
*
* @see #pollInstrumentationThread(Runnable)
*/
public static void pollUiThread(final Runnable criteria) {
pollUiThread(criteria, DEFAULT_MAX_TIME_TO_POLL, DEFAULT_POLLING_INTERVAL);
}
/**
* Checks whether the given Callable<Boolean> is satisfied polling at a given interval on the UI
* thread, until either the criteria is satisfied, or the maxTimeoutMs number of ms has elapsed.
*
* @param criteria The Callable<Boolean> that will be checked.
* @param failureReason The static failure reason
* @param maxTimeoutMs The maximum number of ms that this check will be performed for
* before timeout.
* @param checkIntervalMs The number of ms between checks.
*
* @see #pollInstrumentationThread(Criteria)
*/
public static void pollUiThread(final Callable<Boolean> criteria, String failureReason,
long maxTimeoutMs, long checkIntervalMs) {
pollUiThread(
toNotSatisfiedRunnable(criteria, failureReason), maxTimeoutMs, checkIntervalMs);
}
/**
* Checks whether the given Callable<Boolean> is satisfied polling at a given interval on the UI
* thread, until either the criteria is satisfied, or the maxTimeoutMs number of ms has elapsed.
*
* @param criteria The Callable<Boolean> that will be checked.
* @param maxTimeoutMs The maximum number of ms that this check will be performed for
* before timeout.
* @param checkIntervalMs The number of ms between checks.
*
* @see #pollInstrumentationThread(Criteria)
*/
public static void pollUiThread(
final Callable<Boolean> criteria, long maxTimeoutMs, long checkIntervalMs) {
pollUiThread(criteria, null, maxTimeoutMs, checkIntervalMs);
}
/**
* Checks whether the given Callable<Boolean> is satisfied polling at a default interval on the
* UI thread. A static failure reason is given.
* @param criteria The Callable<Boolean> that will be checked.
* @param failureReason The static failure reason
*
* @see #pollInstrumentationThread(Criteria)
*/
public static void pollUiThread(final Callable<Boolean> criteria, String failureReason) {
pollUiThread(criteria, failureReason, DEFAULT_MAX_TIME_TO_POLL, DEFAULT_POLLING_INTERVAL);
}
/**
* Checks whether the given Callable<Boolean> is satisfied polling at a default interval on the
* UI thread.
* @param criteria The Callable<Boolean> that will be checked.
*
* @see #pollInstrumentationThread(Criteria)
*/
public static void pollUiThread(final Callable<Boolean> criteria) {
pollUiThread(criteria, null);
}
/**
* Checks whether the given Runnable completes without exception at a given interval on the UI
* thread, until either the Runnable successfully completes, or the maxTimeoutMs number of ms
* has elapsed.
* This call will nest the Looper in order to wait for the Runnable to complete.
*
* @param criteria The Runnable that will be attempted.
* @param maxTimeoutMs The maximum number of ms that this check will be performed for
* before timeout.
* @param checkIntervalMs The number of ms between checks.
*
* @see #pollInstrumentationThread(Runnable)
*/
public static void pollUiThreadNested(
Runnable criteria, long maxTimeoutMs, long checkIntervalMs) {
assert ThreadUtils.runningOnUiThread();
pollThreadInternal(criteria, maxTimeoutMs, checkIntervalMs, true);
}
/**
* Checks whether the given Runnable is satisfied polling at a given interval on the UI
* thread, until either the criteria is satisfied, or the maxTimeoutMs number of ms has elapsed.
* This call will nest the Looper in order to wait for the Criteria to be satisfied.
*
* @param criteria The Callable<Boolean> that will be checked.
* @param maxTimeoutMs The maximum number of ms that this check will be performed for
* before timeout.
* @param checkIntervalMs The number of ms between checks.
*
* @see #pollInstrumentationThread(Criteria)
*/
public static void pollUiThreadNested(
final Callable<Boolean> criteria, long maxTimeoutMs, long checkIntervalMs) {
pollUiThreadNested(toNotSatisfiedRunnable(criteria, null), maxTimeoutMs, checkIntervalMs);
}
/**
* Checks whether the given Runnable completes without exception at the default interval on
* the UI thread. This call will nest the Looper in order to wait for the Runnable to complete.
* @param criteria The Runnable that will be attempted.
*
* @see #pollInstrumentationThread(Runnable)
*/
public static void pollUiThreadNested(final Runnable criteria) {
pollUiThreadNested(criteria, DEFAULT_MAX_TIME_TO_POLL, DEFAULT_POLLING_INTERVAL);
}
/**
* Checks whether the given Callable<Boolean> is satisfied polling at a default interval on the
* UI thread. This call will nest the Looper in order to wait for the Criteria to be satisfied.
* @param criteria The Callable<Boolean> that will be checked.
*
* @see #pollInstrumentationThread(Criteria)
*/
public static void pollUiThreadNested(final Callable<Boolean> criteria) {
pollUiThreadNested(toNotSatisfiedRunnable(criteria, null));
}
/**
* Sleeps the JUnit UI thread to wait on the condition. The condition must be met by a
* background thread that does not block on the UI thread.
*
* @param criteria The Callable<Boolean> that will be checked.
*
* @see #pollInstrumentationThread(Criteria)
*/
public static void pollUiThreadForJUnit(final Callable<Boolean> criteria) {
pollUiThreadForJUnit(toNotSatisfiedRunnable(criteria, null));
}
/**
* Sleeps the JUnit UI thread to wait on the criteria. The criteria must be met by a
* background thread that does not block on the UI thread.
*
* @param criteria The Runnable that will be attempted.
*
* @see #pollInstrumentationThread(Criteria)
*/
public static void pollUiThreadForJUnit(final Runnable criteria) {
assert ThreadUtils.runningOnUiThread();
pollThreadInternal(
criteria, DEFAULT_JUNIT_MAX_TIME_TO_POLL, DEFAULT_JUNIT_POLLING_INTERVAL, false);
}
private static Runnable toNotSatisfiedRunnable(
Callable<Boolean> criteria, String failureReason) {
return () -> {
boolean isSatisfied;
try {
isSatisfied = criteria.call();
} catch (RuntimeException re) {
throw re;
} catch (Exception e) {
throw new RuntimeException(e);
}
Criteria.checkThat(failureReason, isSatisfied, Matchers.is(true));
};
}
}
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.base.test.util;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* Test utilities for interacting with the Android Looper.
*/
public class LooperUtils {
private static final Method sNextMethod = getMethod(MessageQueue.class, "next");
private static final Field sMessageTargetField = getField(Message.class, "target");
private static final Field sMessageFlagsField = getField(Message.class, "flags");
private static Field getField(Class<?> clazz, String name) {
Field f = null;
try {
f = clazz.getDeclaredField(name);
f.setAccessible(true);
} catch (Exception e) {
e.printStackTrace();
}
return f;
}
private static Method getMethod(Class<?> clazz, String name) {
Method m = null;
try {
m = clazz.getDeclaredMethod(name);
m.setAccessible(true);
} catch (Exception e) {
e.printStackTrace();
}
return m;
}
/**
* Runs a single nested task on the current Looper.
*/
public static void runSingleNestedLooperTask() throws IllegalArgumentException,
IllegalAccessException, SecurityException,
InvocationTargetException {
MessageQueue queue = Looper.myQueue();
// This call will block if there are no messages in the queue. It will
// also run or more pending C++ tasks as a side effect before returning
// |msg|.
Message msg = (Message) sNextMethod.invoke(queue);
if (msg == null) return;
Handler target = (Handler) sMessageTargetField.get(msg);
if (target != null) target.dispatchMessage(msg);
// Unset in-use flag.
Integer oldFlags = (Integer) sMessageFlagsField.get(msg);
sMessageFlagsField.set(msg, oldFlags & ~(1 << 0 /* FLAG_IN_USE */));
msg.recycle();
}
}
......@@ -8,7 +8,10 @@ generate_jni("test_support_content_jni_headers") {
android_library("android_test_message_pump_support_java") {
testonly = true
deps = [ "//base:base_java" ]
deps = [
"//base:base_java",
"//base:base_java_test_support",
]
sources = [ "javatests/src/org/chromium/content_public/browser/test/NestedSystemMessageHandler.java" ]
}
......
......@@ -5,16 +5,12 @@
package org.chromium.content_public.browser.test;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.test.util.LooperUtils;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* Handles processing messages in nested run loops.
......@@ -27,58 +23,9 @@ import java.lang.reflect.Method;
public class NestedSystemMessageHandler {
private static final int QUIT_MESSAGE = 10;
private static final Handler sHandler = new Handler();
private static final Method sNextMethod = getMethod(MessageQueue.class, "next");
private static final Field sMessageTargetField = getField(Message.class, "target");
private static final Field sMessageFlagsField = getField(Message.class, "flags");
private static Field getField(Class<?> clazz, String name) {
Field f = null;
try {
f = clazz.getDeclaredField(name);
f.setAccessible(true);
} catch (Exception e) {
e.printStackTrace();
}
return f;
}
private static Method getMethod(Class<?> clazz, String name) {
Method m = null;
try {
m = clazz.getDeclaredMethod(name);
m.setAccessible(true);
} catch (Exception e) {
e.printStackTrace();
}
return m;
}
private NestedSystemMessageHandler() {}
/**
* Runs a single nested task on the provided MessageQueue
*/
public static void runSingleNestedLooperTask(MessageQueue queue)
throws IllegalArgumentException, IllegalAccessException, SecurityException,
InvocationTargetException {
// This call will block if there are no messages in the queue. It will
// also run or more pending C++ tasks as a side effect before returning
// |msg|.
Message msg = (Message) sNextMethod.invoke(queue);
if (msg == null) return;
Handler target = (Handler) sMessageTargetField.get(msg);
if (target != null) {
target.dispatchMessage(msg);
}
// Unset in-use flag.
Integer oldFlags = (Integer) sMessageFlagsField.get(msg);
sMessageFlagsField.set(msg, oldFlags & ~(1 << 0 /* FLAG_IN_USE */));
msg.recycle();
}
/**
* Dispatches the first message from the current MessageQueue, blocking
* until a task becomes available if the queue is empty. Callbacks for
......@@ -91,9 +38,8 @@ public class NestedSystemMessageHandler {
@SuppressWarnings("unused")
@CalledByNative
private static boolean dispatchOneMessage() {
MessageQueue queue = Looper.myQueue();
try {
runSingleNestedLooperTask(queue);
LooperUtils.runSingleNestedLooperTask();
} catch (IllegalArgumentException | IllegalAccessException | SecurityException
| InvocationTargetException e) {
e.printStackTrace();
......
......@@ -4,13 +4,7 @@
package org.chromium.content_public.browser.test.util;
import android.text.TextUtils;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.StringDescription;
import org.chromium.base.test.util.CriteriaNotSatisfiedException;
/**
* Provides a means for validating whether some condition/criteria has been met.
......@@ -29,7 +23,7 @@ public final class Criteria {
* @param matcher Determines if the current value matches the desired expectation.
*/
public static <T> void checkThat(T actual, Matcher<T> matcher) {
checkThat("", actual, matcher);
org.chromium.base.test.util.Criteria.checkThat(actual, matcher);
}
/**
......@@ -42,16 +36,6 @@ public final class Criteria {
* @param matcher Determines if the current value matches the desired expectation.
*/
public static <T> void checkThat(String reason, T actual, Matcher<T> matcher) {
if (matcher.matches(actual)) return;
Description description = new StringDescription();
if (!TextUtils.isEmpty(reason)) {
description.appendText(reason).appendText(System.lineSeparator());
}
description.appendText("Expected: ")
.appendDescriptionOf(matcher)
.appendText(System.lineSeparator())
.appendText(" but: ");
matcher.describeMismatch(actual, description);
throw new CriteriaNotSatisfiedException(description.toString());
org.chromium.base.test.util.Criteria.checkThat(reason, actual, matcher);
}
}
......@@ -4,22 +4,11 @@
package org.chromium.content_public.browser.test.util;
import android.os.Handler;
import android.os.Looper;
import org.hamcrest.Matchers;
import org.chromium.base.ThreadUtils;
import org.chromium.base.test.util.CriteriaNotSatisfiedException;
import org.chromium.base.test.util.TimeoutTimer;
import org.chromium.content_public.browser.test.NestedSystemMessageHandler;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
/**
* This class is in the process of being moved to base/. Please use that version instead.
*
* Helper methods for creating and managing criteria.
*
* <p>
......@@ -83,74 +72,8 @@ public class CriteriaHelper {
*/
public static void pollInstrumentationThread(
Runnable criteria, long maxTimeoutMs, long checkIntervalMs) {
assert !ThreadUtils.runningOnUiThread();
pollThreadInternal(criteria, maxTimeoutMs, checkIntervalMs, false);
}
private static void pollThreadInternal(
Runnable criteria, long maxTimeoutMs, long checkIntervalMs, boolean shouldNest) {
Throwable throwable;
try {
criteria.run();
return;
} catch (Throwable e) {
// Espresso catches, wraps, and re-throws the exception we want the CriteriaHelper
// to catch.
if (e instanceof CriteriaNotSatisfiedException
|| e.getCause() instanceof CriteriaNotSatisfiedException) {
throwable = e;
} else {
throw e;
}
}
TimeoutTimer timer = new TimeoutTimer(maxTimeoutMs);
while (!timer.isTimedOut()) {
if (shouldNest) {
nestThread(checkIntervalMs);
} else {
sleepThread(checkIntervalMs);
}
try {
criteria.run();
return;
} catch (Throwable e) {
if (e instanceof CriteriaNotSatisfiedException
|| e.getCause() instanceof CriteriaNotSatisfiedException) {
throwable = e;
} else {
throw e;
}
}
}
throw new AssertionError(throwable);
}
private static void sleepThread(long checkIntervalMs) {
try {
Thread.sleep(checkIntervalMs);
} catch (InterruptedException e) {
// Catch the InterruptedException. If the exception occurs before maxTimeoutMs
// and the criteria is not satisfied, the while loop will run again.
}
}
private static void nestThread(long checkIntervalMs) {
AtomicBoolean called = new AtomicBoolean(false);
// Ensure we pump the message handler in case no new tasks arrive.
new Handler(Looper.myLooper()).postDelayed(() -> { called.set(true); }, checkIntervalMs);
TimeoutTimer timer = new TimeoutTimer(checkIntervalMs);
// To allow a checkInterval of 0ms, ensure we at least run a single task, which allows a
// test to check conditions between each task run on the thread.
do {
try {
NestedSystemMessageHandler.runSingleNestedLooperTask(Looper.myQueue());
} catch (IllegalArgumentException | IllegalAccessException | SecurityException
| InvocationTargetException e) {
throw new RuntimeException(e);
}
} while (!timer.isTimedOut() && !called.get());
org.chromium.base.test.util.CriteriaHelper.pollInstrumentationThread(
criteria, maxTimeoutMs, checkIntervalMs);
}
/**
......@@ -165,7 +88,7 @@ public class CriteriaHelper {
* @see #pollInstrumentationThread(Criteria, long, long)
*/
public static void pollInstrumentationThread(Runnable criteria) {
pollInstrumentationThread(criteria, DEFAULT_MAX_TIME_TO_POLL, DEFAULT_POLLING_INTERVAL);
org.chromium.base.test.util.CriteriaHelper.pollInstrumentationThread(criteria);
}
/**
......@@ -184,8 +107,8 @@ public class CriteriaHelper {
*/
public static void pollInstrumentationThread(final Callable<Boolean> criteria,
String failureReason, long maxTimeoutMs, long checkIntervalMs) {
pollInstrumentationThread(
toNotSatisfiedRunnable(criteria, failureReason), maxTimeoutMs, checkIntervalMs);
org.chromium.base.test.util.CriteriaHelper.pollInstrumentationThread(
criteria, failureReason, maxTimeoutMs, checkIntervalMs);
}
/**
......@@ -203,7 +126,8 @@ public class CriteriaHelper {
*/
public static void pollInstrumentationThread(
final Callable<Boolean> criteria, long maxTimeoutMs, long checkIntervalMs) {
pollInstrumentationThread(criteria, null, maxTimeoutMs, checkIntervalMs);
org.chromium.base.test.util.CriteriaHelper.pollInstrumentationThread(
criteria, maxTimeoutMs, checkIntervalMs);
}
/**
......@@ -217,8 +141,8 @@ public class CriteriaHelper {
* @param failureReason The static failure reason
*/
public static void pollInstrumentationThread(Callable<Boolean> criteria, String failureReason) {
pollInstrumentationThread(
criteria, failureReason, DEFAULT_MAX_TIME_TO_POLL, DEFAULT_POLLING_INTERVAL);
org.chromium.base.test.util.CriteriaHelper.pollInstrumentationThread(
criteria, failureReason);
}
/**
......@@ -231,7 +155,7 @@ public class CriteriaHelper {
* @param criteria The Callable<Boolean> that will be checked.
*/
public static void pollInstrumentationThread(Callable<Boolean> criteria) {
pollInstrumentationThread(criteria, null);
org.chromium.base.test.util.CriteriaHelper.pollInstrumentationThread(criteria);
}
/**
......@@ -248,27 +172,8 @@ public class CriteriaHelper {
*/
public static void pollUiThread(
final Runnable criteria, long maxTimeoutMs, long checkIntervalMs) {
assert !ThreadUtils.runningOnUiThread();
pollInstrumentationThread(() -> {
AtomicReference<Throwable> throwableRef = new AtomicReference<>();
ThreadUtils.runOnUiThreadBlocking(() -> {
try {
criteria.run();
} catch (Throwable t) {
throwableRef.set(t);
}
});
Throwable throwable = throwableRef.get();
if (throwable != null) {
if (throwable instanceof CriteriaNotSatisfiedException) {
throw new CriteriaNotSatisfiedException(throwable);
} else if (throwable instanceof RuntimeException) {
throw (RuntimeException) throwable;
} else {
throw new RuntimeException(throwable);
}
}
}, maxTimeoutMs, checkIntervalMs);
org.chromium.base.test.util.CriteriaHelper.pollUiThread(
criteria, maxTimeoutMs, checkIntervalMs);
}
/**
......@@ -279,7 +184,7 @@ public class CriteriaHelper {
* @see #pollInstrumentationThread(Runnable)
*/
public static void pollUiThread(final Runnable criteria) {
pollUiThread(criteria, DEFAULT_MAX_TIME_TO_POLL, DEFAULT_POLLING_INTERVAL);
org.chromium.base.test.util.CriteriaHelper.pollUiThread(criteria);
}
/**
......@@ -296,8 +201,8 @@ public class CriteriaHelper {
*/
public static void pollUiThread(final Callable<Boolean> criteria, String failureReason,
long maxTimeoutMs, long checkIntervalMs) {
pollUiThread(
toNotSatisfiedRunnable(criteria, failureReason), maxTimeoutMs, checkIntervalMs);
org.chromium.base.test.util.CriteriaHelper.pollUiThread(
criteria, failureReason, maxTimeoutMs, checkIntervalMs);
}
/**
......@@ -313,7 +218,8 @@ public class CriteriaHelper {
*/
public static void pollUiThread(
final Callable<Boolean> criteria, long maxTimeoutMs, long checkIntervalMs) {
pollUiThread(criteria, null, maxTimeoutMs, checkIntervalMs);
org.chromium.base.test.util.CriteriaHelper.pollUiThread(
criteria, maxTimeoutMs, checkIntervalMs);
}
/**
......@@ -325,7 +231,7 @@ public class CriteriaHelper {
* @see #pollInstrumentationThread(Criteria)
*/
public static void pollUiThread(final Callable<Boolean> criteria, String failureReason) {
pollUiThread(criteria, failureReason, DEFAULT_MAX_TIME_TO_POLL, DEFAULT_POLLING_INTERVAL);
org.chromium.base.test.util.CriteriaHelper.pollUiThread(criteria, failureReason);
}
/**
......@@ -336,7 +242,7 @@ public class CriteriaHelper {
* @see #pollInstrumentationThread(Criteria)
*/
public static void pollUiThread(final Callable<Boolean> criteria) {
pollUiThread(criteria, null);
org.chromium.base.test.util.CriteriaHelper.pollUiThread(criteria);
}
/**
......@@ -354,8 +260,8 @@ public class CriteriaHelper {
*/
public static void pollUiThreadNested(
Runnable criteria, long maxTimeoutMs, long checkIntervalMs) {
assert ThreadUtils.runningOnUiThread();
pollThreadInternal(criteria, maxTimeoutMs, checkIntervalMs, true);
org.chromium.base.test.util.CriteriaHelper.pollUiThreadNested(
criteria, maxTimeoutMs, checkIntervalMs);
}
/**
......@@ -372,7 +278,8 @@ public class CriteriaHelper {
*/
public static void pollUiThreadNested(
final Callable<Boolean> criteria, long maxTimeoutMs, long checkIntervalMs) {
pollUiThreadNested(toNotSatisfiedRunnable(criteria, null), maxTimeoutMs, checkIntervalMs);
org.chromium.base.test.util.CriteriaHelper.pollUiThreadNested(
criteria, maxTimeoutMs, checkIntervalMs);
}
/**
......@@ -383,7 +290,7 @@ public class CriteriaHelper {
* @see #pollInstrumentationThread(Runnable)
*/
public static void pollUiThreadNested(final Runnable criteria) {
pollUiThreadNested(criteria, DEFAULT_MAX_TIME_TO_POLL, DEFAULT_POLLING_INTERVAL);
org.chromium.base.test.util.CriteriaHelper.pollUiThreadNested(criteria);
}
/**
......@@ -394,7 +301,7 @@ public class CriteriaHelper {
* @see #pollInstrumentationThread(Criteria)
*/
public static void pollUiThreadNested(final Callable<Boolean> criteria) {
pollUiThreadNested(toNotSatisfiedRunnable(criteria, null));
org.chromium.base.test.util.CriteriaHelper.pollUiThreadNested(criteria);
}
/**
......@@ -406,7 +313,7 @@ public class CriteriaHelper {
* @see #pollInstrumentationThread(Criteria)
*/
public static void pollUiThreadForJUnit(final Callable<Boolean> criteria) {
pollUiThreadForJUnit(toNotSatisfiedRunnable(criteria, null));
org.chromium.base.test.util.CriteriaHelper.pollUiThreadForJUnit(criteria);
}
/**
......@@ -418,23 +325,6 @@ public class CriteriaHelper {
* @see #pollInstrumentationThread(Criteria)
*/
public static void pollUiThreadForJUnit(final Runnable criteria) {
assert ThreadUtils.runningOnUiThread();
pollThreadInternal(
criteria, DEFAULT_JUNIT_MAX_TIME_TO_POLL, DEFAULT_JUNIT_POLLING_INTERVAL, false);
}
private static Runnable toNotSatisfiedRunnable(
Callable<Boolean> criteria, String failureReason) {
return () -> {
boolean isSatisfied;
try {
isSatisfied = criteria.call();
} catch (RuntimeException re) {
throw re;
} catch (Exception e) {
throw new RuntimeException(e);
}
Criteria.checkThat(failureReason, isSatisfied, Matchers.is(true));
};
org.chromium.base.test.util.CriteriaHelper.pollUiThreadForJUnit(criteria);
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment