Bug 1572245 - Add TestRuntimeService to restart runtime in tests. r=owlish

This patch adds a new test service class that can be used to more easily write
a test that needs to run multiple runtimes or needs to reset the runtime.

The service also includes an optional Instance class that can be used to
control the service and send and receive messages from the remote runtime.

Differential Revision: https://phabricator.services.mozilla.com/D126542
This commit is contained in:
Agi Sferro 2021-10-05 22:09:58 +00:00
Родитель c8006ed003
Коммит 36efcb63bd
10 изменённых файлов: 503 добавлений и 287 удалений

Просмотреть файл

@ -39,15 +39,15 @@
<!-- This is needed for ParentCrashTest -->
<service
android:name=".crash.RemoteGeckoService"
android:name=".crash.RuntimeCrashTestService"
android:enabled="true"
android:exported="false"
android:process=":gecko">
android:process=":crashtest">
</service>
<!-- This is needed for ProfileLockedTest -->
<service android:name=".TestProfileLockService$p0" android:enabled="true" android:exported="false" android:process=":profiletest0" />
<service android:name=".TestProfileLockService$p1" android:enabled="true" android:exported="false" android:process=":profiletest1" />
<!-- Used to run multiple runtimes during tests -->
<service android:name=".TestRuntimeService$instance0" android:enabled="true" android:exported="false" android:process=":runtime0" />
<service android:name=".TestRuntimeService$instance1" android:enabled="true" android:exported="false" android:process=":runtime1" />
<!-- This is used to run xpcshell tests -->
<service android:name=".XpcshellTestRunnerService$i0" android:enabled="true" android:exported="true" android:process=":xpcshell0"/>

Просмотреть файл

@ -105,10 +105,13 @@ open class BaseSessionTest(noErrorCollector: Boolean = false) {
const val SHOW_DYNAMIC_TOOLBAR_HTML_PATH = "/assets/www/showDynamicToolbar.html"
const val TEST_ENDPOINT = GeckoSessionTestRule.TEST_ENDPOINT
const val TEST_HOST = GeckoSessionTestRule.TEST_HOST
}
@get:Rule val sessionRule = GeckoSessionTestRule()
@get:Rule var temporaryProfile = TemporaryProfileRule()
@get:Rule val errors = ErrorCollector()
val mainSession get() = sessionRule.session

Просмотреть файл

