зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1553515 - Replace loopUntilIdle -> waitForCondition. r=snorp
Differential Revision: https://phabricator.services.mozilla.com/D32583 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
4f0dfa866e
Коммит
b9b75a5400
|
@ -1,8 +1,7 @@
|
|||
package org.mozilla.geckoview.test;
|
||||
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
import org.mozilla.geckoview.GeckoResult;
|
||||
import org.mozilla.geckoview.GeckoResult.OnExceptionListener;
|
||||
import org.mozilla.geckoview.GeckoResult.OnValueListener;
|
||||
import org.mozilla.geckoview.test.util.Environment;
|
||||
import org.mozilla.geckoview.test.util.UiThreadUtils;
|
||||
|
||||
|
@ -34,19 +33,11 @@ public class GeckoResultTest {
|
|||
|
||||
private void waitUntilDone() {
|
||||
assertThat("We should not be done", mDone, equalTo(false));
|
||||
|
||||
while (!mDone) {
|
||||
UiThreadUtils.loopUntilIdle(mEnv.getDefaultTimeoutMillis());
|
||||
}
|
||||
UiThreadUtils.waitForCondition(() -> mDone, mEnv.getDefaultTimeoutMillis());
|
||||
}
|
||||
|
||||
private void done() {
|
||||
UiThreadUtils.HANDLER.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mDone = true;
|
||||
}
|
||||
});
|
||||
UiThreadUtils.HANDLER.post(() -> mDone = true);
|
||||
}
|
||||
|
||||
@Before
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.junit.Before
|
|||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.geckoview.test.util.HttpBin
|
||||
import org.mozilla.geckoview.test.util.UiThreadUtils
|
||||
import java.net.URI
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
|
@ -1193,6 +1194,7 @@ class NavigationDelegateTest : BaseSessionTest() {
|
|||
|
||||
sessionRule.session.waitUntilCalled(GeckoSession.NavigationDelegate::class,
|
||||
"onNewSession")
|
||||
UiThreadUtils.loopUntilIdle(sessionRule.env.defaultTimeoutMillis)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -481,20 +481,10 @@ class SessionLifecycleTest : BaseSessionTest() {
|
|||
}
|
||||
|
||||
private fun waitUntilCollected(ref: QueuedWeakReference<*>) {
|
||||
val start = SystemClock.uptimeMillis()
|
||||
while (ref.queue.poll() == null) {
|
||||
val elapsed = SystemClock.uptimeMillis() - start
|
||||
if (elapsed > sessionRule.timeoutMillis) {
|
||||
dumpHprof()
|
||||
throw UiThreadUtils.TimeoutException("Timed out after " + elapsed + "ms")
|
||||
}
|
||||
|
||||
try {
|
||||
UiThreadUtils.loopUntilIdle(100)
|
||||
} catch (e: UiThreadUtils.TimeoutException) {
|
||||
}
|
||||
UiThreadUtils.waitForCondition({
|
||||
Runtime.getRuntime().gc()
|
||||
}
|
||||
ref.queue.poll() != null
|
||||
}, sessionRule.timeoutMillis)
|
||||
}
|
||||
|
||||
class QueuedWeakReference<T> @JvmOverloads constructor(obj: T, var queue: ReferenceQueue<T> =
|
||||
|
|
|
@ -1055,7 +1055,7 @@ public class GeckoSessionTestRule implements TestRule {
|
|||
return new RuntimeException(cause != null ? cause : e);
|
||||
}
|
||||
|
||||
protected void prepareStatement(final Description description) throws Throwable {
|
||||
protected void prepareStatement(final Description description) {
|
||||
final GeckoSessionSettings settings = new GeckoSessionSettings(mDefaultSettings);
|
||||
mTimeoutMillis = env.getDefaultTimeoutMillis();
|
||||
mNullDelegates = new HashSet<>();
|
||||
|
@ -1195,9 +1195,13 @@ public class GeckoSessionTestRule implements TestRule {
|
|||
}
|
||||
}
|
||||
|
||||
protected void prepareSession(final GeckoSession session) throws Throwable {
|
||||
protected void prepareSession(final GeckoSession session) {
|
||||
for (final Class<?> cls : DEFAULT_DELEGATES) {
|
||||
setDelegate(cls, session, mNullDelegates.contains(cls) ? null : mCallbackProxy);
|
||||
try {
|
||||
setDelegate(cls, session, mNullDelegates.contains(cls) ? null : mCallbackProxy);
|
||||
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1234,6 +1238,7 @@ public class GeckoSessionTestRule implements TestRule {
|
|||
}
|
||||
|
||||
private void waitForInitialLoad(final GeckoSession session) {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
// We receive an initial about:blank load; don't expose that to the test. The initial
|
||||
// load ends with the first onPageStop call, so ignore everything from the session
|
||||
// until the first onPageStop call.
|
||||
|
@ -1295,7 +1300,7 @@ public class GeckoSessionTestRule implements TestRule {
|
|||
}
|
||||
}
|
||||
|
||||
protected void cleanupStatement() throws Throwable {
|
||||
protected void cleanupStatement() {
|
||||
mWaitScopeDelegates.clear();
|
||||
mTestScopeDelegates.clear();
|
||||
|
||||
|
@ -1339,21 +1344,19 @@ public class GeckoSessionTestRule implements TestRule {
|
|||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
final AtomicReference<Throwable> exceptionRef = new AtomicReference<>();
|
||||
mInstrumentation.runOnMainSync(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
mInstrumentation.runOnMainSync(() -> {
|
||||
try {
|
||||
prepareStatement(description);
|
||||
base.evaluate();
|
||||
performTestEndCheck();
|
||||
} catch (Throwable t) {
|
||||
exceptionRef.set(t);
|
||||
} finally {
|
||||
try {
|
||||
prepareStatement(description);
|
||||
base.evaluate();
|
||||
performTestEndCheck();
|
||||
cleanupStatement();
|
||||
} catch (Throwable t) {
|
||||
exceptionRef.set(t);
|
||||
} finally {
|
||||
try {
|
||||
cleanupStatement();
|
||||
} catch (Throwable t) {
|
||||
exceptionRef.set(t);
|
||||
}
|
||||
exceptionRef.compareAndSet(null, t);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1553,9 +1556,11 @@ public class GeckoSessionTestRule implements TestRule {
|
|||
forCallbacksDuringWait(session, callback);
|
||||
}
|
||||
|
||||
protected void waitUntilCalled(final @Nullable GeckoSession session,
|
||||
final @NonNull Class<?> delegate,
|
||||
final @NonNull List<MethodCall> methodCalls) {
|
||||
private void waitUntilCalled(final @Nullable GeckoSession session,
|
||||
final @NonNull Class<?> delegate,
|
||||
final @NonNull List<MethodCall> methodCalls) {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
|
||||
if (session != null && !session.equals(mMainSession)) {
|
||||
assertThat("Session should be wrapped through wrapSession",
|
||||
session, isIn(mSubSessions));
|
||||
|
@ -1602,15 +1607,8 @@ public class GeckoSessionTestRule implements TestRule {
|
|||
beforeWait();
|
||||
|
||||
while (!calledAny || !methodCalls.isEmpty()) {
|
||||
while (index >= mCallRecords.size()) {
|
||||
UiThreadUtils.loopUntilIdle(mTimeoutMillis);
|
||||
// We could loop forever here if the UI thread keeps receiving
|
||||
// messages that don't result in any methods being called.
|
||||
// Check whether we've exceeded our allotted time and bail out.
|
||||
if (SystemClock.uptimeMillis() - startTime > mTimeoutMillis) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
final int checkIndex = index;
|
||||
UiThreadUtils.waitForCondition(() -> (checkIndex < mCallRecords.size()), mTimeoutMillis);
|
||||
|
||||
if (SystemClock.uptimeMillis() - startTime > mTimeoutMillis) {
|
||||
throw new UiThreadUtils.TimeoutException("Timed out after " + mTimeoutMillis + "ms");
|
||||
|
|
|
@ -5,11 +5,13 @@ import org.mozilla.geckoview.GeckoRuntimeSettings;
|
|||
import org.mozilla.geckoview.test.TestCrashHandler;
|
||||
|
||||
import android.os.Process;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
|
||||
public class RuntimeCreator {
|
||||
private static GeckoRuntime sRuntime;
|
||||
|
||||
@UiThread
|
||||
public static GeckoRuntime getRuntime() {
|
||||
if (sRuntime != null) {
|
||||
return sRuntime;
|
||||
|
@ -30,12 +32,7 @@ public class RuntimeCreator {
|
|||
InstrumentationRegistry.getTargetContext(),
|
||||
runtimeSettingsBuilder.build());
|
||||
|
||||
sRuntime.setDelegate(new GeckoRuntime.Delegate() {
|
||||
@Override
|
||||
public void onShutdown() {
|
||||
Process.killProcess(Process.myPid());
|
||||
}
|
||||
});
|
||||
sRuntime.setDelegate(() -> Process.killProcess(Process.myPid()));
|
||||
|
||||
return sRuntime;
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
package org.mozilla.geckoview.test.util;
|
||||
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
import org.mozilla.geckoview.GeckoResult;
|
||||
|
||||
import android.os.Handler;
|
||||
|
@ -13,15 +14,16 @@ import android.os.Message;
|
|||
import android.os.MessageQueue;
|
||||
import android.os.SystemClock;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.internal.runner.InstrumentationConnection;
|
||||
import android.util.Log;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class UiThreadUtils {
|
||||
private static final String LOGTAG = "UiThreadUtils";
|
||||
private static long sLongestWait;
|
||||
|
||||
private static Method sGetNextMessage = null;
|
||||
static {
|
||||
try {
|
||||
|
@ -59,16 +61,6 @@ public class UiThreadUtils {
|
|||
|
||||
public static final Handler HANDLER = new Handler(Looper.getMainLooper());
|
||||
private static final TimeoutRunnable TIMEOUT_RUNNABLE = new TimeoutRunnable();
|
||||
private static final MessageQueue.IdleHandler IDLE_HANDLER = new MessageQueue.IdleHandler() {
|
||||
@Override
|
||||
public boolean queueIdle() {
|
||||
final Message msg = Message.obtain(HANDLER);
|
||||
msg.obj = HANDLER;
|
||||
HANDLER.sendMessageAtFrontOfQueue(msg);
|
||||
return false; // Remove this idle handler.
|
||||
}
|
||||
};
|
||||
|
||||
private static RuntimeException unwrapRuntimeException(final Throwable e) {
|
||||
final Throwable cause = e.getCause();
|
||||
if (cause != null && cause instanceof RuntimeException) {
|
||||
|
@ -91,9 +83,7 @@ public class UiThreadUtils {
|
|||
public static <T> T waitForResult(@NonNull GeckoResult<T> result, long timeout) throws Throwable {
|
||||
final ResultHolder<T> holder = new ResultHolder<>(result);
|
||||
|
||||
while (!holder.isComplete) {
|
||||
loopUntilIdle(timeout);
|
||||
}
|
||||
waitForCondition(() -> holder.isComplete, timeout);
|
||||
|
||||
if (holder.error != null) {
|
||||
throw holder.error;
|
||||
|
@ -122,6 +112,27 @@ public class UiThreadUtils {
|
|||
boolean test();
|
||||
}
|
||||
|
||||
public static void loopUntilIdle(final long timeout) {
|
||||
AtomicBoolean idle = new AtomicBoolean(false);
|
||||
|
||||
MessageQueue.IdleHandler handler = null;
|
||||
try {
|
||||
handler = () -> {
|
||||
idle.set(true);
|
||||
// Remove handler
|
||||
return false;
|
||||
};
|
||||
|
||||
HANDLER.getLooper().getQueue().addIdleHandler(handler);
|
||||
|
||||
waitForCondition(() -> idle.get(), timeout);
|
||||
} finally {
|
||||
if (handler != null) {
|
||||
HANDLER.getLooper().getQueue().removeIdleHandler(handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void waitForCondition(Condition condition, final long timeout) {
|
||||
// Adapted from GeckoThread.pumpMessageLoop.
|
||||
final MessageQueue queue = HANDLER.getLooper().getQueue();
|
||||
|
@ -146,49 +157,4 @@ public class UiThreadUtils {
|
|||
TIMEOUT_RUNNABLE.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
public static void loopUntilIdle(final long timeout) {
|
||||
// Adapted from GeckoThread.pumpMessageLoop.
|
||||
final MessageQueue queue = HANDLER.getLooper().getQueue();
|
||||
if (timeout > 0) {
|
||||
TIMEOUT_RUNNABLE.set(timeout);
|
||||
} else {
|
||||
queue.addIdleHandler(IDLE_HANDLER);
|
||||
}
|
||||
|
||||
final long startTime = SystemClock.uptimeMillis();
|
||||
try {
|
||||
while (true) {
|
||||
final Message msg;
|
||||
try {
|
||||
msg = (Message) sGetNextMessage.invoke(queue);
|
||||
} catch (final IllegalAccessException | InvocationTargetException e) {
|
||||
throw unwrapRuntimeException(e);
|
||||
}
|
||||
if (msg.getTarget() == HANDLER && msg.obj == HANDLER) {
|
||||
// Our idle signal.
|
||||
break;
|
||||
} else if (msg.getTarget() == null) {
|
||||
HANDLER.getLooper().quit();
|
||||
return;
|
||||
}
|
||||
msg.getTarget().dispatchMessage(msg);
|
||||
|
||||
if (timeout > 0) {
|
||||
TIMEOUT_RUNNABLE.cancel();
|
||||
queue.addIdleHandler(IDLE_HANDLER);
|
||||
}
|
||||
}
|
||||
|
||||
final long waitDuration = SystemClock.uptimeMillis() - startTime;
|
||||
if (waitDuration > sLongestWait) {
|
||||
sLongestWait = waitDuration;
|
||||
Log.i(LOGTAG, "New longest wait: " + waitDuration + "ms");
|
||||
}
|
||||
} finally {
|
||||
if (timeout > 0) {
|
||||
TIMEOUT_RUNNABLE.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче