Bug 1388256 - Improving stability of name caching test. r=eeejay, a=test-only
authorYura Zenevich <yura.zenevich@gmail.com>
Thu, 05 Oct 2017 16:20:48 -0400
changeset 432363 a0b348c406e4
parent 432362 5ce1a536ea4d
child 432364 5efe45ee1b0a
push id7946
push userryanvm@gmail.com
push dateWed, 11 Oct 2017 17:47:53 +0000
treeherdermozilla-beta@465b880a2af9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerseeejay, test-only
bugs1388256
milestone57.0
Bug 1388256 - Improving stability of name caching test. r=eeejay, a=test-only MozReview-Commit-ID: A6yoo3CZEyP
accessible/tests/browser/.eslintrc.js
accessible/tests/browser/e10s/browser_caching_name.js
accessible/tests/browser/events.js
accessible/tests/browser/tree/head.js
--- a/accessible/tests/browser/.eslintrc.js
+++ b/accessible/tests/browser/.eslintrc.js
@@ -1,14 +1,17 @@
 "use strict";
 
 module.exports = {
   "extends": [
     "plugin:mozilla/browser-test"
   ],
+  "globals": {
+    "gBrowser": false
+  },
   "rules": {
     "mozilla/no-aArgs": "error",
     "mozilla/no-cpows-in-tests": "error",
     "mozilla/reject-importGlobalProperties": "error",
     "mozilla/var-only-at-top-level": "error",
 
     "block-scoped-var": "error",
     "camelcase": "error",
--- a/accessible/tests/browser/e10s/browser_caching_name.js
+++ b/accessible/tests/browser/e10s/browser_caching_name.js
@@ -307,100 +307,99 @@ const markupTests = [{
     <a id="a-img"
        aria-label="test1"
        aria-labelledby="l1 l2"
        title="test4"><img alt="test5"/></a>`,
   expected: ["test2 test3", "test1", "test5", "test4"]
 }];
 
 /**
- * Wait for an accessible event to happen and, in case given accessible is
- * defunct, update it to one that is attached to the accessible event.
- * @param {Promise} onEvent      accessible event promise
- * @param {Object}  target       { acc, parent, id } structure that contains an
- *                                accessible, its parent and its content element
- *                                id.
- */
-async function updateAccessibleIfNeeded(onEvent, target) {
-  let event = await onEvent;
-  if (isDefunct(target.acc)) {
-    target.acc = findAccessibleChildByID(event.accessible, target.id);
-  }
-}
-
-/**
  * Test accessible name that is calculated from an attribute, remove the
  * attribute before proceeding to the next name test. If attribute removal
  * results in a reorder or text inserted event - wait for it. If accessible
  * becomes defunct, update its reference using the one that is attached to one
  * of the above events.
  * @param {Object} browser      current "tabbrowser" element
  * @param {Object} target       { acc, parent, id } structure that contains an
  *                               accessible, its parent and its content element
  *                               id.
  * @param {Object} rule         current attr rule for name calculation
  * @param {[type]} expected     expected name value
  */
 async function testAttrRule(browser, target, rule, expected) {
-  testName(target.acc, expected);
-  let onEvent;
-  if (rule.recreated) {
-    onEvent = waitForEvent(EVENT_REORDER, target.parent);
-  } else if (rule.textchanged) {
-    onEvent = waitForEvent(EVENT_TEXT_INSERTED, target.id);
-  }
-  await invokeSetAttribute(browser, target.id, rule.attr);
-  if (onEvent) {
-    await updateAccessibleIfNeeded(onEvent, target);
+  let {id, parent, acc} = target;
+  let {recreated, textchanged, attr} = rule;
+
+  testName(acc, expected);
+
+  if (recreated || textchanged) {
+    let [event] = await contentSpawnMutation(browser, {
+      expected: [recreated ? [EVENT_REORDER, parent] : [EVENT_TEXT_INSERTED, id]]
+    }, ([contentId, contentAttr]) =>
+      document.getElementById(contentId).removeAttribute(contentAttr), [id, attr]);
+
+    // Update accessible just in case it is now defunct.
+    target.acc = findAccessibleChildByID(event.accessible, id);
+  } else {
+    await invokeSetAttribute(browser, id, attr);
   }
 }
 
 /**
  * Test accessible name that is calculated from an element name, remove the
  * element before proceeding to the next name test. If element removal results
  * in a reorder event - wait for it. If accessible becomes defunct, update its
  * reference using the one that is attached to a possible reorder event.
  * @param {Object} browser      current "tabbrowser" element
  * @param {Object} target       { acc, parent, id } structure that contains an
  *                               accessible, its parent and its content element
  *                               id.
  * @param {Object} rule         current elm rule for name calculation
  * @param {[type]} expected     expected name value
  */
 async function testElmRule(browser, target, rule, expected) {
-  testName(target.acc, expected);
-  let onEvent = waitForEvent(EVENT_REORDER, rule.isSibling ?
-    target.parent : target.id);
-  await ContentTask.spawn(browser, rule.elm, elm =>
-    content.document.querySelector(`${elm}`).remove());
-  await updateAccessibleIfNeeded(onEvent, target);
+  let {id, parent, acc} = target;
+  let {isSibling, elm} = rule;
+
+  testName(acc, expected);
+  let [event] = await contentSpawnMutation(browser, {
+    expected: [[EVENT_REORDER, isSibling ? parent : id]]
+  }, contentElm => document.querySelector(`${contentElm}`).remove(), elm);
+
+  // Update accessible just in case it is now defunct.
+  target.acc = findAccessibleChildByID(event.accessible, id);
 }
 
 /**
  * Test accessible name that is calculated from its subtree, remove the subtree
  * and wait for a reorder event before proceeding to the next name test. If
  * accessible becomes defunct, update its reference using the one that is
  * attached to a reorder event.
  * @param {Object} browser      current "tabbrowser" element
  * @param {Object} target       { acc, parent, id } structure that contains an
  *                               accessible, its parent and its content element
  *                               id.
  * @param {Object} rule         current subtree rule for name calculation
  * @param {[type]} expected     expected name value
  */
 async function testSubtreeRule(browser, target, rule, expected) {
-  testName(target.acc, expected);
-  let onEvent = waitForEvent(EVENT_REORDER, target.id);
-  await ContentTask.spawn(browser, target.id, id => {
-    let elm = content.document.getElementById(id);
+  let {id, acc} = target;
+
+  testName(acc, expected);
+  let [event] = await contentSpawnMutation(browser, {
+    expected: [[EVENT_REORDER, id]]
+  }, contentId => {
+    let elm = document.getElementById(contentId);
     while (elm.firstChild) {
       elm.firstChild.remove();
     }
-  });
-  await updateAccessibleIfNeeded(onEvent, target);
+  }, id);
+
+  // Update accessible just in case it is now defunct.
+  target.acc = findAccessibleChildByID(event.accessible, id);
 }
 
 /**
  * Iterate over a list of rules and test accessible names for each one of the
  * rules.
  * @param {Object} browser      current "tabbrowser" element
  * @param {Object} target       { acc, parent, id } structure that contains an
  *                               accessible, its parent and its content element
--- a/accessible/tests/browser/events.js
+++ b/accessible/tests/browser/events.js
@@ -8,17 +8,17 @@
 // globals from there.
 /* import-globals-from shared-head.js */
 /* import-globals-from ../mochitest/common.js */
 
 /* exported EVENT_REORDER, EVENT_SHOW, EVENT_TEXT_INSERTED, EVENT_TEXT_REMOVED,
             EVENT_DOCUMENT_LOAD_COMPLETE, EVENT_HIDE, EVENT_TEXT_CARET_MOVED,
             EVENT_DESCRIPTION_CHANGE, EVENT_NAME_CHANGE, EVENT_STATE_CHANGE,
             EVENT_VALUE_CHANGE, EVENT_TEXT_VALUE_CHANGE, EVENT_FOCUS,
-            EVENT_DOCUMENT_RELOAD, UnexpectedEvents,
+            EVENT_DOCUMENT_RELOAD, UnexpectedEvents, contentSpawnMutation,
             waitForEvent, waitForEvents, waitForOrderedEvents */
 
 const EVENT_DOCUMENT_LOAD_COMPLETE = nsIAccessibleEvent.EVENT_DOCUMENT_LOAD_COMPLETE;
 const EVENT_HIDE = nsIAccessibleEvent.EVENT_HIDE;
 const EVENT_REORDER = nsIAccessibleEvent.EVENT_REORDER;
 const EVENT_SHOW = nsIAccessibleEvent.EVENT_SHOW;
 const EVENT_STATE_CHANGE = nsIAccessibleEvent.EVENT_STATE_CHANGE;
 const EVENT_TEXT_CARET_MOVED = nsIAccessibleEvent.EVENT_TEXT_CARET_MOVED;
@@ -178,8 +178,49 @@ function waitForEvents(events, ordered =
     unexpectedListener.stop();
     return results;
   });
 }
 
 function waitForOrderedEvents(events) {
   return waitForEvents(events, true);
 }
+
+/*
+ * This function spawns a content task and awaits expected mutation events from
+ * various content changes. It's good at catching events we did *not* expect. We
+ * do this advancing the layout refresh to flush the relocations/insertions
+ * queue.
+ */
+async function contentSpawnMutation(browser, waitFor, func, args = null) {
+  let onReorders = waitForEvents({ expected: waitFor.expected || [] });
+  let unexpectedListener = new UnexpectedEvents(waitFor.unexpected || []);
+
+  function tick() {
+    // 100ms is an arbitrary positive number to advance the clock.
+    // We don't need to advance the clock for a11y mutations, but other
+    // tick listeners may depend on an advancing clock with each refresh.
+    content.QueryInterface(Ci.nsIInterfaceRequestor)
+      .getInterface(Ci.nsIDOMWindowUtils).advanceTimeAndRefresh(100);
+  }
+
+  // This stops the refreh driver from doing its regular ticks, and leaves
+  // us in control.
+  await ContentTask.spawn(browser, null, tick);
+
+  // Perform the tree mutation.
+  await ContentTask.spawn(browser, args, func);
+
+  // Do one tick to flush our queue (insertions, relocations, etc.)
+  await ContentTask.spawn(browser, null, tick);
+
+  let events = await onReorders;
+
+  unexpectedListener.stop();
+
+  // Go back to normal refresh driver ticks.
+  await ContentTask.spawn(browser, null, function() {
+    content.QueryInterface(Ci.nsIInterfaceRequestor)
+      .getInterface(Ci.nsIDOMWindowUtils).restoreNormalRefresh();
+  });
+
+  return events;
+}
--- a/accessible/tests/browser/tree/head.js
+++ b/accessible/tests/browser/tree/head.js
@@ -1,15 +1,15 @@
 /* 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/. */
 
 "use strict";
 
-/* exported contentSpawnMutation, testChildrenIds */
+/* exported testChildrenIds */
 
 // Load the shared-head file first.
 /* import-globals-from ../shared-head.js */
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/accessible/tests/browser/shared-head.js",
   this);
 
 // Loading and common.js from accessible/tests/mochitest/ for all tests, as
@@ -20,46 +20,8 @@ loadScripts({ name: "common.js", dir: MO
  * A test function for comparing the IDs of an accessible's children
  * with an expected array of IDs.
  */
 function testChildrenIds(acc, expectedIds) {
   let ids = arrayFromChildren(acc).map(child => getAccessibleDOMNodeID(child));
   Assert.deepEqual(ids, expectedIds,
     `Children for ${getAccessibleDOMNodeID(acc)} are wrong.`);
 }
-
-/*
- * This function spawns a content task and awaits expected mutation events from
- * aria-owns changes. It's good at catching events we did *not* expect. We do
- * this advancing the layout refresh to flush the relocations/insertions queue.
- */
-async function contentSpawnMutation(browser, waitFor, func) {
-  let onReorders = waitForEvents({ expected: waitFor.expected || [] });
-  let unexpectedListener = new UnexpectedEvents(waitFor.unexpected || []);
-
-  function tick() {
-    // 100ms is an arbitrary positive number to advance the clock.
-    // We don't need to advance the clock for a11y mutations, but other
-    // tick listeners may depend on an advancing clock with each refresh.
-    content.QueryInterface(Ci.nsIInterfaceRequestor)
-      .getInterface(Ci.nsIDOMWindowUtils).advanceTimeAndRefresh(100);
-  }
-
-  // This stops the refreh driver from doing its regular ticks, and leaves
-  // us in control.
-  await ContentTask.spawn(browser, null, tick);
-
-  // Perform the tree mutation.
-  await ContentTask.spawn(browser, null, func);
-
-  // Do one tick to flush our queue (insertions, relocations, etc.)
-  await ContentTask.spawn(browser, null, tick);
-
-  await onReorders;
-
-  unexpectedListener.stop();
-
-  // Go back to normal refresh driver ticks.
-  await ContentTask.spawn(browser, null, function() {
-    content.QueryInterface(Ci.nsIInterfaceRequestor)
-      .getInterface(Ci.nsIDOMWindowUtils).restoreNormalRefresh();
-  });
-}