Bug 615846 - correct name/test_markup.html, extend events debugging in mochitests, r=fer, a=test
authorAlexander Surkov <surkov.alexander@gmail.com>
Fri, 03 Dec 2010 18:49:08 +0800
changeset 58539 d25494ae4c3b3a3a63c96f96078ec1e9a3908ecc
parent 58538 0ff6d59842870cf6c29e07f082a66d4e15306567
child 58540 eb4d2a3dc388cac70d1dcf9b0c3900063d430c97
push id1
push usershaver@mozilla.com
push dateTue, 04 Jan 2011 17:58:04 +0000
reviewersfer, test
bugs615846
milestone2.0b8pre
Bug 615846 - correct name/test_markup.html, extend events debugging in mochitests, r=fer, a=test
accessible/tests/mochitest/events.js
accessible/tests/mochitest/name/markup.js
accessible/tests/mochitest/name/markuprules.xml
--- a/accessible/tests/mochitest/events.js
+++ b/accessible/tests/mochitest/events.js
@@ -31,16 +31,21 @@ const EVENT_VALUE_CHANGE = nsIAccessible
 var gA11yEventDumpID = "";
 
 /**
  * Set up this variable to dump event processing into console.
  */
 var gA11yEventDumpToConsole = false;
 
 /**
+ * Set up this variable to dump event processing into error console.
+ */
+var gA11yEventDumpToAppConsole = false;
+
+/**
  * Executes the function when requested event is handled.
  *
  * @param aEventType  [in] event type
  * @param aTarget     [in] event target
  * @param aFunc       [in] function to call when event is handled
  * @param aContext    [in, optional] object in which context the function is
  *                    called
  * @param aArg1       [in, optional] argument passed into the function
@@ -265,20 +270,22 @@ function eventQueue(aEventType)
         SimpleTest.finish();
 
       return;
     }
 
     // Start processing of next invoker.
     invoker = this.getNextInvoker();
 
-    this.setEventHandler(invoker);
+    if (gLogger.isEnabled()) {
+      gLogger.logToConsole("Event queue: \n  invoke: " + invoker.getID());
+      gLogger.logToDOM("EQ: invoke: " + invoker.getID(), true);
+    }
 
-    if (gA11yEventDumpToConsole)
-      dump("\nEvent queue: \n  invoke: " + invoker.getID() + "\n");
+    this.setEventHandler(invoker);
 
     if (invoker.invoke() == INVOKER_ACTION_FAILED) {
       // Invoker failed to prepare action, fail and finish tests.
       this.processNextInvoker();
       return;
     }
 
     if (this.areAllEventsUnexpected())
@@ -397,16 +404,32 @@ function eventQueue(aEventType)
     this.mEventSeqIdx = -1;
 
     // Register event listeners
     if (this.mEventSeq) {
       aInvoker.wasCaught = new Array(this.mEventSeq.length);
 
       for (var idx = 0; idx < this.mEventSeq.length; idx++) {
         var eventType = this.getEventType(idx);
+
+        if (gLogger.isEnabled()) {
+          var strEventType = (typeof eventType == "string") ? eventType :
+            eventTypeToString(eventType);
+
+          var msg = "registered";
+          if (this.isEventUnexpected(idx))
+            msg += " unexpected";
+
+          msg += ": event type: " + strEventType + ", target: " +
+            prettyName(this.getEventTarget(idx));
+
+          gLogger.logToConsole(msg);
+          gLogger.logToDOM(msg, true);
+        }
+
         if (typeof eventType == "string") {
           // DOM event
           var target = this.getEventTarget(idx);
           var phase = this.getEventPhase(idx);
           target.ownerDocument.addEventListener(eventType, this, phase);
 
         } else {
           // A11y event
@@ -461,16 +484,21 @@ function eventQueue(aEventType)
     var eventItem = this.mEventSeq[aIdx];
     if ("getID" in eventItem)
       return eventItem.getID();
     
     var invoker = this.getInvoker();
     return invoker.getID();
   }
 
+  this.isEventUnexpected = function eventQueue_isEventUnexpected(aIdx)
+  {
+    return this.mEventSeq[aIdx].unexpected;
+  }
+
   this.compareEvents = function eventQueue_compareEvents(aIdx, aEvent)
   {
     var eventType1 = this.getEventType(aIdx);
 
     var eventType2 = (aEvent instanceof nsIDOMEvent) ?
       aEvent.type : aEvent.eventType;
 
     if (eventType1 != eventType2)
@@ -521,64 +549,49 @@ function eventQueue(aEventType)
 
     return true;
   }
 
   this.dumpEventToDOM = function eventQueue_dumpEventToDOM(aOrigEvent,
                                                            aExpectedEventIdx,
                                                            aMatch)
   {
-    if (!gA11yEventDumpID) // debug stuff
+    if (!gLogger.isEnabled()) // debug stuff
       return;
 
     // Dump DOM event information. Skip a11y event since it is dumped by
     // gA11yEventObserver.
     if (aOrigEvent instanceof nsIDOMEvent) {
       var info = "Event type: " + aOrigEvent.type;
       info += ". Target: " + prettyName(aOrigEvent.originalTarget);
-      dumpInfoToDOM(info);
+      gLogger.logToDOM(info);
     }
 
     var currType = this.getEventType(aExpectedEventIdx);
     var currTarget = this.getEventTarget(aExpectedEventIdx);
 
-    var containerTagName = document instanceof nsIDOMHTMLDocument ?
-      "div" : "description";
-    var inlineTagName = document instanceof nsIDOMHTMLDocument ?
-      "span" : "description";
-
-    var container = document.createElement(containerTagName);
-    container.setAttribute("style", "padding-left: 10px;");
+    var msg = "EQ: ";
+    var emphText = "";
+    if (aMatch) {
+      emphText = "matched ";
 
-    var text1 = document.createTextNode("EQ: ");
-    container.appendChild(text1);
-
-    var styledNode = document.createElement(inlineTagName);
-    if (aMatch) {
-      styledNode.setAttribute("style", "color: blue;");
-      styledNode.textContent = "matched";
-
-      // Dump matched events into console.
-      if (gA11yEventDumpToConsole)
-        dump("\n*****\nEQ matched: " + eventTypeToString(currType) + "\n*****\n");
+      var consoleMsg =
+        "*****\nEQ matched: " + eventTypeToString(currType) + "\n*****";
+      gLogger.logToConsole(consoleMsg);
 
     } else {
-      styledNode.textContent = "expected";
+      msg += "expected";
     }
-    container.appendChild(styledNode);
 
-    var info = " event, type: ";
-    info += (typeof currType == "string") ?
+    msg += " event, type: ";
+    msg += (typeof currType == "string") ?
       currType : eventTypeToString(currType);
-    info += ". Target: " + prettyName(currTarget);
+    msg += ", target: " + prettyName(currTarget);
 
-    var text1 = document.createTextNode(info);
-    container.appendChild(text1);
-
-    dumpInfoToDOM(container);
+    gLogger.logToDOM(msg, true, emphText);
   }
 
   this.mDefEventType = aEventType;
 
   this.mInvokers = new Array();
   this.mIndex = -1;
 
   this.mEventSeq = null;
@@ -906,46 +919,46 @@ var gA11yEventObserver =
       // Remove the leftover observer, otherwise it "leaks" to all the following tests.
       this.observerService.removeObserver(this, "accessible-event");
       // Forward the exception, with added explanation.
       throw "[accessible/events.js, gA11yEventObserver.observe] This is expected if a previous test has been aborted... Initial exception was: [ " + ex + " ]";
     }
     var listenersArray = gA11yEventListeners[event.eventType];
 
     var eventFromDumpArea = false;
-    if (gA11yEventDumpID) { // debug stuff
+    if (gLogger.isEnabled()) { // debug stuff
       eventFromDumpArea = true;
 
       var target = event.DOMNode;
-      var dumpElm = document.getElementById(gA11yEventDumpID);
+      var dumpElm = gA11yEventDumpID ?
+        document.getElementById(gA11yEventDumpID) : null;
 
-      var parent = target;
-      while (parent && parent != dumpElm)
-        parent = parent.parentNode;
+      if (dumpElm) {
+        var parent = target;
+        while (parent && parent != dumpElm)
+          parent = parent.parentNode;
+      }
 
-      if (parent != dumpElm) {
+      if (!dumpElm || parent != dumpElm) {
         var type = eventTypeToString(event.eventType);
         var info = "Event type: " + type;
 
         if (event instanceof nsIAccessibleTextChangeEvent) {
           info += ", start: " + event.start + ", length: " + event.length +
             ", " + (event.isInserted() ? "inserted" : "removed") +
             " text: " + event.modifiedText;
         }
 
         info += ". Target: " + prettyName(event.accessible);
 
         if (listenersArray)
           info += ". Listeners count: " + listenersArray.length;
 
         eventFromDumpArea = false;
-
-        if (gA11yEventDumpToConsole)
-          dump("\n" + info + "\n");
-        dumpInfoToDOM(info);
+        gLogger.log(info);
       }
     }
 
     // Do not notify listeners if event is result of event log changes.
     if (!listenersArray || eventFromDumpArea)
       return;
 
     for (var index = 0; index < listenersArray.length; index++)
@@ -993,45 +1006,103 @@ function removeA11yEventListener(aEventT
     gA11yEventListeners[aEventType] = null;
     delete gA11yEventListeners[aEventType];
   }
 
   return true;
 }
 
 /**
- * Dumps message to DOM.
- *
- * @param aInfo      [in] the message or DOM node to dump
- * @param aDumpNode  [in, optional] host DOM node for dumped message, if ommited
- *                    then global variable gA11yEventDumpID is used
+ * Used to dump debug information.
  */
