Bug 1465480 - Add @IgnoreCrash to GeckoSessionTestRule r=jchen

This allows a test to declare that crashes should not be considered
fatal. It does not, however, assert that a crash will occur. That
responsibility is left with the test itself.

MozReview-Commit-ID: 4DB6xs5jlEZ
This commit is contained in:
James Willcox 2018-05-31 10:28:34 -05:00
parent 2a505d2d75
commit 4f1fa4bc6e
3 changed files with 67 additions and 14 deletions

View File

@ -26,6 +26,7 @@ import kotlin.reflect.KClass
open class BaseSessionTest(noErrorCollector: Boolean = false) {
companion object {
const val CLICK_TO_RELOAD_HTML_PATH = "/assets/www/clickToReload.html"
const val CONTENT_CRASH_URL = "about:crashcontent"
const val DOWNLOAD_HTML_PATH = "/assets/www/download.html"
const val HELLO_HTML_PATH = "/assets/www/hello.html"
const val HELLO2_HTML_PATH = "/assets/www/hello2.html"

View File

@ -8,8 +8,10 @@ import org.mozilla.geckoview.GeckoSession
import org.mozilla.geckoview.GeckoSessionSettings
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.AssertCalled
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.ClosedSessionAtStart
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.IgnoreCrash
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.NullDelegate
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.RejectedPromiseException
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.ReuseSession
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.Setting
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.TimeoutException
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.TimeoutMillis
@ -1604,31 +1606,56 @@ class GeckoSessionTestRuleTest : BaseSessionTest(noErrorCollector = true) {
val unregister = { _: TestDelegate -> delegate = null }
sessionRule.addExternalDelegateDuringNextWait(TestDelegate::class, register, unregister,
object : TestDelegate {
@AssertCalled(count = 1)
override fun onDelegate(foo: String, bar: String): Int {
return 24
}
})
object : TestDelegate {
@AssertCalled(count = 1)
override fun onDelegate(foo: String, bar: String): Int {
return 24
}
})
sessionRule.addExternalDelegateUntilTestEnd(TestDelegate::class, register, unregister,
object : TestDelegate {
@AssertCalled(count = 1)
override fun onDelegate(foo: String, bar: String): Int {
return 42
}
})
object : TestDelegate {
@AssertCalled(count = 1)
override fun onDelegate(foo: String, bar: String): Int {
return 42
}
})
assertThat("Wait delegate should be registered", delegate, notNullValue())
assertThat("Wait delegate return value should be correct",
delegate?.onDelegate("", ""), equalTo(24))
delegate?.onDelegate("", ""), equalTo(24))
mainSession.reload()
mainSession.waitForPageStop()
assertThat("Test delegate should still be registered", delegate, notNullValue())
assertThat("Test delegate return value should be correct",
delegate?.onDelegate("", ""), equalTo(42))
delegate?.onDelegate("", ""), equalTo(42))
sessionRule.performTestEndCheck()
}
@IgnoreCrash
@ReuseSession(false)
@Test fun contentCrashIgnored() {
assumeThat(sessionRule.env.isMultiprocess, equalTo(true))
// This test has some kind of strange race on ARM emulators
assumeThat(!(sessionRule.env.isEmulator && sessionRule.env.cpuArch.contains("arm")),
equalTo(true))
sessionRule.session.loadUri(CONTENT_CRASH_URL)
sessionRule.waitUntilCalled(object : Callbacks.ContentDelegate {
@AssertCalled(count = 1)
override fun onCrash(session: GeckoSession) = Unit
})
}
@Test(expected = RuntimeException::class)
@ReuseSession(false)
fun contentCrashFails() {
assumeThat(sessionRule.env.isMultiprocess, equalTo(true))
sessionRule.session.loadUri(CONTENT_CRASH_URL)
sessionRule.waitForPageStop()
}
}

View File

@ -97,6 +97,7 @@ public class GeckoSessionTestRule extends UiThreadTestRule {
private static final Method sGetNextMessage;
private static final Method sOnPageStop;
private static final Method sOnNewSession;
private static final Method sOnCrash;
static {
try {
@ -106,6 +107,8 @@ public class GeckoSessionTestRule extends UiThreadTestRule {
"onPageStop", GeckoSession.class, boolean.class);
sOnNewSession = GeckoSession.NavigationDelegate.class.getMethod(
"onNewSession", GeckoSession.class, String.class, GeckoResponse.class);
sOnCrash = GeckoSession.ContentDelegate.class.getMethod(
"onCrash", GeckoSession.class);
} catch (final NoSuchMethodException e) {
throw new RuntimeException(e);
}
@ -315,6 +318,20 @@ public class GeckoSessionTestRule extends UiThreadTestRule {
void invoke(T delegate) throws Throwable;
}
/*
* If the value here is true, content crashes will be ignored. If false, the test will
* be failed immediately if a content crash occurs. This is also the case when
* {@link IgnoreCrash} is not present.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface IgnoreCrash {
/**
* @return True if content crashes should be ignored, false otherwise. Default is true.
*/
boolean value() default true;
}
public static class TimeoutException extends RuntimeException {
public TimeoutException(final String detailMessage) {
super(detailMessage);
@ -904,6 +921,7 @@ public class GeckoSessionTestRule extends UiThreadTestRule {
protected Map<GeckoSession, Tab> mRDPTabs;
protected Tab mRDPChromeProcess;
protected boolean mReuseSession;
protected boolean mIgnoreCrash;
public GeckoSessionTestRule() {
mDefaultSettings = new GeckoSessionSettings();
@ -1076,6 +1094,8 @@ public class GeckoSessionTestRule extends UiThreadTestRule {
mWithDevTools = ((WithDevToolsAPI) annotation).value();
} else if (ReuseSession.class.equals(annotation.annotationType())) {
mReuseSession = ((ReuseSession) annotation).value();
} else if (IgnoreCrash.class.equals(annotation.annotationType())) {
mIgnoreCrash = ((IgnoreCrash) annotation).value();
}
}
}
@ -1144,8 +1164,13 @@ public class GeckoSessionTestRule extends UiThreadTestRule {
ignore = mCallRecordHandler.handleCall(method, args);
}
if (!mIgnoreCrash && sOnCrash.equals(method)) {
throw new RuntimeException("Content process crashed");
}
final boolean isExternalDelegate =
!DEFAULT_DELEGATES.contains(method.getDeclaringClass());
if (!ignore) {
assertThat("Callbacks must be on UI thread",
Looper.myLooper(), equalTo(Looper.getMainLooper()));