Bug 1567341 - Add XpcshellTestRunnerService. r=owlish

This service, based off of TestRunnerActivity, can be used to run an
xpcshell-test instance.

It supports up to 10 concurrent instances, although we can add more if we need
to.

Local testing indicates that with more than 4 concurrent instances there's not
gain in test running performance.

This also adds a new intent action
|org.mozilla.geckoview.test.XPCSHELL_TEST_MAIN| that can be used by the test
harness to start the main application and gain foreground priority without
starting Gecko (which would interfere with the xpcshell runner services).

Differential Revision: https://phabricator.services.mozilla.com/D106212
This commit is contained in:
Agi Sferro 2021-03-24 21:49:38 +00:00
parent 34624a6abf
commit c056964bc7
3 changed files with 148 additions and 1 deletions

View File

@ -23,6 +23,7 @@
<activity-alias android:name=".App" android:targetActivity=".TestRunnerActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<action android:name="org.mozilla.geckoview.test.XPCSHELL_TEST"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
@ -47,6 +48,18 @@
<!-- 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" />
<!-- This is used to run xpcshell tests -->
<service android:name=".XpcshellTestRunnerService$i0" android:enabled="true" android:exported="true" android:process=":xpcshell0"/>
<service android:name=".XpcshellTestRunnerService$i1" android:enabled="true" android:exported="true" android:process=":xpcshell1"/>
<service android:name=".XpcshellTestRunnerService$i2" android:enabled="true" android:exported="true" android:process=":xpcshell2"/>
<service android:name=".XpcshellTestRunnerService$i3" android:enabled="true" android:exported="true" android:process=":xpcshell3"/>
<service android:name=".XpcshellTestRunnerService$i4" android:enabled="true" android:exported="true" android:process=":xpcshell4"/>
<service android:name=".XpcshellTestRunnerService$i5" android:enabled="true" android:exported="true" android:process=":xpcshell5"/>
<service android:name=".XpcshellTestRunnerService$i6" android:enabled="true" android:exported="true" android:process=":xpcshell6"/>
<service android:name=".XpcshellTestRunnerService$i7" android:enabled="true" android:exported="true" android:process=":xpcshell7"/>
<service android:name=".XpcshellTestRunnerService$i8" android:enabled="true" android:exported="true" android:process=":xpcshell8"/>
<service android:name=".XpcshellTestRunnerService$i9" android:enabled="true" android:exported="true" android:process=":xpcshell9"/>
</application>
</manifest>

View File

