mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 06:11:37 +00:00
Bug 1169476 -- Implement |mach robocop --serve|. r=gbrown
This adds a flag to |mach robocop| that does everything to run a Robocop test except launch the actual test. Instead of launching the test, it starts the mochi.test server and launches Fennec with a test profile; then it sits and waits forever. This allows regular Java IDEs (IntelliJ, but previously Eclipse) to run Robocop tests like regular instrumentation tests, "injecting" them into the prepared testing environment. It's quite nice! --HG-- extra : rebase_source : a5ab08222110a20291aebe70ef1fda0d340dbe7d extra : source : e91ac9a35f86928fcd519911476ee7d68d06f921
This commit is contained in:
parent
30b2eea9fd
commit
97786c5695
@ -24,6 +24,10 @@ fennecLogcatFilters = [ "The character encoding of the HTML document was not dec
|
||||
class RemoteAutomation(Automation):
|
||||
_devicemanager = None
|
||||
|
||||
# Part of a hack for Robocop: "am COMMAND" is handled specially if COMMAND
|
||||
# is in this set. See usages below.
|
||||
_specialAmCommands = ('instrument', 'start')
|
||||
|
||||
def __init__(self, deviceManager, appName = '', remoteLog = None,
|
||||
processArgs=None):
|
||||
self._devicemanager = deviceManager
|
||||
@ -237,7 +241,7 @@ class RemoteAutomation(Automation):
|
||||
|
||||
# Hack for robocop, if app & testURL == None and extraArgs contains the rest of the stuff, lets
|
||||
# assume extraArgs is all we need
|
||||
if app == "am" and extraArgs[0] == "instrument":
|
||||
if app == "am" and extraArgs[0] in RemoteAutomation._specialAmCommands:
|
||||
return app, extraArgs
|
||||
|
||||
cmd, args = Automation.buildCommandLine(self, app, debuggerInfo, profileDir, testURL, extraArgs)
|
||||
@ -275,7 +279,7 @@ class RemoteAutomation(Automation):
|
||||
else:
|
||||
raise Exception("unable to launch process")
|
||||
self.procName = cmd[0].split('/')[-1]
|
||||
if cmd[0] == 'am' and cmd[1] == "instrument":
|
||||
if cmd[0] == 'am' and cmd[1] in RemoteAutomation._specialAmCommands:
|
||||
self.procName = app
|
||||
print "Robocop process name: "+self.procName
|
||||
|
||||
|
@ -8,7 +8,11 @@
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0" >
|
||||
|
||||
<uses-sdk android:minSdkVersion="8" />
|
||||
<uses-sdk android:minSdkVersion="@MOZ_ANDROID_MIN_SDK_VERSION@"
|
||||
#ifdef MOZ_ANDROID_MAX_SDK_VERSION
|
||||
android:maxSdkVersion="@MOZ_ANDROID_MAX_SDK_VERSION@"
|
||||
#endif
|
||||
android:targetSdkVersion="@ANDROID_TARGET_SDK@"/>
|
||||
|
||||
<instrumentation
|
||||
android:name="org.mozilla.gecko.FennecInstrumentationTestRunner"
|
||||
@ -43,6 +47,14 @@
|
||||
|
||||
</activity>
|
||||
|
||||
<activity android:name="org.mozilla.gecko.LaunchFennecWithConfigurationActivity"
|
||||
android:label="Robocop Fennec">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
@ -0,0 +1,40 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.mozilla.gecko.tests.BaseRobocopTest;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
/**
|
||||
* An Activity that extracts Robocop settings from robotium.config, launches
|
||||
* Fennec with the Robocop testing parameters, and finishes itself.
|
||||
* <p>
|
||||
* This is intended to be used by local testers using |mach robocop --serve|.
|
||||
*/
|
||||
public class LaunchFennecWithConfigurationActivity extends Activity {
|
||||
@Override
|
||||
public void onCreate(Bundle arguments) {
|
||||
super.onCreate(arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
final String configFile = FennecNativeDriver.getFile(BaseRobocopTest.DEFAULT_ROOT_PATH + "/robotium.config");
|
||||
final Map<String, String> config = FennecNativeDriver.convertTextToTable(configFile);
|
||||
final Intent intent = BaseRobocopTest.createActivityIntent(config);
|
||||
|
||||
intent.setClassName(AppConstants.ANDROID_PACKAGE_NAME, AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS);
|
||||
|
||||
this.finish();
|
||||
this.startActivity(intent);
|
||||
}
|
||||
}
|
@ -24,6 +24,7 @@ _JAVA_HARNESS := \
|
||||
FennecTalosAssert.java \
|
||||
FennecNativeDriver.java \
|
||||
FennecNativeElement.java \
|
||||
LaunchFennecWithConfigurationActivity.java \
|
||||
RoboCopException.java \
|
||||
RobocopShare1.java \
|
||||
RobocopShare2.java \
|
||||
|
@ -4,7 +4,15 @@
|
||||
|
||||
package org.mozilla.gecko.tests;
|
||||
|
||||
import java.util.Map;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.PowerManager;
|
||||
import android.test.ActivityInstrumentationTestCase2;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.jayway.android.robotium.solo.Solo;
|
||||
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.HttpClient;
|
||||
@ -13,32 +21,32 @@ import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.mozilla.gecko.Actions;
|
||||
import org.mozilla.gecko.AppConstants;
|
||||
import org.mozilla.gecko.Assert;
|
||||
import org.mozilla.gecko.BrowserApp;
|
||||
import org.mozilla.gecko.Driver;
|
||||
import org.mozilla.gecko.FennecInstrumentationTestRunner;
|
||||
import org.mozilla.gecko.FennecMochitestAssert;
|
||||
import org.mozilla.gecko.FennecNativeActions;
|
||||
import org.mozilla.gecko.FennecNativeDriver;
|
||||
import org.mozilla.gecko.FennecTalosAssert;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.GeckoEvent;
|
||||
import org.mozilla.gecko.updater.UpdateServiceHelper;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.os.PowerManager;
|
||||
import android.test.ActivityInstrumentationTestCase2;
|
||||
import android.util.Log;
|
||||
|
||||
import com.jayway.android.robotium.solo.Solo;
|
||||
import java.util.Map;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public abstract class BaseRobocopTest extends ActivityInstrumentationTestCase2<Activity> {
|
||||
public static final String LOGTAG = "BaseTest";
|
||||
|
||||
public enum Type {
|
||||
MOCHITEST,
|
||||
TALOS
|
||||
}
|
||||
|
||||
private static final String DEFAULT_ROOT_PATH = "/mnt/sdcard/tests";
|
||||
public static final String DEFAULT_ROOT_PATH = "/mnt/sdcard/tests";
|
||||
|
||||
// How long to wait for a Robocop:Quit message to actually kill Fennec.
|
||||
private static final int ROBOCOP_QUIT_WAIT_MS = 180000;
|
||||
|
||||
/**
|
||||
* The Java Class instance that launches the browser.
|
||||
@ -76,8 +84,6 @@ public abstract class BaseRobocopTest extends ActivityInstrumentationTestCase2<A
|
||||
|
||||
protected StringHelper mStringHelper;
|
||||
|
||||
protected abstract Intent createActivityIntent();
|
||||
|
||||
/**
|
||||
* The browser is started at the beginning of this test. A single test is a
|
||||
* class inheriting from <code>BaseRobocopTest</code> that contains test
|
||||
@ -112,6 +118,30 @@ public abstract class BaseRobocopTest extends ActivityInstrumentationTestCase2<A
|
||||
return Type.MOCHITEST;
|
||||
}
|
||||
|
||||
// Member function to allow specialization.
|
||||
protected Intent createActivityIntent() {
|
||||
return BaseRobocopTest.createActivityIntent(mConfig);
|
||||
}
|
||||
|
||||
// Static function to allow re-use.
|
||||
public static Intent createActivityIntent(Map<String, String> config) {
|
||||
final Intent intent = new Intent(Intent.ACTION_MAIN);
|
||||
intent.putExtra("args", "-no-remote -profile " + config.get("profile"));
|
||||
// Don't show the first run experience.
|
||||
intent.putExtra(BrowserApp.EXTRA_SKIP_STARTPANE, true);
|
||||
|
||||
final String envString = config.get("envvars");
|
||||
if (!TextUtils.isEmpty(envString)) {
|
||||
final String[] envStrings = envString.split(",");
|
||||
|
||||
for (int iter = 0; iter < envStrings.length; iter++) {
|
||||
intent.putExtra("env" + iter, envStrings[iter]);
|
||||
}
|
||||
}
|
||||
|
||||
return intent;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
// Disable the updater.
|
||||
@ -152,7 +182,43 @@ public abstract class BaseRobocopTest extends ActivityInstrumentationTestCase2<A
|
||||
mSolo = new Solo(getInstrumentation(), tempActivity);
|
||||
mDriver = new FennecNativeDriver(tempActivity, mSolo, mRootPath);
|
||||
mActions = new FennecNativeActions(tempActivity, mSolo, getInstrumentation(), mAsserter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tearDown() throws Exception {
|
||||
try {
|
||||
mAsserter.endTest();
|
||||
|
||||
// By default, we don't quit Fennec on finish, and we don't finish
|
||||
// all opened activities. Not quiting Fennec entirely is intended to
|
||||
// make life better for local testers, who might want to alter a
|
||||
// test that is under development rather than Fennec itself. Not
|
||||
// finishing activities is intended to allow local testers to
|
||||
// manually inspect an activity's state after a test
|
||||
// run. runtestsremote.py sets this to "1". Testers running via an
|
||||
// IDE will not have this set at all.
|
||||
final String quitAndFinish = FennecInstrumentationTestRunner.getFennecArguments()
|
||||
.getString("quit_and_finish"); // null means not specified.
|
||||
if ("1".equals(quitAndFinish)) {
|
||||
// Request the browser force quit and wait for it to take effect.
|
||||
Log.i(LOGTAG, "Requesting force quit.");
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Robocop:Quit", null));
|
||||
mSolo.sleep(ROBOCOP_QUIT_WAIT_MS);
|
||||
|
||||
// If still running, finish activities as recommended by Robotium.
|
||||
Log.i(LOGTAG, "Finishing all opened activities.");
|
||||
mSolo.finishOpenedActivities();
|
||||
} else {
|
||||
// This has the effect of keeping the activity-under-test
|
||||
// around; if we don't set it to null, it is killed, either by
|
||||
// finishOpenedActivities above or super.tearDown below.
|
||||
Log.i(LOGTAG, "Not requesting force quit and trying to keep started activity alive.");
|
||||
setActivity(null);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -144,38 +144,6 @@ abstract class BaseTest extends BaseRobocopTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tearDown() throws Exception {
|
||||
try {
|
||||
mAsserter.endTest();
|
||||
// request a force quit of the browser and wait for it to take effect
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Robocop:Quit", null));
|
||||
mSolo.sleep(120000);
|
||||
// if still running, finish activities as recommended by Robotium
|
||||
mSolo.finishOpenedActivities();
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Intent createActivityIntent() {
|
||||
final Intent intent = new Intent(Intent.ACTION_MAIN);
|
||||
intent.putExtra("args", "-no-remote -profile " + mProfile);
|
||||
|
||||
final String envString = mConfig.get("envvars");
|
||||
if (!TextUtils.isEmpty(envString)) {
|
||||
final String[] envStrings = envString.split(",");
|
||||
|
||||
for (int iter = 0; iter < envStrings.length; iter++) {
|
||||
intent.putExtra("env" + iter, envStrings[iter]);
|
||||
}
|
||||
}
|
||||
|
||||
return intent;
|
||||
}
|
||||
|
||||
public void assertMatches(String value, String regex, String name) {
|
||||
if (value == null) {
|
||||
mAsserter.ok(false, name, "Expected /" + regex + "/, got null");
|
||||
|
@ -58,22 +58,6 @@ abstract class UITest extends BaseRobocopTest
|
||||
throwIfScreenNotOn();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tearDown() throws Exception {
|
||||
try {
|
||||
mAsserter.endTest();
|
||||
// request a force quit of the browser and wait for it to take effect
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Robocop:Quit", null));
|
||||
mSolo.sleep(120000);
|
||||
// if still running, finish activities as recommended by Robotium
|
||||
mSolo.finishOpenedActivities();
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void runTest() throws Throwable {
|
||||
try {
|
||||
@ -182,26 +166,6 @@ abstract class UITest extends BaseRobocopTest
|
||||
return baseUrl + "/" + url.replaceAll("(^/)", "");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Intent createActivityIntent() {
|
||||
final Intent intent = new Intent(Intent.ACTION_MAIN);
|
||||
|
||||
// Don't show the first run experience.
|
||||
intent.putExtra(BrowserApp.EXTRA_SKIP_STARTPANE, true);
|
||||
intent.putExtra("args", "-no-remote -profile " + mProfile);
|
||||
|
||||
final String envString = mConfig.get("envvars");
|
||||
if (!TextUtils.isEmpty(envString)) {
|
||||
final String[] envStrings = envString.split(",");
|
||||
|
||||
for (int iter = 0; iter < envStrings.length; iter++) {
|
||||
intent.putExtra("env" + iter, envStrings[iter]);
|
||||
}
|
||||
}
|
||||
|
||||
return intent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an Exception. Called from overridden JUnit methods to ensure JUnit assertions
|
||||
* are not accidentally used over AssertionHelper assertions (the latter of which contains
|
||||
|
@ -580,7 +580,13 @@ class RobocopCommands(MachCommandBase):
|
||||
help='Test to run. Can be a single Robocop test file (like "testLoad.java") '
|
||||
' or a directory of tests '
|
||||
'(to run recursively). If omitted, the entire Robocop suite is run.')
|
||||
def run_robocop(self, test_paths, **kwargs):
|
||||
@CommandArgument('--serve', default=False, action='store_true',
|
||||
help='Run no tests but start the mochi.test web server and launch '
|
||||
'Fennec with a test profile.')
|
||||
def run_robocop(self, test_paths, serve=False, **kwargs):
|
||||
if serve:
|
||||
kwargs['autorun'] = False
|
||||
|
||||
if not kwargs.get('robocopIni'):
|
||||
kwargs['robocopIni'] = os.path.join(self.topobjdir, '_tests', 'testing',
|
||||
'mochitest', 'robocop.ini')
|
||||
|
@ -543,6 +543,11 @@ def run_test_harness(options):
|
||||
if (options.dm_trans == 'adb' and options.robocopApk):
|
||||
dm._checkCmd(["install", "-r", options.robocopApk])
|
||||
|
||||
if not options.autorun:
|
||||
# Force a single loop iteration. The iteration will start Fennec and
|
||||
# the httpd server, but not actually run a test.
|
||||
options.testPath = robocop_tests[0]['name']
|
||||
|
||||
retVal = None
|
||||
# Filtering tests
|
||||
active_tests = []
|
||||
@ -570,20 +575,36 @@ def run_test_harness(options):
|
||||
mochitest.localProfile = options.profilePath
|
||||
|
||||
options.app = "am"
|
||||
options.browserArgs = [
|
||||
"instrument",
|
||||
"-w",
|
||||
"-e",
|
||||
"deviceroot",
|
||||
deviceRoot,
|
||||
"-e",
|
||||
"class"]
|
||||
options.browserArgs.append(
|
||||
"org.mozilla.gecko.tests.%s" %
|
||||
test['name'].split('.java')[0])
|
||||
options.browserArgs.append(
|
||||
"org.mozilla.roboexample.test/org.mozilla.gecko.FennecInstrumentationTestRunner")
|
||||
mochitest.nsprLogName = "nspr-%s.log" % test['name']
|
||||
if options.autorun:
|
||||
# This launches a test (using "am instrument") and instructs
|
||||
# Fennec to /quit/ the browser (using Robocop:Quit) and to
|
||||
# /finish/ all opened activities.
|
||||
options.browserArgs = [
|
||||
"instrument",
|
||||
"-w",
|
||||
"-e", "quit_and_finish", "1",
|
||||
"-e", "deviceroot", deviceRoot,
|
||||
"-e",
|
||||
"class"]
|
||||
options.browserArgs.append(
|
||||
"org.mozilla.gecko.tests.%s" %
|
||||
test['name'].split('.java')[0])
|
||||
options.browserArgs.append(
|
||||
"org.mozilla.roboexample.test/org.mozilla.gecko.FennecInstrumentationTestRunner")
|
||||
else:
|
||||
# This does not launch a test at all. It launches an activity
|
||||
# that starts Fennec and then waits indefinitely, since cat
|
||||
# never returns.
|
||||
options.browserArgs = ["start",
|
||||
"-n", "org.mozilla.roboexample.test/org.mozilla.gecko.LaunchFennecWithConfigurationActivity",
|
||||
"&&", "cat"]
|
||||
dm.default_timeout = sys.maxint # Forever.
|
||||
|
||||
mochitest.log.info("")
|
||||
mochitest.log.info("Serving mochi.test Robocop root at http://%s:%s/tests/robocop/" %
|
||||
(options.remoteWebServer, options.httpPort))
|
||||
mochitest.log.info("")
|
||||
|
||||
# If the test is for checking the import from bookmarks then make
|
||||
# sure there is data to import
|
||||
|
Loading…
Reference in New Issue
Block a user