Bug 1614738, remove racy promiseContentSearchChange function in favour of a version that adds the listener first before performing the engine modification action, r=adw

This fixes intermittent test failures that occur more frequently with the JSWindowActor based content search module.

Depends on D68237

Differential Revision: https://phabricator.services.mozilla.com/D70619

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Neil Deakin 2020-04-14 18:47:05 +00:00
Родитель 1351306d5f
Коммит 349e62fa06
5 изменённых файлов: 137 добавлений и 94 удалений

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

@ -7,75 +7,83 @@ ignoreAllUncaughtExceptions();
add_task(async function() { add_task(async function() {
info("Check POST search engine support"); info("Check POST search engine support");
await BrowserTestUtils.withNewTab({ gBrowser, url: "about:home" }, function( let currEngine = await Services.search.getDefault();
browser
) {
return new Promise(resolve => {
let searchObserver = async function search_observer(
subject,
topic,
data
) {
let currEngine = await Services.search.getDefault();
let engine = subject.QueryInterface(Ci.nsISearchEngine);
info("Observer: " + data + " for " + engine.name);
if (data != "engine-added") { await BrowserTestUtils.withNewTab(
return; { gBrowser, url: "about:home" },
} async browser => {
let observerPromise = new Promise(resolve => {
let searchObserver = async function search_observer(
subject,
topic,
data
) {
let engine = subject.QueryInterface(Ci.nsISearchEngine);
info("Observer: " + data + " for " + engine.name);
if (engine.name != "POST Search") { if (data != "engine-added") {
return; return;
} }
Services.obs.removeObserver( if (engine.name != "POST Search") {
return;
}
Services.obs.removeObserver(
searchObserver,
"browser-search-engine-modified"
);
resolve(engine);
};
Services.obs.addObserver(
searchObserver, searchObserver,
"browser-search-engine-modified" "browser-search-engine-modified"
); );
});
// Ready to execute the tests! let engine;
let needle = "Search for something awesome."; await promiseContentSearchChange(browser, async () => {
Services.search.addEngine(
"http://test:80/browser/browser/base/content/test/about/POSTSearchEngine.xml",
null,
false
);
await Promise.all([ engine = await observerPromise;
promiseContentSearchChange(browser, engine.name), Services.search.setDefault(engine);
Services.search.setDefault(engine), return engine.name;
]); });
let promise = BrowserTestUtils.browserLoaded(browser);
await SpecialPowers.spawn(browser, [{ needle }], async function(args) {
let doc = content.document;
let el = doc.querySelector(["#searchText", "#newtab-search-text"]);
el.value = args.needle;
doc.getElementById("searchSubmit").click();
});
await promise; // Ready to execute the tests!
let needle = "Search for something awesome.";
// When the search results load, check them for correctness. let promise = BrowserTestUtils.browserLoaded(browser);
await SpecialPowers.spawn(browser, [{ needle }], async function(args) { await SpecialPowers.spawn(browser, [{ needle }], async function(args) {
let loadedText = content.document.body.textContent; let doc = content.document;
ok(loadedText, "search page loaded"); let el = doc.querySelector(["#searchText", "#newtab-search-text"]);
is( el.value = args.needle;
loadedText, doc.getElementById("searchSubmit").click();
"searchterms=" + escape(args.needle.replace(/\s/g, "+")), });
"Search text should arrive correctly"
);
});
await Services.search.setDefault(currEngine); await promise;
try {
await Services.search.removeEngine(engine); // When the search results load, check them for correctness.
} catch (ex) {} await SpecialPowers.spawn(browser, [{ needle }], async function(args) {
resolve(); let loadedText = content.document.body.textContent;
}; ok(loadedText, "search page loaded");
Services.obs.addObserver( is(
searchObserver, loadedText,
"browser-search-engine-modified" "searchterms=" + escape(args.needle.replace(/\s/g, "+")),
); "Search text should arrive correctly"
Services.search.addEngine( );
"http://test:80/browser/browser/base/content/test/about/POSTSearchEngine.xml", });
null,
false await Services.search.setDefault(currEngine);
); try {
}); await Services.search.removeEngine(engine);
}); } catch (ex) {}
}
);
}); });

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