@ -340,6 +340,11 @@ public class TestRunnerActivity extends Activity {
final Intent intent = getIntent();
if ("org.mozilla.geckoview.test.XPCSHELL_TEST_MAIN".equals(intent.getAction())) {
// This activity is just a stub to run xpcshell tests in a service
return;
}
if (sRuntime == null) {
final GeckoRuntimeSettings.Builder runtimeSettingsBuilder =
new GeckoRuntimeSettings.Builder();
@ -498,6 +503,9 @@ public class TestRunnerActivity extends Activity {
}
}
// Random start timestamp for the BrowsingDataDelegate API.
private static final int CLEAR_DATA_START_TIMESTAMP = 1234;
private void refreshExtensionList() {
webExtensionController().list().accept(extensions -> {
mExtensions.clear();
@ -516,7 +524,8 @@ public class TestRunnerActivity extends Activity {
Type.HISTORY |
Type.FORM_DATA |
Type.DOWNLOADS;
return GeckoResult.fromValue(new Settings(1234, types, types ));
return GeckoResult.fromValue(new Settings(
CLEAR_DATA_START_TIMESTAMP, types, types));
}
});

View File

@ -0,0 +1,125 @@
package org.mozilla.geckoview.test;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Process;
import android.util.Log;
import androidx.annotation.Nullable;
import org.mozilla.geckoview.ContentBlocking;
import org.mozilla.geckoview.GeckoResult;
import org.mozilla.geckoview.GeckoRuntime;
import org.mozilla.geckoview.GeckoRuntimeSettings;
import org.mozilla.geckoview.WebExtension;
import org.mozilla.geckoview.WebExtensionController;
import java.util.HashMap;
public class XpcshellTestRunnerService extends Service {
public static final class i0 extends XpcshellTestRunnerService {}
public static final class i1 extends XpcshellTestRunnerService {}
public static final class i2 extends XpcshellTestRunnerService {}
public static final class i3 extends XpcshellTestRunnerService {}
public static final class i4 extends XpcshellTestRunnerService {}
public static final class i5 extends XpcshellTestRunnerService {}
public static final class i6 extends XpcshellTestRunnerService {}
public static final class i7 extends XpcshellTestRunnerService {}
public static final class i8 extends XpcshellTestRunnerService {}
public static final class i9 extends XpcshellTestRunnerService {}
private static final String LOGTAG = "XpcshellTestRunner";
static GeckoRuntime sRuntime;
private HashMap<String, WebExtension> mExtensions = new HashMap<>();
private static WebExtensionController webExtensionController() {
return sRuntime.getWebExtensionController();
}
@Override
public synchronized int onStartCommand(Intent intent, int flags, int startId) {
if (sRuntime != null) {
// We don't support restarting GeckoRuntime
throw new RuntimeException("Cannot start more than once");
}
final Bundle extras = intent.getExtras();
for (final String key : extras.keySet()) {
Log.i(LOGTAG, "Got extras " + key + "=" + extras.get(key));
}
final ContentBlocking.SafeBrowsingProvider googleLegacy = ContentBlocking.SafeBrowsingProvider
.from(ContentBlocking.GOOGLE_LEGACY_SAFE_BROWSING_PROVIDER)
.getHashUrl("http://mochi.test:8888/safebrowsing-dummy/gethash")
.updateUrl("http://mochi.test:8888/safebrowsing-dummy/update")
.build();
final ContentBlocking.SafeBrowsingProvider google = ContentBlocking.SafeBrowsingProvider
.from(ContentBlocking.GOOGLE_SAFE_BROWSING_PROVIDER)
.getHashUrl("http://mochi.test:8888/safebrowsing4-dummy/gethash")
.updateUrl("http://mochi.test:8888/safebrowsing4-dummy/update")
.build();
final GeckoRuntimeSettings runtimeSettings =
new GeckoRuntimeSettings.Builder()
.arguments(new String[] { "-xpcshell" })
.extras(extras)
.consoleOutput(true)
.contentBlocking(new ContentBlocking.Settings.Builder()
.safeBrowsingProviders(google, googleLegacy)
.build())
.crashHandler(TestCrashHandler.class)
.build();
sRuntime = GeckoRuntime.create(this, runtimeSettings);
webExtensionController().setDebuggerDelegate(new WebExtensionController.DebuggerDelegate() {
@Override
public void onExtensionListUpdated() {
refreshExtensionList();
}
});
sRuntime.setDelegate(() -> {
stopSelf();
System.exit(0);
});
return Service.START_NOT_STICKY;
}
// Random start timestamp for the BrowsingDataDelegate API.
private static final int CLEAR_DATA_START_TIMESTAMP = 1234;
private void refreshExtensionList() {
webExtensionController().list().accept(extensions -> {
mExtensions.clear();
for (final WebExtension extension : extensions) {
mExtensions.put(extension.id, extension);
extension.setBrowsingDataDelegate(new WebExtension.BrowsingDataDelegate() {
@Nullable
@Override
public GeckoResult<Settings> onGetSettings() {
final long types =
Type.CACHE |
Type.COOKIES |
Type.HISTORY |
Type.FORM_DATA |
Type.DOWNLOADS;
return GeckoResult.fromValue(
new Settings(CLEAR_DATA_START_TIMESTAMP, types, types));
}
});
}
});
}
@Override
public synchronized IBinder onBind(Intent intent) {
return null;
}
}