@ -1,113 +1,41 @@
package org.mozilla.geckoview.test
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.*
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import androidx.test.platform.app.InstrumentationRegistry
import org.hamcrest.CoreMatchers.equalTo
import org.junit.Assert.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import org.junit.runner.RunWith
import org.mozilla.geckoview.GeckoResult
import java.io.File
import org.mozilla.geckoview.test.TestRuntimeService.RuntimeInstance
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.ClosedSessionAtStart
@RunWith(AndroidJUnit4::class)
@MediumTest
class ProfileLockedTest {
class ProfileLockedTest : BaseSessionTest() {
private val targetContext
get() = InstrumentationRegistry.getInstrumentation().targetContext
@get:Rule
var folder = TemporaryFolder()
/**
* Starts GeckoRuntime in the process given in input, and waits for the MESSAGE_PAGE_STOP
* event that's fired when the first GeckoSession receives the onPageStop event.
*
* We wait for a page load to make sure that everything started up correctly (as opposed
* to quitting during the startup procedure).
*/
class RuntimeInstance<T>(
private val context: Context,
private val service: Class<T>,
private val profileFolder: File) : ServiceConnection {
var isConnected = false
var disconnected = GeckoResult<Void>()
var started = GeckoResult<Void>()
var quitted = GeckoResult<Void>()
override fun onServiceConnected(name: ComponentName, binder: IBinder) {
val handler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
when (msg.what) {
TestProfileLockService.MESSAGE_PAGE_STOP -> started.complete(null)
TestProfileLockService.MESSAGE_QUIT -> {
quitted.complete(null)
// No reason to keep the service around anymore
context.unbindService(this@RuntimeInstance)
}
}
}
}
val messenger = Messenger(handler)
val service = Messenger(binder)
val msg = Message.obtain(null, TestProfileLockService.MESSAGE_REGISTER)
msg.replyTo = messenger
service.send(msg)
isConnected = true
}
override fun onServiceDisconnected(name: ComponentName?) {
isConnected = false
context.unbindService(this)
disconnected.complete(null)
}
fun start() {
val intent = Intent(context, service)
intent.putExtra("args", "-profile ${profileFolder.absolutePath}")
context.bindService(intent, this, Context.BIND_AUTO_CREATE)
}
}
@Test
@ClosedSessionAtStart
fun profileLocked() {
val profileFolder = folder.newFolder("profile-locked-test")
val runtime0 = RuntimeInstance(targetContext,
TestProfileLockService.p0::class.java,
profileFolder)
val runtime1 = RuntimeInstance(targetContext,
TestProfileLockService.p1::class.java,
profileFolder)
val runtime0 = RuntimeInstance.start(
targetContext, TestRuntimeService.instance0::class.java, temporaryProfile.get())
// Start the first runtime and wait until it's ready
runtime0.start()
runtime0.started.pollDefault()
sessionRule.waitForResult(runtime0.started)
assertThat("The service should be connected now", runtime0.isConnected, equalTo(true))
// Now start a _second_ runtime with the same profile folder, this will kill the first
// runtime
runtime1.start()
val runtime1 = RuntimeInstance.start(
targetContext, TestRuntimeService.instance1::class.java, temporaryProfile.get())
// Wait for the first runtime to disconnect
runtime0.disconnected.pollDefault()
sessionRule.waitForResult(runtime0.disconnected)
// GeckoRuntime will quit after killing the offending process
runtime1.quitted.pollDefault()
sessionRule.waitForResult(runtime1.quitted)
assertThat("The service shouldn't be connected anymore",
runtime0.isConnected, equalTo(false))

Просмотреть файл

@ -0,0 +1,36 @@
package org.mozilla.geckoview.test;
import org.junit.rules.ExternalResource;
import org.junit.rules.TemporaryFolder;
import org.mozilla.geckoview.test.rule.TestHarnessException;
import java.io.File;
import java.io.IOException;
/** Lazily provides a temporary profile folder for tests. */
public class TemporaryProfileRule extends ExternalResource {
TemporaryFolder mTemporaryFolder;
File mProfileFolder;
@Override
protected void after() {
if (mTemporaryFolder != null) {
mTemporaryFolder.delete();
mProfileFolder = null;
}
}
public File get() {
if (mProfileFolder == null) {
mTemporaryFolder = new TemporaryFolder();
try {
mTemporaryFolder.create();
mProfileFolder = mTemporaryFolder.newFolder("test-profile");
} catch (IOException ex) {
throw new TestHarnessException(ex);
}
}
return mProfileFolder;
}
}

Просмотреть файл

@ -1,102 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.geckoview.test;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.mozilla.geckoview.GeckoRuntime;
import org.mozilla.geckoview.GeckoRuntimeSettings;
import org.mozilla.geckoview.GeckoSession;
public class TestProfileLockService extends Service implements
GeckoSession.ProgressDelegate,
GeckoRuntime.Delegate {
public static final class p0 extends TestProfileLockService {}
public static final class p1 extends TestProfileLockService {}
// Used by the client to register themselves
public static final int MESSAGE_REGISTER = 1;
// Sent when the first page load completes
public static final int MESSAGE_PAGE_STOP = 2;
// Sent when GeckoRuntime exits
public static final int MESSAGE_QUIT = 3;
private GeckoRuntime mRuntime;
private Messenger mClient;
private class Handler extends android.os.Handler {
@Override
public void handleMessage(@NonNull final Message msg) {
switch (msg.what) {
case MESSAGE_REGISTER:
mClient = msg.replyTo;
return;
default:
throw new IllegalStateException("Unknown message: " + msg.what);
}
}
}
final Messenger mMessenger = new Messenger(new Handler());
@Override
public void onShutdown() {
final Message message = Message.obtain(null, MESSAGE_QUIT);
try {
mClient.send(message);
} catch (final RemoteException ex) {
throw new RuntimeException(ex);
}
}
@Override
public void onPageStop(@NonNull final GeckoSession session, final boolean success) {
final Message message = Message.obtain(null, MESSAGE_PAGE_STOP);
try {
mClient.send(message);
} catch (final RemoteException ex) {
throw new RuntimeException(ex);
}
}
@Override
public void onDestroy() {
// Sometimes the service doesn't die on it's own so we need to kill it here.
System.exit(0);
}
@Nullable
@Override
public IBinder onBind(final Intent intent) {
// Request to be killed as soon as the client unbinds.
stopSelf();
if (mRuntime != null) {
// We only expect one client
throw new RuntimeException("Multiple clients !?");
}
final GeckoRuntimeSettings settings = new GeckoRuntimeSettings.Builder()
.extras(intent.getExtras())
.build();
mRuntime = GeckoRuntime.create(getApplicationContext(), settings);
mRuntime.setDelegate(this);
final GeckoSession session = new GeckoSession();
session.setProgressDelegate(this);
session.open(mRuntime);
return mMessenger.getBinder();
}
}

Просмотреть файл

@ -0,0 +1,412 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.geckoview.test;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.mozilla.gecko.util.GeckoBundle;
import org.mozilla.geckoview.GeckoResult;
import org.mozilla.geckoview.GeckoRuntime;
import org.mozilla.geckoview.GeckoRuntimeSettings;
import org.mozilla.geckoview.GeckoSession;
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class TestRuntimeService extends Service implements
GeckoSession.ProgressDelegate,
GeckoRuntime.Delegate {
// Used by the client to register themselves
public static final int MESSAGE_REGISTER = 1;
// Sent when the first page load completes
public static final int MESSAGE_INIT_COMPLETE = 2;
// Sent when GeckoRuntime exits
public static final int MESSAGE_QUIT = 3;
// Reload current session
public static final int MESSAGE_RELOAD = 4;
// Load URI in current session
public static final int MESSAGE_LOAD_URI = 5;
// Receive a reply for a message
public static final int MESSAGE_REPLY = 6;
// Execute action on the remote service
public static final int MESSAGE_PAGE_STOP = 7;
// Used by clients to know the first safe ID that can be used
// for additional message types
public static final int FIRST_SAFE_MESSAGE = MESSAGE_PAGE_STOP + 1;
// Generic service instances
public static final class instance0 extends TestRuntimeService {}
public static final class instance1 extends TestRuntimeService {}
protected GeckoRuntime mRuntime;
protected GeckoSession mSession;
protected GeckoBundle mTestData;
private Messenger mClient;
private class TestHandler extends Handler {
@Override
public void handleMessage(@NonNull final Message msg) {
final Bundle msgData = msg.getData();
final GeckoBundle data = msgData != null
? GeckoBundle.fromBundle(msgData.getBundle("data"))
: null;
final String id = msgData != null ? msgData.getString("id") : null;
switch (msg.what) {
case MESSAGE_REGISTER:
mClient = msg.replyTo;
return;
case MESSAGE_QUIT:
// Unceremoniously exit
System.exit(0);
return;
case MESSAGE_RELOAD:
mSession.reload();
break;
case MESSAGE_LOAD_URI:
mSession.loadUri(data.getString("uri"));
break;
default: {
final GeckoResult<GeckoBundle> result =
TestRuntimeService.this.handleMessage(msg.what, data);
if (result != null) {
result.accept(bundle -> {
final GeckoBundle reply = new GeckoBundle();
reply.putString("id", id);
reply.putBundle("data", bundle);
TestRuntimeService.this.sendMessage(MESSAGE_REPLY, reply);
});
}
return;
}
}
}
}
final Messenger mMessenger = new Messenger(new TestHandler());
@Override
public void onShutdown() {
sendMessage(MESSAGE_QUIT);
}
protected void sendMessage(final int message) {
sendMessage(message, null);
}
protected void sendMessage(final int message, final GeckoBundle bundle) {
if (mClient == null) {
throw new IllegalStateException("Service is not connected yet!");
}
Message msg = Message.obtain(null, message);
msg.replyTo = mMessenger;
if (bundle != null) {
msg.setData(bundle.toBundle());
}
try {
mClient.send(msg);
} catch (RemoteException ex) {
throw new RuntimeException(ex);
}
}
private boolean mFirstPageStop = true;
@Override
public void onPageStop(@NonNull final GeckoSession session, final boolean success) {
// Notify the subclass that the session is ready to use
if (success && mFirstPageStop) {
onSessionReady(session);
mFirstPageStop = false;
sendMessage(MESSAGE_INIT_COMPLETE);
} else {
sendMessage(MESSAGE_PAGE_STOP);
}
}
protected void onSessionReady(final GeckoSession session) {}
@Override
public void onDestroy() {
// Sometimes the service doesn't die on it's own so we need to kill it here.
System.exit(0);
}
@Nullable
@Override
public IBinder onBind(final Intent intent) {
// Request to be killed as soon as the client unbinds.
stopSelf();
if (mRuntime != null) {
// We only expect one client
throw new RuntimeException("Multiple clients !?");
}
mRuntime = createRuntime(getApplicationContext(), intent);
mRuntime.setDelegate(this);
if (intent.hasExtra("test-data")) {
mTestData = GeckoBundle.fromBundle(intent.getBundleExtra("test-data"));
}
mSession = createSession(intent);
mSession.setProgressDelegate(this);
mSession.open(mRuntime);
return mMessenger.getBinder();
}
/** Override this to handle custom messages. */
protected GeckoResult<GeckoBundle> handleMessage(final int messageId, final GeckoBundle data) {
return null;
}
/** Override this to change the default runtime */
protected GeckoRuntime createRuntime(final @NonNull Context context,
final @NonNull Intent intent) {
return GeckoRuntime.create(context,
new GeckoRuntimeSettings.Builder().extras(intent.getExtras()).build());
}
/** Override this to change the default session */
protected GeckoSession createSession(final Intent intent) {
return new GeckoSession();
}
/**
* Starts GeckoRuntime in the process given in input, and waits for the MESSAGE_INIT_COMPLETE
* event that's fired when the first GeckoSession receives the onPageStop event.
*
* We wait for a page load to make sure that everything started up correctly (as opposed
* to quitting during the startup procedure).
*/
public static class RuntimeInstance<T> {
public boolean isConnected = false;
public GeckoResult<Void> disconnected = new GeckoResult<>();
public GeckoResult<Void> started = new GeckoResult<>();
public GeckoResult<Void> quitted = new GeckoResult<>();
public final Context context;
public final Class<T> service;
private final File mProfileFolder;
private final GeckoBundle mTestData;
private final ClientHandler mClientHandler = new ClientHandler();
private Messenger mMessenger;
private Messenger mServiceMessenger;
private GeckoResult<Void> mPageStop = null;
private Map<String, GeckoResult<GeckoBundle>> mPendingMessages = new HashMap<>();
protected RuntimeInstance(
final Context context,
final Class<T> service,
final File profileFolder) {
this(context, service, profileFolder, null);
}
protected RuntimeInstance(
final Context context,
final Class<T> service,
final File profileFolder,
final GeckoBundle testData) {
this.context = context;
this.service = service;
mProfileFolder = profileFolder;
mTestData = testData;
}
public static <T> RuntimeInstance<T> start(
final Context context,
final Class<T> service,
final File profileFolder) {
RuntimeInstance<T> instance = new RuntimeInstance<>(context, service, profileFolder);
instance.sendIntent();
return instance;
}
class ClientHandler extends Handler implements ServiceConnection {
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what) {
case MESSAGE_INIT_COMPLETE:
started.complete(null);
break;
case MESSAGE_QUIT:
quitted.complete(null);
// No reason to keep the service around anymore
context.unbindService(mClientHandler);
break;
case MESSAGE_REPLY:
final String messageId = msg.getData().getString("id");
final Bundle data = msg.getData().getBundle("data");
mPendingMessages.remove(messageId).complete(GeckoBundle.fromBundle(data));
break;
case MESSAGE_PAGE_STOP:
if (mPageStop != null) {
mPageStop.complete(null);
mPageStop = null;
}
break;
default:
RuntimeInstance.this.handleMessage(msg);
break;
}
}
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
mMessenger = new Messenger(mClientHandler);
mServiceMessenger = new Messenger(binder);
isConnected = true;
RuntimeInstance.this.sendMessage(MESSAGE_REGISTER);
}
@Override
public void onServiceDisconnected(ComponentName name) {
isConnected = false;
context.unbindService(this);
disconnected.complete(null);
}
}
/** Override this to handle additional messages. */
protected void handleMessage(Message msg) {}
/** Override to modify the intent sent to the service */
protected Intent createIntent(final Context context) {
return new Intent(context, service);
}
private GeckoResult<GeckoBundle> sendMessageInternal(
final int message,
final GeckoBundle bundle,
final GeckoResult<GeckoBundle> result) {
if (!isConnected) {
throw new IllegalStateException("Service is not connected yet!");
}
final String messageId = UUID.randomUUID().toString();
GeckoBundle data = new GeckoBundle();
data.putString("id", messageId);
if (bundle != null) {
data.putBundle("data", bundle);
}
Message msg = Message.obtain(null, message);
msg.replyTo = mMessenger;
msg.setData(data.toBundle());
if (result != null) {
mPendingMessages.put(messageId, result);
}
try {
mServiceMessenger.send(msg);
} catch (RemoteException ex) {
throw new RuntimeException(ex);
}
return result;
}
private GeckoResult<Void> waitForPageStop() {
if (mPageStop == null) {
mPageStop = new GeckoResult<>();
}
return mPageStop;
}
protected GeckoResult<GeckoBundle> query(final int message) {
return query(message, null);
}
protected GeckoResult<GeckoBundle> query(final int message, final GeckoBundle bundle) {
final GeckoResult<GeckoBundle> result = new GeckoResult<>();
return sendMessageInternal(message, bundle, result);
}
protected void sendMessage(final int message) {
sendMessage(message, null);
}
protected void sendMessage(final int message,
final GeckoBundle bundle) {
sendMessageInternal(message, bundle, null);
}
protected void sendIntent() {
final Intent intent = createIntent(context);
intent.putExtra("args", "-profile " + mProfileFolder.getAbsolutePath());
if (mTestData != null) {
intent.putExtra("test-data", mTestData.toBundle());
}
context.bindService(intent, mClientHandler, Context.BIND_AUTO_CREATE);
}
/**
* Quits the current runtime.
*
* @return a {@link GeckoResult} that is resolved when the service fully disconnects.
*/
public GeckoResult<Void> quit() {
sendMessage(MESSAGE_QUIT);
return disconnected;
}
/**
* Reloads the current session.
*
* @return A {@link GeckoResult} that is resolved when the page is fully reloaded.
*/
public GeckoResult<Void> reload() {
sendMessage(MESSAGE_RELOAD);
return waitForPageStop();
}
/**
* Load a test path in the current session.
*
* @return A {@link GeckoResult} that is resolved when the page is fully loaded.
*/
public GeckoResult<Void> loadTestPath(final String path) {
return loadUri(GeckoSessionTestRule.TEST_ENDPOINT + path);
}
/**
* Load an arbitrary URI in the current session.
*
* @return A {@link GeckoResult} that is resolved when the page is fully loaded.
*/
public GeckoResult<Void> loadUri(final String uri) {
return started.then(unused -> {
final GeckoBundle data = new GeckoBundle(1);
data.putString("uri", uri);
sendMessage(MESSAGE_LOAD_URI, data);
return waitForPageStop();
});
}
}
}

