Bug 1602668 - Enables xorigin iframe mode for mochitest-plain. r=kmag

Differential Revision: https://phabricator.services.mozilla.com/D70360
This commit is contained in:
tkhan 2020-06-22 19:10:30 +00:00
Родитель f58c7a2553
Коммит b4821a1c8e
7 изменённых файлов: 269 добавлений и 49 удалений

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

@ -66,6 +66,7 @@ http://127.0.0.1:80 privileged
http://127.0.0.1:8888 privileged http://127.0.0.1:8888 privileged
http://test:80 privileged http://test:80 privileged
http://mochi.test:8888 privileged http://mochi.test:8888 privileged
http://mochi.xorigin-test:8888 privileged
http://test1.mochi.test:8888 http://test1.mochi.test:8888
http://sub1.test1.mochi.test:8888 http://sub1.test1.mochi.test:8888
http://sub2.xn--lt-uia.mochi.test:8888 http://sub2.xn--lt-uia.mochi.test:8888

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

@ -34,6 +34,9 @@ function parseTestManifest(testManifest, params, callback) {
}; };
} else { } else {
let name = params.testPrefix + path; let name = params.testPrefix + path;
if (params.xOriginTests && obj.scheme == "https") {
name = params.httpsBaseUrl + path;
}
paths.push({ paths.push({
test: { test: {
url: name, url: name,

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

@ -418,6 +418,12 @@ class MochitestArguments(ArgumentContainer):
"default": False, "default": False,
"help": "Run tests with fission (site isolation) enabled.", "help": "Run tests with fission (site isolation) enabled.",
}], }],
[["--enable-xorigin-tests"],
{"action": "store_true",
"default": False,
"dest": "xOriginTests",
"help": "Run tests in a cross origin iframe.",
}],
[["--store-chrome-manifest"], [["--store-chrome-manifest"],
{"action": "store", {"action": "store",
"help": "Destination path to write a copy of any chrome manifest " "help": "Destination path to write a copy of any chrome manifest "

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

@ -199,7 +199,7 @@ class MessageLogger(object):
def _fix_test_name(self, message): def _fix_test_name(self, message):
"""Normalize a logged test path to match the relative path from the sourcedir. """Normalize a logged test path to match the relative path from the sourcedir.
""" """
if 'test' in message: if message.get('test') is not None:
test = message['test'] test = message['test']
for prefix in MessageLogger.TEST_PATH_PREFIXES: for prefix in MessageLogger.TEST_PATH_PREFIXES:
if test.startswith(prefix): if test.startswith(prefix):
@ -307,7 +307,6 @@ class MessageLogger(object):
if message['action'] == 'test_start': if message['action'] == 'test_start':
self.is_test_running = True self.is_test_running = True
if self.restore_buffering: if self.restore_buffering:
self.restore_buffering = False self.restore_buffering = False
self.buffering = True self.buffering = True
@ -1086,6 +1085,11 @@ class MochitestDesktop(object):
self.urlOpts.append("jscovDirPrefix=%s" % options.jscov_dir_prefix) self.urlOpts.append("jscovDirPrefix=%s" % options.jscov_dir_prefix)
if options.cleanupCrashes: if options.cleanupCrashes:
self.urlOpts.append("cleanupCrashes=true") self.urlOpts.append("cleanupCrashes=true")
if "MOZ_XORIGIN_MOCHITEST" in env and \
env["MOZ_XORIGIN_MOCHITEST"] == "1":
options.xOriginTests = True
if options.xOriginTests:
self.urlOpts.append("xOriginTests=true")
def normflavor(self, flavor): def normflavor(self, flavor):
""" """
@ -1129,6 +1133,8 @@ class MochitestDesktop(object):
def buildTestURL(self, options, scheme='http'): def buildTestURL(self, options, scheme='http'):
if scheme == 'https': if scheme == 'https':
testHost = "https://example.com:443" testHost = "https://example.com:443"
elif options.xOriginTests:
testHost = "http://mochi.xorigin-test:8888"
else: else:
testHost = "http://mochi.test:8888" testHost = "http://mochi.test:8888"
testURL = "/".join([testHost, self.TEST_PATH]) testURL = "/".join([testHost, self.TEST_PATH])
@ -2647,6 +2653,7 @@ toolbar#nav-bar {
'network.process.enabled', False), 'network.process.enabled', False),
"verify": options.verify, "verify": options.verify,
"webrender": options.enable_webrender, "webrender": options.enable_webrender,
"xorigin": options.xOriginTests,
}) })
self.setTestRoot(options) self.setTestRoot(options)
@ -2918,6 +2925,8 @@ toolbar#nav-bar {
self.log.info("runtests.py | Running with e10s: {}".format(options.e10s)) self.log.info("runtests.py | Running with e10s: {}".format(options.e10s))
self.log.info("runtests.py | Running with fission: {}".format( self.log.info("runtests.py | Running with fission: {}".format(
mozinfo.info.get('fission', False))) mozinfo.info.get('fission', False)))
self.log.info("runtests.py | Running with cross-origin iframes: {}".format(
mozinfo.info.get('xorigin', False)))
self.log.info("runtests.py | Running with serviceworker_e10s: {}".format( self.log.info("runtests.py | Running with serviceworker_e10s: {}".format(
mozinfo.info.get('serviceworker_e10s', False))) mozinfo.info.get('serviceworker_e10s', False)))
self.log.info("runtests.py | Running with socketprocess_e10s: {}".format( self.log.info("runtests.py | Running with socketprocess_e10s: {}".format(

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

@ -25,6 +25,21 @@
var SimpleTest = {}; var SimpleTest = {};
var parentRunner = null; var parentRunner = null;
// Using a try/catch rather than SpecialPowers.Cu.isRemoteProxy() because
// it doesn't cover the case where an iframe is xorigin but fission is
// not enabled.
let isSameOrigin = function(w) {
try {
w.top.TestRunner;
} catch (e) {
if (e instanceof DOMException) {
return false;
}
}
return true;
};
let isXOrigin = !isSameOrigin(window);
// In normal test runs, the window that has a TestRunner in its parent is // In normal test runs, the window that has a TestRunner in its parent is
// the primary window. In single test runs, if there is no parent and there // the primary window. In single test runs, if there is no parent and there
// is no opener then it is the primary window. // is no opener then it is the primary window.
@ -32,7 +47,9 @@ var isSingleTestRun =
parent == window && parent == window &&
!(opener || (window.arguments && window.arguments[0].SimpleTest)); !(opener || (window.arguments && window.arguments[0].SimpleTest));
try { try {
var isPrimaryTestWindow = !!parent.TestRunner || isSingleTestRun; var isPrimaryTestWindow =
(isXOrigin && parent != window && parent == top) ||
(!isXOrigin && (!!parent.TestRunner || isSingleTestRun));
} catch (e) { } catch (e) {
dump( dump(
"TEST-UNEXPECTED-FAIL, Exception caught: " + "TEST-UNEXPECTED-FAIL, Exception caught: " +
@ -46,6 +63,87 @@ try {
"\n" "\n"
); );
} }
let xOriginRunner = {
init(harnessWindow) {
this.harnessWindow = harnessWindow;
let url = new URL(document.URL);
this.testFile = url.pathname;
this.showTestReport = url.searchParams.get("showTestReport") == "true";
this.expected = url.searchParams.get("expected");
},
callHarnessMethod(applyOn, command, ...params) {
this.harnessWindow.postMessage(
{
harnessType: "SimpleTest",
applyOn,
command,
params,
},
"*"
);
},
getParameterInfo() {
let url = new URL(document.URL);
return {
currentTestURL: url.searchParams.get("currentTestURL"),
testRoot: url.searchParams.get("testRoot"),
};
},
addFailedTest(test) {
this.callHarnessMethod("runner", "addFailedTest", test);
},
expectAssertions(min, max) {
this.callHarnessMethod("runner", "expectAssertions", min, max);
},
requestLongerTimeout(factor) {
this.harnessWindow.postMessage(
{
harnessType: "SimpleTest",
command: "requestLongerTimeout",
applyOn: "runner",
params: [factor],
},
"*"
);
},
testFinished(tests) {
this.callHarnessMethod("runner", "testFinished", tests);
},
structuredLogger: {
info(msg) {
xOriginRunner.callHarnessMethod("logger", "structuredLogger.info", msg);
},
error(msg) {
xOriginRunner.callHarnessMethod("logger", "structuredLogger.error", msg);
},
activateBuffering() {
xOriginRunner.callHarnessMethod(
"logger",
"structuredLogger.activateBuffering"
);
},
deactivateBuffering() {
xOriginRunner.callHarnessMethod(
"logger",
"structuredLogger.deactivateBuffering"
);
},
testStatus(url, subtest, status, expected, diagnostic, stack) {
xOriginRunner.callHarnessMethod(
"logger",
"structuredLogger.testStatus",
url,
subtest,
status,
expected,
diagnostic,
stack
);
},
},
};
// Finds the TestRunner for this test run and the SpecialPowers object (in // Finds the TestRunner for this test run and the SpecialPowers object (in
// case it is not defined) from a parent/opener window. // case it is not defined) from a parent/opener window.
// //
@ -57,20 +155,31 @@ try {
function ancestor(w) { function ancestor(w) {
return w.parent != w return w.parent != w
? w.parent ? w.parent
: w.opener || (w.arguments && w.arguments[0]); : w.opener ||
(!isXOrigin &&
w.arguments &&
SpecialPowers.wrap(Window).isInstance(w.arguments[0]) &&
w.arguments[0]);
} }
var w = ancestor(window); var w = ancestor(window);
while (w && (!parentRunner || !window.SpecialPowers)) { while (w && !parentRunner) {
isXOrigin = !isSameOrigin(w);
if (isXOrigin) {
if (w.parent != w) {
w = w.top;
}
xOriginRunner.init(w);
parentRunner = xOriginRunner;
}
if (!parentRunner) { if (!parentRunner) {
parentRunner = w.TestRunner; parentRunner = w.TestRunner;
if (!parentRunner && w.wrappedJSObject) { if (!parentRunner && w.wrappedJSObject) {
parentRunner = w.wrappedJSObject.TestRunner; parentRunner = w.wrappedJSObject.TestRunner;
} }
} }
if (!window.SpecialPowers) {
window.SpecialPowers = w.SpecialPowers;
}
w = ancestor(w); w = ancestor(w);
} }
@ -273,18 +382,19 @@ function recordIfMatchesFailurePattern(name, diag) {
} }
SimpleTest.setExpected = function() { SimpleTest.setExpected = function() {
if (parent.TestRunner) { if (!parentRunner) {
if (!Array.isArray(parent.TestRunner.expected)) { return;
SimpleTest.expected = parent.TestRunner.expected; }
if (!Array.isArray(parentRunner.expected)) {
SimpleTest.expected = parentRunner.expected;
} else { } else {
// Assertions are checked by the runner. // Assertions are checked by the runner.
SimpleTest.expected = parent.TestRunner.expected.filter( SimpleTest.expected = parentRunner.expected.filter(
([pat]) => pat != "ASSERTION" ([pat]) => pat != "ASSERTION"
); );
SimpleTest.num_failed = new Array(SimpleTest.expected.length); SimpleTest.num_failed = new Array(SimpleTest.expected.length);
SimpleTest.num_failed.fill(0); SimpleTest.num_failed.fill(0);
} }
}
}; };
SimpleTest.setExpected(); SimpleTest.setExpected();
@ -368,7 +478,6 @@ SimpleTest.record = function(condition, name, diag, stack, expected) {
stack.splice(0, 1); stack.splice(0, 1);
stack = stack.join("\n"); stack = stack.join("\n");
} }
SimpleTest._logResult(test, successInfo, failureInfo, stack); SimpleTest._logResult(test, successInfo, failureInfo, stack);
SimpleTest._tests.push(test); SimpleTest._tests.push(test);
}; };
@ -464,6 +573,8 @@ SimpleTest.getTestFileURL = function(path) {
SimpleTest._getCurrentTestURL = function() { SimpleTest._getCurrentTestURL = function() {
return ( return (
(SimpleTest.harnessParameters &&
SimpleTest.harnessParameters.currentTestURL) ||
(parentRunner && parentRunner.currentTestURL) || (parentRunner && parentRunner.currentTestURL) ||
(typeof gTestPath == "string" && gTestPath) || (typeof gTestPath == "string" && gTestPath) ||
"unknown test url" "unknown test url"
@ -1331,7 +1442,6 @@ SimpleTest.finish = function() {
expected: "FAIL", expected: "FAIL",
message: "TEST-UNEXPECTED-PASS", message: "TEST-UNEXPECTED-PASS",
}; };
SimpleTest._logResult(test, successInfo, failureInfo); SimpleTest._logResult(test, successInfo, failureInfo);
SimpleTest._tests.push(test); SimpleTest._tests.push(test);
} else if (usesFailurePatterns()) { } else if (usesFailurePatterns()) {

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

@ -75,6 +75,16 @@ function flattenArguments(lst /* ...*/) {
return res; return res;
} }
function testInXOriginFrame() {
// Check if the test running in an iframe is a cross origin test.
try {
$("testframe").contentWindow.origin;
return false;
} catch (e) {
return true;
}
}
/** /**
* TestRunner: A test runner for SimpleTest * TestRunner: A test runner for SimpleTest
* TODO: * TODO:
@ -356,7 +366,30 @@ TestRunner._makeIframe = function(url, retry) {
); );
} }
window.scrollTo(0, $("indicator").offsetTop); window.scrollTo(0, $("indicator").offsetTop);
try {
let urlObj = new URL(url);
if (TestRunner.xOriginTests) {
// The test will run in a xorigin iframe, so we pass in additional test params in the
// URL since the content process won't be able to access them from the parentRunner
// directly.
let params = TestRunner.getParameterInfo();
urlObj.searchParams.append(
"currentTestURL",
urlObj.pathname.replace("/tests/", "")
);
urlObj.searchParams.append("closeWhenDone", params.closeWhenDone);
urlObj.searchParams.append("showTestReport", TestRunner.showTestReport);
urlObj.searchParams.append("expected", TestRunner.expected);
iframe.src = urlObj.href;
} else {
iframe.src = url; iframe.src = url;
}
} catch {
// If the provided `url` is not a valid URL (i.e. doesn't include a protocol)
// then the new URL() constructor will raise a TypeError. This is expected in the
// usual case (i.e. non-xorigin iFrame tests) so set the URL in the usual way.
iframe.src = url;
}
iframe.name = url; iframe.name = url;
iframe.width = "500"; iframe.width = "500";
}; };
@ -367,12 +400,15 @@ TestRunner._makeIframe = function(url, retry) {
* being finished first. * being finished first.
*/ */
TestRunner.getLoadedTestURL = function() { TestRunner.getLoadedTestURL = function() {
if (!testInXOriginFrame()) {
var prefix = ""; var prefix = "";
// handle mochitest-chrome URIs // handle mochitest-chrome URIs
if ($("testframe").contentWindow.location.protocol == "chrome:") { if ($("testframe").contentWindow.location.protocol == "chrome:") {
prefix = "chrome://mochitests"; prefix = "chrome://mochitests";
} }
return prefix + $("testframe").contentWindow.location.pathname; return prefix + $("testframe").contentWindow.location.pathname;
}
return TestRunner.currentTestURL;
}; };
TestRunner.setParameterInfo = function(params) { TestRunner.setParameterInfo = function(params) {
@ -704,12 +740,16 @@ TestRunner.testFinished = function(tests) {
} }
var interstitialURL; var interstitialURL;
if ($("testframe").contentWindow.location.protocol == "chrome:") { if (
!testInXOriginFrame() &&
$("testframe").contentWindow.location.protocol == "chrome:"
) {
interstitialURL = "tests/SimpleTest/iframe-between-tests.html"; interstitialURL = "tests/SimpleTest/iframe-between-tests.html";
} else { } else {
interstitialURL = "/tests/SimpleTest/iframe-between-tests.html"; interstitialURL = "/tests/SimpleTest/iframe-between-tests.html";
} }
// check if there were test run after SimpleTest.finish, which should never happen // check if there were test run after SimpleTest.finish, which should never happen
if (!testInXOriginFrame()) {
$("testframe").contentWindow.addEventListener("unload", function() { $("testframe").contentWindow.addEventListener("unload", function() {
var testwin = $("testframe").contentWindow; var testwin = $("testframe").contentWindow;
if ( if (
@ -732,6 +772,7 @@ TestRunner.testFinished = function(tests) {
TestRunner.updateUI([{ result: false }]); TestRunner.updateUI([{ result: false }]);
} }
}); });
}
TestRunner._makeIframe(interstitialURL, 0); TestRunner._makeIframe(interstitialURL, 0);
} }
@ -891,3 +932,43 @@ TestRunner.updateUI = function(tests) {
TestRunner.displayLoopErrors("fail-table", tests); TestRunner.displayLoopErrors("fail-table", tests);
} }
}; };
// XOrigin Tests
// If "--enable-xorigin-tests" is set, mochitests are run in a cross origin iframe.
// The parent process will run at http://mochi.xorigin-test:8888", and individual
// mochitests will be launched in a cross-origin iframe at http://mochi.test:8888.
var xOriginDispatchMap = {
runner: TestRunner,
logger: TestRunner.structuredLogger,
addFailedTest: TestRunner.addFailedTest,
expectAssertions: TestRunner.expectAssertions,
requestLongerTimeout: TestRunner.requestLongerTimeout,
testUnloaded: TestRunner.testUnloaded,
"structuredLogger.deactivateBuffering":
TestRunner.structuredLogger.deactivateBuffering,
"structuredLogger.activateBuffering":
TestRunner.structuredLogger.activateBuffering,
"structuredLogger.testStatus": TestRunner.structuredLogger.testStatus,
"structuredLogger.info": TestRunner.structuredLogger.info,
testFinished: TestRunner.testFinished,
};
function xOriginTestRunnerHandler(event) {
if (event.data.harnessType != "SimpleTest") {
return;
}
if (event.data.command in xOriginDispatchMap) {
xOriginDispatchMap[event.data.command].apply(
xOriginDispatchMap[event.data.applyOn],
event.data.params
);
} else {
TestRunner.error(`Command ${event.data.command} not found
in xOriginDispatchMap`);
}
}
TestRunner.setXOriginEventHandler = function() {
window.addEventListener("message", xOriginTestRunnerHandler);
};

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

@ -90,6 +90,8 @@ if (config.testRoot == "chrome" || config.testRoot == "a11y") {
} }
params = config; params = config;
params.baseurl = "chrome://mochitests/content"; params.baseurl = "chrome://mochitests/content";
} else if (params.xOriginTests) {
params.baseurl = "http://mochi.test:8888/tests/";
} else { } else {
params.baseurl = ""; params.baseurl = "";
} }
@ -100,6 +102,9 @@ if (params.testRoot == "browser") {
params.testPrefix = "chrome://mochitests/content/chrome/"; params.testPrefix = "chrome://mochitests/content/chrome/";
} else if (params.testRoot == "a11y") { } else if (params.testRoot == "a11y") {
params.testPrefix = "chrome://mochitests/content/a11y/"; params.testPrefix = "chrome://mochitests/content/a11y/";
} else if (params.xOriginTests) {
params.testPrefix = "http://mochi.test:8888/tests/";
params.httpsBaseUrl = "https://example.org:443/tests/";
} else { } else {
params.testPrefix = "/tests/"; params.testPrefix = "/tests/";
} }
@ -175,6 +180,11 @@ if (params.cleanupCrashes) {
TestRunner.cleanupCrashes = true; TestRunner.cleanupCrashes = true;
} }
if (params.xOriginTests) {
TestRunner.xOriginTests = true;
TestRunner.setXOriginEventHandler();
}
// Log things to the console if appropriate. // Log things to the console if appropriate.
TestRunner.logger.addListener("dumpListener", consoleLevel + "", function(msg) { TestRunner.logger.addListener("dumpListener", consoleLevel + "", function(msg) {
dump(msg.info.join(" ") + "\n"); dump(msg.info.join(" ") + "\n");