-function dumpInfoToDOM(aInfo, aDumpNode)
+var gLogger =
 {
-  var dumpID = gA11yEventDumpID ? gA11yEventDumpID : aDumpNode;
-  if (!dumpID)
-    return;
-  
-  var dumpElm = document.getElementById(dumpID);
-  if (!dumpElm) {
-    ok(false, "No dump element '" + dumpID + "' within the document!");
-    return;
-  }
-  
-  var containerTagName = document instanceof nsIDOMHTMLDocument ?
-    "div" : "description";
+  /**
+   * Return true if dump is enabled.
+   */
+  isEnabled: function debugOutput_isEnabled()
+  {
+    return gA11yEventDumpID || gA11yEventDumpToConsole ||
+      gA11yEventDumpToAppConsole;
+  },
+
+  /**
+   * Dump information into DOM and console if applicable.
+   */
+  log: function logger_log(aMsg)
+  {
+    this.logToConsole(aMsg);
+    this.logToAppConsole(aMsg);
+    this.logToDOM(aMsg);
+  },
+
+  /**
+   * Log message to DOM.
+   *
+   * @param aMsg          [in] the primary message
+   * @param aHasIndent    [in, optional] if specified the message has an indent
+   * @param aPreEmphText  [in, optional] the text is colored and appended prior
+   *                        primary message
+   */
+  logToDOM: function logger_logToDOM(aMsg, aHasIndent, aPreEmphText)
+  {
+    if (gA11yEventDumpID == "")
+      return;
+
+    var dumpElm = document.getElementById(gA11yEventDumpID);
+    if (!dumpElm) {
+      ok(false,
+         "No dump element '" + gA11yEventDumpID + "' within the document!");
+      return;
+    }
+
+    var containerTagName = document instanceof nsIDOMHTMLDocument ?
+      "div" : "description";
 
-  var container = document.createElement(containerTagName);
-  if (aInfo instanceof nsIDOMNode)
-    container.appendChild(aInfo);
-  else
-    container.textContent = aInfo;
+    var container = document.createElement(containerTagName);
+    if (aHasIndent)
+      container.setAttribute("style", "padding-left: 10px;");
+
+    if (aPreEmphText) {
+      var inlineTagName = document instanceof nsIDOMHTMLDocument ?
+        "span" : "description";
+      var emphElm = document.createElement(inlineTagName);
+      emphElm.setAttribute("style", "color: blue;");
+      emphElm.textContent = aPreEmphText;
+
+      container.appendChild(emphElm);
+    }
+
+    var textNode = document.createTextNode(aMsg);
+    container.appendChild(textNode);
+
+    dumpElm.appendChild(container);
+  },
 
-  dumpElm.appendChild(container);
-}
+  /**
+   * Log message to console.
+   */
+  logToConsole: function logger_logToConsole(aMsg)
+  {
+    if (gA11yEventDumpToConsole)
+      dump("\n" + aMsg + "\n");
+  },
+
+  /**
+   * Log message to error console.
+   */
+  logToAppConsole: function logger_logToAppConsole(aMsg)
+  {
+    if (gA11yEventDumpToAppConsole)
+      consoleService.logStringMessage("events: " + aMsg);
+  },
+
+  consoleService: Components.classes["@mozilla.org/consoleservice;1"].
+    getService(Components.interfaces.nsIConsoleService)
+};
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // Sequence
 
 /**
  * Base class of sequence item.
  */