Просмотреть файл

@ -1,65 +1,42 @@
package org.mozilla.geckoview.test.crash
import android.content.Intent
import android.os.Message
import android.os.Messenger
import androidx.test.annotation.UiThreadTest
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.filters.MediumTest
import androidx.test.rule.ServiceTestRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.hamcrest.Matchers.equalTo
import org.hamcrest.Matchers.notNullValue
import org.junit.Assert.assertThat
import org.junit.Assert.assertTrue
import org.junit.Assume.assumeThat
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import org.junit.runner.RunWith
import org.mozilla.geckoview.test.BaseSessionTest
import org.mozilla.geckoview.test.TestCrashHandler
import org.mozilla.geckoview.test.util.Environment
import org.mozilla.geckoview.test.TestRuntimeService
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.ClosedSessionAtStart
@RunWith(AndroidJUnit4::class)
@MediumTest
class ParentCrashTest {
lateinit var messenger: Messenger
val env = Environment()
class ParentCrashTest : BaseSessionTest() {
private val targetContext
get() = InstrumentationRegistry.getInstrumentation().targetContext
@get:Rule val rule = ServiceTestRule()
@get:Rule
var folder = TemporaryFolder()
@Before
fun setup() {
val context = InstrumentationRegistry.getInstrumentation().targetContext
val intent = Intent(context, RemoteGeckoService::class.java)
// We need to run in a different profile so we don't conflict with other tests running
// in parallel in other processes.
val profileFolder = folder.newFolder("remote-gecko-test")
intent.putExtra("args", "-profile ${profileFolder.absolutePath}")
val binder = rule.bindService(intent)
messenger = Messenger(binder)
assertThat("messenger should not be null", binder, notNullValue())
}
private val timeout
get() = sessionRule.env.defaultTimeoutMillis
@Test
@UiThreadTest
@ClosedSessionAtStart
fun crashParent() {
// TODO: Bug 1673956
assumeThat(env.isFission, equalTo(false))
val client = TestCrashHandler.Client(InstrumentationRegistry.getInstrumentation().targetContext)
assumeThat(sessionRule.env.isFission, equalTo(false))
val client = TestCrashHandler.Client(targetContext)
assertTrue(client.connect(env.defaultTimeoutMillis))
assertTrue(client.connect(timeout))
client.setEvalNextCrashDump(/* expectFatal */ true)
messenger.send(Message.obtain(null, RemoteGeckoService.CMD_CRASH_PARENT_NATIVE))
val runtime = TestRuntimeService.RuntimeInstance.start(
targetContext, RuntimeCrashTestService::class.java, temporaryProfile.get())
runtime.loadUri("about:crashparent")
var evalResult = client.getEvalResult(env.defaultTimeoutMillis)
val evalResult = client.getEvalResult(timeout)
assertTrue(evalResult.mMsg, evalResult.mResult)
client.disconnect()

Просмотреть файл

@ -1,57 +0,0 @@
package org.mozilla.geckoview.test.crash
import android.app.Service
import android.content.Intent
import android.os.*
import org.mozilla.geckoview.GeckoRuntime
import org.mozilla.geckoview.GeckoRuntimeSettings
import org.mozilla.geckoview.GeckoSession
import org.mozilla.geckoview.GeckoSessionSettings
import org.mozilla.geckoview.test.TestCrashHandler
class RemoteGeckoService : Service() {
companion object {
val CMD_CRASH_PARENT_NATIVE = 1
val CMD_CRASH_CONTENT_NATIVE = 2
var runtime: GeckoRuntime? = null
}
var session: GeckoSession? = null;
class TestHandler: Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
when (msg.what) {
CMD_CRASH_PARENT_NATIVE -> {
val settings = GeckoSessionSettings()
val session = GeckoSession(settings)
session.open(runtime!!)
session.loadUri("about:crashparent")
}
CMD_CRASH_CONTENT_NATIVE -> {
val settings = GeckoSessionSettings.Builder()
.build()
val session = GeckoSession(settings)
session.open(runtime!!)
session.loadUri("about:crashcontent")
}
else -> {
throw RuntimeException("Unhandled command")
}
}
}
}
val handler = Messenger(TestHandler())
override fun onBind(intent: Intent): IBinder {
if (runtime == null) {
runtime = GeckoRuntime.create(this.applicationContext,
GeckoRuntimeSettings.Builder()
.extras(intent.extras!!)
.crashHandler(TestCrashHandler::class.java).build())
}
return handler.binder
}
}

Просмотреть файл

@ -0,0 +1,18 @@
package org.mozilla.geckoview.test.crash
import android.content.Context
import android.content.Intent
import org.mozilla.geckoview.GeckoRuntime
import org.mozilla.geckoview.GeckoRuntimeSettings
import org.mozilla.geckoview.test.TestCrashHandler
import org.mozilla.geckoview.test.TestRuntimeService
import java.io.File
class RuntimeCrashTestService : TestRuntimeService() {
override fun createRuntime(context: Context, intent: Intent): GeckoRuntime {
return GeckoRuntime.create(this.applicationContext,
GeckoRuntimeSettings.Builder()
.extras(intent.extras!!)
.crashHandler(TestCrashHandler::class.java).build())
}
}

Просмотреть файл

@ -104,7 +104,8 @@ public class GeckoSessionTestRule implements TestRule {
private static final String LOGTAG = "GeckoSessionTestRule";
private static final int TEST_PORT = 4245;
public static final String TEST_ENDPOINT = "http://localhost:" + TEST_PORT;
public static final String TEST_HOST = "localhost";
public static final String TEST_ENDPOINT = "http://" + TEST_HOST + ":" + TEST_PORT;
private static final Method sOnPageStart;
private static final Method sOnPageStop;