blob: ca2279e1a6b5b4562a3bf8cd46a5d171a45647de [file] [log] [blame]
// 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.android_webview.test;
import android.support.test.InstrumentationRegistry;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.chromium.android_webview.AwContents;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* Base class for WebView find-in-page API tests.
*/
public class WebViewFindApisTestRule extends AwActivityTestRule {
private static final String WOODCHUCK =
"How much WOOD would a woodchuck chuck if a woodchuck could chuck wOoD?";
private FindResultListener mFindResultListener;
private AwContents mContents;
@Override
public Statement apply(final Statement base, Description description) {
return super.apply(new Statement() {
@Override
public void evaluate() throws Throwable {
try {
mContents = loadContentsFromStringSync(WOODCHUCK);
} catch (Throwable t) {
throw new Exception(t);
}
base.evaluate();
}
}, description);
}
public AwContents contents() {
return mContents;
}
// Internal interface to intercept find results from AwContentsClient.
private interface FindResultListener {
public void onFindResultReceived(
int activeMatchOrdinal, int numberOfMatches, boolean isDoneCounting);
}
private AwContents loadContentsFromStringSync(final String html) throws Throwable {
final TestAwContentsClient contentsClient = new TestAwContentsClient() {
@Override
public void onFindResultReceived(
int activeMatchOrdinal, int numberOfMatches, boolean isDoneCounting) {
if (mFindResultListener == null) return;
mFindResultListener.onFindResultReceived(
activeMatchOrdinal, numberOfMatches, isDoneCounting);
}
};
final AwContents contents =
createAwTestContainerViewOnMainSync(contentsClient).getAwContents();
final String data = "<html><head></head><body>" + html + "</body></html>";
loadDataSync(contents, contentsClient.getOnPageFinishedHelper(), data, "text/html", false);
return contents;
}
/**
* Invokes findAllAsync on the UI thread, blocks until find results are
* received, and returns the number of matches.
*
* @param searchString A string to search for.
* @return The number of instances of the string that were found.
* @throws Throwable
*/
public int findAllAsyncOnUiThread(final String searchString) throws Throwable {
final IntegerFuture future = new IntegerFuture() {
@Override
public void run() {
mFindResultListener = (activeMatchOrdinal, numberOfMatches, isDoneCounting) -> {
if (isDoneCounting) set(numberOfMatches);
};
mContents.findAllAsync(searchString);
}
};
InstrumentationRegistry.getInstrumentation().runOnMainSync(future);
return future.get(10, TimeUnit.SECONDS);
}
/**
* Invokes findNext on the UI thread, blocks until find results are
* received, and returns the ordinal of the highlighted match.
*
* @param forwards The direction to search as a boolean, with forwards
* represented as true and backwards as false.
* @return The ordinal of the highlighted match.
* @throws Throwable
*/
public int findNextOnUiThread(final boolean forwards) throws Throwable {
final IntegerFuture future = new IntegerFuture() {
@Override
public void run() {
mFindResultListener = (activeMatchOrdinal, numberOfMatches, isDoneCounting) -> {
if (isDoneCounting) set(activeMatchOrdinal);
};
mContents.findNext(forwards);
}
};
InstrumentationRegistry.getInstrumentation().runOnMainSync(future);
return future.get(10, TimeUnit.SECONDS);
}
/**
* Invokes clearMatches on the UI thread.
*
* @throws Throwable
*/
public void clearMatchesOnUiThread() throws Throwable {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> mContents.clearMatches());
}
// Similar to java.util.concurrent.Future, but without the ability to cancel.
private abstract static class IntegerFuture implements Runnable {
private CountDownLatch mLatch = new CountDownLatch(1);
private int mValue;
@Override
public abstract void run();
/**
* Gets the value of this Future, blocking for up to the specified
* timeout for it become available. Throws a TimeoutException if the
* timeout expires.
*/
public int get(long timeout, TimeUnit unit) throws Throwable {
if (!mLatch.await(timeout, unit)) {
throw new TimeoutException();
}
return mValue;
}
/**
* Sets the value of this Future.
*/
protected void set(int value) {
mValue = value;
mLatch.countDown();
}
}
}