--- a/accessible/tests/mochitest/name/markup.js
+++ b/accessible/tests/mochitest/name/markup.js
@@ -1,15 +1,19 @@
 ////////////////////////////////////////////////////////////////////////////////
 // Name tests described by "markuprules.xml" file.
 
 var gNameRulesFileURL = "markuprules.xml";
 
 var gRuleDoc = null;
 
+// Debuggin stuff.
+var gDumpToConsole = false;
+gA11yEventDumpToConsole = gDumpToConsole;
+
 /**
  * Start name tests. Run through markup elements and test names for test
  * element (see namerules.xml for details).
  */
 function testNames()
 {
   var request = new XMLHttpRequest();
   request.open("get", gNameRulesFileURL, false);
@@ -56,20 +60,26 @@ var gTestIterator =
     this.ruleIdx++;
     if (this.ruleIdx == this.ruleElms.length) {
       this.markupIdx++;
       if (this.markupIdx == this.markupElms.length) {
         SimpleTest.finish();
         return;
       }
 
-      document.body.removeChild(this.container);
+      this.ruleIdx = -1;
 
-      this.ruleIdx = -1;
-      testNamesForMarkup(this.markupElms[this.markupIdx]);
+      if (gDumpToConsole) {
+        dump("\nPend next markup processing. Wait for reorder event on " +
+             prettyName(document) + "'\n");
+      }
+      waitForEvent(EVENT_REORDER, document, testNamesForMarkup,
+                   null, this.markupElms[this.markupIdx]);
+
+      document.body.removeChild(this.container);
       return;
     }
 
     testNameForRule(this.elm, this.ruleElms[this.ruleIdx]);
   },
 
   markupElms: null,
   markupIdx: -1,
