From a7bea0cda290473d2a842035615eeef39b6f6b82 Mon Sep 17 00:00:00 2001 From: Kris Maglione Date: Thu, 15 Aug 2019 14:21:37 -0700 Subject: [PATCH] Bug 1574296: Support assertions in nested SpecialPowers.span calls. r=mccr8 Prior to this patch, assertions in SpecialPowers.spawn callbacks only work when the caller was in a window with a SimpleTest harness. This patch fixes that by registering a default assertion handler at the start of a test, and sending assertions from any task without its own harness to said default handler. MANUAL PUSH: Contains complex rename operations that I don't trust Lando to handle correctly. Differential Revision: https://phabricator.services.mozilla.com/D42210 --HG-- extra : rebase_source : b0b72ce166647621a50aad0af8f130c0d89e3829 extra : source : 41b6d03a870caa7c4be2146ce8c8d84efdb20e79 --- testing/mochitest/browser-test.js | 3 +++ .../test_SpecialPowersSandbox.html | 25 +++++++++++++++++-- .../mochitest/tests/SimpleTest/SimpleTest.js | 2 ++ .../content/SpecialPowersChild.jsm | 20 ++++++++++++--- .../content/SpecialPowersParent.jsm | 22 +++++++++++++--- 5 files changed, 64 insertions(+), 8 deletions(-) diff --git a/testing/mochitest/browser-test.js b/testing/mochitest/browser-test.js index 9d7c541020de..351ef4138549 100644 --- a/testing/mochitest/browser-test.js +++ b/testing/mochitest/browser-test.js @@ -477,6 +477,9 @@ function Tester(aTests, structuredLogger, aCallback) { ); this.SimpleTest = simpleTestScope.SimpleTest; + window.SpecialPowers.SimpleTest = this.SimpleTest; + window.SpecialPowers.setAsDefaultAssertHandler(); + var extensionUtilsScope = { registerCleanupFunction: fn => { this.currentTest.scope.registerCleanupFunction(fn); diff --git a/testing/mochitest/tests/Harness_sanity/test_SpecialPowersSandbox.html b/testing/mochitest/tests/Harness_sanity/test_SpecialPowersSandbox.html index 6208a35af4dc..36c8280b684f 100644 --- a/testing/mochitest/tests/Harness_sanity/test_SpecialPowersSandbox.html +++ b/testing/mochitest/tests/Harness_sanity/test_SpecialPowersSandbox.html @@ -37,8 +37,11 @@ async function interceptDiagnostics(func) { } add_task(async function() { + const frameSrc = "https://example.com/tests/testing/mochitest/tests/Harness_sanity/file_spawn.html"; + const subframeSrc = "https://example.org/tests/testing/mochitest/tests/Harness_sanity/file_spawn.html"; + let frame = document.getElementById("iframe"); - frame.src = "https://example.com/tests/testing/mochitest/tests/Harness_sanity/file_spawn.html"; + frame.src = frameSrc; await new Promise(resolve => { frame.addEventListener("load", resolve, {once: true}); @@ -59,13 +62,31 @@ add_task(async function() { // just need to make sure that the general functionality works as expected. let tests = { "SpecialPowers.spawn": () => { - return SpecialPowers.spawn(frame, [], () => { + return SpecialPowers.spawn(frame, [], async () => { Assert.equal(1, 2, "Thing"); Assert.equal(1, 1, "Hmm"); Assert.ok(true, "Yay."); Assert.ok(false, "Boo!."); }); }, + "SpecialPowers.spawn-subframe": () => { + return SpecialPowers.spawn(frame, [subframeSrc], async src => { + let frame = this.content.document.createElement("iframe"); + frame.src = src; + this.content.document.body.appendChild(frame); + + await new Promise(resolve => { + frame.addEventListener("load", resolve, { once: true }); + }); + + await SpecialPowers.spawn(frame, [], () => { + Assert.equal(1, 2, "Thing"); + Assert.equal(1, 1, "Hmm"); + Assert.ok(true, "Yay."); + Assert.ok(false, "Boo!."); + }); + }); + }, "SpecialPowers.loadChromeScript": async () => { let script = SpecialPowers.loadChromeScript(() => { this.addMessageListener("ping", () => "pong"); diff --git a/testing/mochitest/tests/SimpleTest/SimpleTest.js b/testing/mochitest/tests/SimpleTest/SimpleTest.js index c59e6b237a04..05362cf1b19c 100644 --- a/testing/mochitest/tests/SimpleTest/SimpleTest.js +++ b/testing/mochitest/tests/SimpleTest/SimpleTest.js @@ -228,6 +228,8 @@ SimpleTest._inChaosMode = false; SimpleTest.expected = 'pass'; SimpleTest.num_failed = 0; +SpecialPowers.setAsDefaultAssertHandler(); + function usesFailurePatterns() { return Array.isArray(SimpleTest.expected); } diff --git a/testing/specialpowers/content/SpecialPowersChild.jsm b/testing/specialpowers/content/SpecialPowersChild.jsm index e51294e9f27b..5766721a2813 100644 --- a/testing/specialpowers/content/SpecialPowersChild.jsm +++ b/testing/specialpowers/content/SpecialPowersChild.jsm @@ -285,9 +285,7 @@ class SpecialPowersChild extends JSWindowActorChild { // An assertion has been done in a mochitest chrome script let { name, passed, stack, diag } = message.data; - let SimpleTest = - this.contentWindow && this.contentWindow.wrappedJSObject.SimpleTest; - + let { SimpleTest } = this; if (SimpleTest) { SimpleTest.record( passed, @@ -1661,6 +1659,7 @@ class SpecialPowersChild extends JSWindowActorChild { args, task: String(task), caller: SpecialPowersSandbox.getCallerInfo(Components.stack.caller), + hasHarness: typeof this.SimpleTest === "object", }); } @@ -1692,6 +1691,21 @@ class SpecialPowersChild extends JSWindowActorChild { return sb.execute(task, args, caller); } + get SimpleTest() { + return this._SimpleTest || this.contentWindow.wrappedJSObject.SimpleTest; + } + set SimpleTest(val) { + this._SimpleTest = val; + } + + /** + * Sets this actor as the default assertion result handler for tasks + * which originate in a window without a test harness. + */ + setAsDefaultAssertHandler() { + this.sendAsyncMessage("SetAsDefaultAssertHandler"); + } + getFocusedElementForWindow(targetWindow, aDeep) { var outParam = {}; Services.focus.getFocusedElementForWindow(targetWindow, aDeep, outParam); diff --git a/testing/specialpowers/content/SpecialPowersParent.jsm b/testing/specialpowers/content/SpecialPowersParent.jsm index d75d80d5c881..e36c2bd63eef 100644 --- a/testing/specialpowers/content/SpecialPowersParent.jsm +++ b/testing/specialpowers/content/SpecialPowersParent.jsm @@ -119,6 +119,10 @@ function doPrefEnvOp(fn) { // used to bounce assertion messages back down to the correct child. let nextTaskID = 1; +// The default actor to send assertions to if a task originated in a +// window without a test harness. +let defaultAssertHandler; + class SpecialPowersParent extends JSWindowActorParent { constructor() { super(); @@ -200,6 +204,10 @@ class SpecialPowersParent extends JSWindowActorParent { } uninit() { + if (defaultAssertHandler === this) { + defaultAssertHandler = null; + } + var obs = Services.obs; obs.removeObserver(this._observer, "http-on-modify-request"); this._registerObservers._topics.splice(0).forEach(element => { @@ -1042,15 +1050,22 @@ class SpecialPowersParent extends JSWindowActorParent { }); } + case "SetAsDefaultAssertHandler": { + defaultAssertHandler = this; + return undefined; + } + case "Spawn": { - let { browsingContext, task, args, caller } = aMessage.data; + let { browsingContext, task, args, caller, hasHarness } = aMessage.data; let spParent = browsingContext.currentWindowGlobal.getActor( "SpecialPowers" ); let taskId = nextTaskID++; - spParent._taskActors.set(taskId, this); + if (hasHarness) { + spParent._taskActors.set(taskId, this); + } return spParent .sendQuery("Spawn", { task, args, caller, taskId }) @@ -1083,9 +1098,10 @@ class SpecialPowersParent extends JSWindowActorParent { case "ProxiedAssert": { let { taskId, data } = aMessage.data; - let actor = this._taskActors.get(taskId); + let actor = this._taskActors.get(taskId) || defaultAssertHandler; actor.sendAsyncMessage("Assert", data); + return undefined; }