@ -12,10 +12,13 @@ add_task(async function() {
async function(browser) { async function(browser) {
// Add a test engine that provides suggestions and switch to it. // Add a test engine that provides suggestions and switch to it.
let currEngine = await Services.search.getDefault(); let currEngine = await Services.search.getDefault();
let engine = await promiseNewEngine("searchSuggestionEngine.xml");
let p = promiseContentSearchChange(browser, engine.name); let engine;
await Services.search.setDefault(engine); await promiseContentSearchChange(browser, async () => {
await p; engine = await promiseNewEngine("searchSuggestionEngine.xml");
await Services.search.setDefault(engine);
return engine.name;
});
// Clear any search history results // Clear any search history results
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {

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

@ -13,11 +13,13 @@ add_task(async function() {
async function(browser) { async function(browser) {
// Add a test engine that provides suggestions and switch to it. // Add a test engine that provides suggestions and switch to it.
let currEngine = await Services.search.getDefault(); let currEngine = await Services.search.getDefault();
let engine = await promiseNewEngine("searchSuggestionEngine.xml");
await Promise.all([ let engine;
promiseContentSearchChange(browser, engine.name), await promiseContentSearchChange(browser, async () => {
Services.search.setDefault(engine), engine = await promiseNewEngine("searchSuggestionEngine.xml");
]); await Services.search.setDefault(engine);
return engine.name;
});
await SpecialPowers.spawn(browser, [], async function() { await SpecialPowers.spawn(browser, [], async function() {
// Type an X in the search input. // Type an X in the search input.

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

@ -13,17 +13,19 @@ add_task(async function() {
{ gBrowser, url: "about:home" }, { gBrowser, url: "about:home" },
async function(browser) { async function(browser) {
let currEngine = await Services.search.getDefault(); let currEngine = await Services.search.getDefault();
let engine = await promiseNewEngine("searchSuggestionEngine.xml");
let engine;
await promiseContentSearchChange(browser, async () => {
engine = await promiseNewEngine("searchSuggestionEngine.xml");
await Services.search.setDefault(engine);
return engine.name;
});
// Make this actually work in healthreport by giving it an ID: // Make this actually work in healthreport by giving it an ID:
Object.defineProperty(engine.wrappedJSObject, "identifier", { Object.defineProperty(engine.wrappedJSObject, "identifier", {
value: "org.mozilla.testsearchsuggestions", value: "org.mozilla.testsearchsuggestions",
}); });
await Promise.all([
promiseContentSearchChange(browser, engine.name),
Services.search.setDefault(engine),
]);
await SpecialPowers.spawn( await SpecialPowers.spawn(
browser, browser,
[{ expectedName: engine.name }], [{ expectedName: engine.name }],

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

@ -158,28 +158,56 @@ function promiseTabLoadEvent(tab, url) {
} }
/** /**
* Wait for the search engine to change. * Wait for the search engine to change. searchEngineChangeFn is a function
* that will be called to change the search engine.
*/ */
function promiseContentSearchChange(browser, newEngineName) { async function promiseContentSearchChange(browser, searchEngineChangeFn) {
// Callers of this depend on very specific, very racy timing, and fail // Add an event listener manually then perform the action, rather than using
// if we introduce the trip through SpecialPowersParent that // BrowserTestUtils.addContentEventListener as that doesn't add the listener
// SpecialPowers.spawn requires. // early enough.
return ContentTask.spawn(browser, { newEngineName }, async function(args) { await SpecialPowers.spawn(browser, [], async () => {
return new Promise(resolve => { // Store the results in a temporary place.
content.addEventListener("ContentSearchService", function listener( content._searchDetails = {
aEvent defaultEnginesList: [],
) { listener: event => {
if ( if (event.detail.type == "CurrentState") {
aEvent.detail.type == "CurrentState" && content._searchDetails.defaultEnginesList.push(
content.wrappedJSObject.gContentSearchController.defaultEngine.name == content.wrappedJSObject.gContentSearchController.defaultEngine.name
args.newEngineName );
) {
content.removeEventListener("ContentSearchService", listener);
resolve();
} }
}); },
}); };
// Listen using the system group to ensure that it fires after
// the default behaviour.
content.addEventListener(
"ContentSearchService",
content._searchDetails.listener,
{ mozSystemGroup: true }
);
}); });
let expectedEngineName = await searchEngineChangeFn();
await SpecialPowers.spawn(
browser,
[expectedEngineName],
async expectedEngineNameChild => {
await ContentTaskUtils.waitForCondition(
() =>
content._searchDetails.defaultEnginesList &&
content._searchDetails.defaultEnginesList[
content._searchDetails.defaultEnginesList.length - 1
] == expectedEngineNameChild
);
content.removeEventListener(
"ContentSearchService",
content._searchDetails.listener,
{ mozSystemGroup: true }
);
delete content._searchDetails;
}
);
} }
/** /**