@@ -80,34 +90,44 @@ var gTestIterator =
 };
 
 /**
  * Process every 'markup' element and test names for it. Used by testNames
  * function.
  */
 function testNamesForMarkup(aMarkupElm)
 {
+  if (gDumpToConsole)
+    dump("\nProcessing markup '" + aMarkupElm.getAttribute("id") + "'\n");
+
   var div = document.createElement("div");
   div.setAttribute("id", "test");
 
   var child = aMarkupElm.firstChild;
   while (child) {
     var newChild = document.importNode(child, true);
     div.appendChild(newChild);
     child = child.nextSibling;
   }
 
+  if (gDumpToConsole) {
+    dump("\nProcessing markup. Wait for reorder event on " +
+         prettyName(document) + "'\n");
+  }
   waitForEvent(EVENT_REORDER, document, testNamesForMarkupRules,
                 null, aMarkupElm, div);
 
   document.body.appendChild(div);
 }
 
 function testNamesForMarkupRules(aMarkupElm, aContainer)
 {
+  if (gDumpToConsole)
+    dump("\nProcessing markup rules '" + aMarkupElm.getAttribute("id") + "'\n");
+
   ensureAccessibleTree(aContainer);
 
   var serializer = new XMLSerializer();
 
   var expr = "//html/body/div[@id='test']/" + aMarkupElm.getAttribute("ref");
   var elms = evaluateXPath(document, expr, htmlDocResolver);
 
   var ruleId = aMarkupElm.getAttribute("ruleset");
@@ -117,22 +137,39 @@ function testNamesForMarkupRules(aMarkup
 }
 
 /**
  * Test name for current rule and current 'markup' element. Used by
  * testNamesForMarkup function.
  */
 function testNameForRule(aElm, aRuleElm)
 {
-  if (aRuleElm.hasAttribute("attr"))
+  if (aRuleElm.hasAttribute("attr")) {
+    if (gDumpToConsole) {
+      dump("\nProcessing rule { attr: " + aRuleElm.getAttribute("attr") +" }\n");
+    }
+
     testNameForAttrRule(aElm, aRuleElm);
-  else if (aRuleElm.hasAttribute("elm") && aRuleElm.hasAttribute("elmattr"))
+
+  } else if (aRuleElm.hasAttribute("elm") && aRuleElm.hasAttribute("elmattr")) {
+    if (gDumpToConsole) {
+      dump("\nProcessing rule { elm: " + aRuleElm.getAttribute("elm") +
+           ", elmattr: " + aRuleElm.getAttribute("elmattr") +" }\n");
+    }
+
     testNameForElmRule(aElm, aRuleElm);
-  else if (aRuleElm.getAttribute("fromsubtree") == "true")
+
+  } else if (aRuleElm.getAttribute("fromsubtree") == "true") {
+    if (gDumpToConsole) {
+      dump("\nProcessing rule { fromsubtree: " +
+           aRuleElm.getAttribute("fromsubtree") +" }\n");
+    }
+
     testNameForSubtreeRule(aElm, aRuleElm);
+  }
 }
 
 function testNameForAttrRule(aElm, aRule)
 {
   var name = "";
 
   var attr = aRule.getAttribute("attr");
   var attrValue = aElm.getAttribute(attr);
@@ -182,27 +219,36 @@ function testNameForElmRule(aElm, aRule)
   var treeWalker = document.createTreeWalker(document.body,
                                              NodeFilter.SHOW_ELEMENT,
                                              filter, false);
   var labelElm = treeWalker.nextNode();
   var msg = "Element '" + elm + "' test.";
   testName(aElm, labelElm.getAttribute("a11yname"), msg);
 
   var parentNode = labelElm.parentNode;
+
+  if (gDumpToConsole) {
+    dump("\nProcessed elm rule. Wait for reorder event on " +
+         prettyName(parentNode) + "'\n");
+  }
   waitForEvent(EVENT_REORDER, parentNode,
                gTestIterator.iterateNext, gTestIterator);
 
   parentNode.removeChild(labelElm);
 }
 
 function testNameForSubtreeRule(aElm, aRule)
 {
   var msg = "From subtree test.";
   testName(aElm, aElm.getAttribute("a11yname"), msg);
 
+  if (gDumpToConsole) {
+    dump("\nProcessed from subtree rule. Wait for reorder event on " +
+         prettyName(aElm) + "\n");
+  }
   waitForEvent(EVENT_REORDER, aElm, gTestIterator.iterateNext, gTestIterator);
 
   while (aElm.firstChild)
     aElm.removeChild(aElm.firstChild);
 }
 
 /**
  * Return array of 'rule' elements. Used in conjunction with
--- a/accessible/tests/mochitest/name/markuprules.xml
+++ b/accessible/tests/mochitest/name/markuprules.xml
@@ -119,57 +119,59 @@
       <rule fromsubtree="true"/>
       <rule attr="title" type="string"/>
     </ruleset>
 
   </ruledfn>
 
   <rulesample>
 
-    <markup ref="html:button" ruleset="htmlctrl">
+    <markup ref="html:button" ruleset="htmlctrl" id="markup1test">
       <html:span id="l1" a11yname="test2">test2</html:span>
       <html:span id="l2" a11yname="test3">test3</html:span>
       <html:label for="btn" a11yname="test4">test4</html:label>
       <html:button id="btn"
                    aria-label="test1"
                    aria-labelledby="l1 l2"
                    title="test5"
                    a11yname="press me">press me</html:button>
     </markup>
 
-    <markup ref="html:input" ruleset="htmlinputbutton">
+    <markup ref="html:input" ruleset="htmlinputbutton" id="markup2test">
       <html:span id="l1" a11yname="test2">test2</html:span>
       <html:span id="l2" a11yname="test3">test3</html:span>
       <html:label for="btn" a11yname="test4">test4</html:label>
       <html:input id="btn"
                   type="button"
                   aria-label="test1"
                   aria-labelledby="l1 l2"
                   value="test5"
                   alt="test6"
                   src="test7"
                   data="test8"
                   title="test9"/>
     </markup>
 
-    <markup ref="html:select/html:option[1]" ruleset="htmloption">
+    <markup ref="html:select/html:option[1]" ruleset="htmloption"
+            id="markup3test">
       <html:span id="l1" a11yname="test2">test2</html:span>
       <html:span id="l2" a11yname="test3">test3</html:span>
       <html:select>
         <html:option id="opt"
                      aria-label="test1"
                      aria-labelledby="l1 l2"
                      label="test4"
                      title="test5"
                      a11yname="option1">option1</html:option>
         <html:option>option2</html:option>
       </html:select>
     </markup>
 
-    <markup ref="html:table/html:tr/html:td" ruleset="htmlelm">
+    <markup ref="html:table/html:tr/html:td" ruleset="htmlelm"
+            id="markup4test">
       <html:span id="l1" a11yname="test2">test2</html:span>
       <html:span id="l2" a11yname="test3">test3</html:span>
       <html:label for="tc" a11yname="test4">test4</html:label>
       <html:table>
         <html:tr>
           <html:td id="tc"
                    aria-label="test1"
                    aria-labelledby="l1 l2"
@@ -179,17 +181,18 @@
             <html:ul>
               <html:li>This is a list</html:li>
             </html:ul>
           </html:td>
         </html:tr>
       </html:table>
     </markup>
 
-    <markup ref="html:table/html:tr/html:td" ruleset="htmlctrl">
+    <markup ref="html:table/html:tr/html:td" ruleset="htmlctrl"
+            id="markup5test">
       <html:span id="l1" a11yname="test2">test2</html:span>
       <html:span id="l2" a11yname="test3">test3</html:span>
       <html:label for="gc" a11yname="test4">test4</html:label>
       <html:table>
         <html:tr>
           <html:td id="gc"
                    role="gridcell"
                    aria-label="test1"