Bug 1041706 - Mochitests prints raw JSON logs when running with a debugger. r=ahal, a=test-only
authorAhmed Kachkach <ahmed.kachkach@gmail.com>
Wed, 23 Jul 2014 15:31:00 +0200
changeset 217480 251037f4b6c9bd5897e3b535b1ebd2cb614ce1eb
parent 217479 48047fdbe1265df130fbcc1870cca99c17db72c3
child 217481 b0d492f89974872417ab2e419a5d1565ea450048
push id515
push userraliiev@mozilla.com
push dateMon, 06 Oct 2014 12:51:51 +0000
treeherdermozilla-release@267c7a481bef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersahal, test-only
bugs1041706
milestone33.0a2
Bug 1041706 - Mochitests prints raw JSON logs when running with a debugger. r=ahal, a=test-only
testing/mochitest/runtests.py
testing/mochitest/tests/SimpleTest/TestRunner.js
testing/mochitest/tests/SimpleTest/setup.js
testing/specialpowers/content/MozillaLogger.js
--- a/testing/mochitest/runtests.py
+++ b/testing/mochitest/runtests.py
@@ -514,16 +514,18 @@ class MochitestUtilsMixin(object):
       if options.debugOnFailure:
         self.urlOpts.append("debugOnFailure=true")
       if options.dumpOutputDirectory:
         self.urlOpts.append("dumpOutputDirectory=%s" % encodeURIComponent(options.dumpOutputDirectory))
       if options.dumpAboutMemoryAfterTest:
         self.urlOpts.append("dumpAboutMemoryAfterTest=true")
       if options.dumpDMDAfterTest:
         self.urlOpts.append("dumpDMDAfterTest=true")
+      if options.debugger:
+        self.urlOpts.append("interactiveDebugger=true")
 
   def getTestFlavor(self, options):
     if options.browserChrome:
       return "browser-chrome"
     elif options.chrome:
       return "chrome"
     elif options.a11y:
       return "a11y"
--- a/testing/mochitest/tests/SimpleTest/TestRunner.js
+++ b/testing/mochitest/tests/SimpleTest/TestRunner.js
@@ -60,27 +60,81 @@ function flattenArguments(lst/* ...*/) {
             }
         } else {
             res.push(o);
         }
     }
     return res;
 }
 
+/**
+ * StructuredFormatter: Formatter class turning structured messages
+ * into human-readable messages.
+ */
+this.StructuredFormatter = function() {
+    this.testStartTimes = {};
+};
+
+StructuredFormatter.prototype.log = function(message) {
+  return message.message;
+};
+
+StructuredFormatter.prototype.suite_start = function(message) {
+    this.suiteStartTime = message.time;
+    return "SUITE-START | Running " +  message.tests.length + " tests";
+};
+
+StructuredFormatter.prototype.test_start = function(message) {
+    this.testStartTimes[message.test] = new Date().getTime();
+    return "TEST-START | " + message.test;
+};
+
+StructuredFormatter.prototype.test_status = function(message) {
+    var statusInfo = message.test + " | " + message.subtest +
+                    (message.message ? " | " + message.message : "");
+    if (message.expected) {
+        return "TEST-UNEXPECTED-" + message.status + " | " + statusInfo +
+               " - expected: " + message.expected;
+    } else {
+        return "TEST-" + message.status + " | " + statusInfo;
+    }
+};
+
+StructuredFormatter.prototype.test_end = function(message) {
+    var startTime = this.testStartTimes[message.test];
+    delete this.testStartTimes[message.test];
+    var statusInfo = message.test + (message.message ? " | " + String(message.message) : "");
+    var result;
+    if (message.expected) {
+        result = "TEST-UNEXPECTED-" + message.status + " | " + statusInfo +
+                 " - expected: " + message.expected;
+    } else {
+        return "TEST-" + message.status + " | " + statusInfo;
+    }
+    result = " | took " + message.time - startTime + "ms";
+};
+
+StructuredFormatter.prototype.suite_end = function(message) {
+    return "SUITE-END | took " + message.time - this.suiteStartTime + "ms";
+};
 
 /**
  * StructuredLogger: Structured logger class following the mozlog.structured protocol
  *
  *
 **/
 var VALID_ACTIONS = ['suite_start', 'suite_end', 'test_start', 'test_end', 'test_status', 'process_output', 'log'];
+// This delimiter is used to avoid interleaving Mochitest/Gecko logs.
+var LOG_DELIMITER = String.fromCharCode(0xe175) + String.fromCharCode(0xee31) + String.fromCharCode(0x2c32) + String.fromCharCode(0xacbf);
 
 function StructuredLogger(name) {
     this.name = name;
     this.testsStarted = [];
+    this.interactiveDebugger = false;
+    this.structuredFormatter = new StructuredFormatter();
 
     /* test logs */
 
     this.testStart = function(test) {
         var data = {test: test};
         this._logData("test_start", data);
     };
 
@@ -190,17 +244,22 @@ function StructuredLogger(name) {
         for (var attrname in data) {
             allData[attrname] = data[attrname];
         }
 
         this._dumpMessage(allData);
     };
 
     this._dumpMessage = function(message) {
-        var str = JSON.stringify(message);
+        var str;
+        if (this.interactiveDebugger) {
+            str = this.structuredFormatter[message.action](message);
+        } else {
+            str = LOG_DELIMITER + JSON.stringify(message) + LOG_DELIMITER;
+        }
         // BUGFIX: browser-chrome tests doesn't use LogController
         if (Object.keys(LogController.listeners).length !== 0) {
             LogController.log(str);
         } else {
             dump('\n' + str + '\n');
         }
     };
 
--- a/testing/mochitest/tests/SimpleTest/setup.js
+++ b/testing/mochitest/tests/SimpleTest/setup.js
@@ -130,20 +130,23 @@ if (params.dumpOutputDirectory) {
 if (params.dumpAboutMemoryAfterTest) {
   TestRunner.dumpAboutMemoryAfterTest = true;
 }
 
 if (params.dumpDMDAfterTest) {
   TestRunner.dumpDMDAfterTest = true;
 }
 
+if (params.interactiveDebugger) {
+  TestRunner.structuredLogger.interactiveDebugger = true;
+}
+
 // Log things to the console if appropriate.
 TestRunner.logger.addListener("dumpListener", consoleLevel + "", function(msg) {
-  var data = formatLogMessage(msg);
-  dump(data);
+  dump(msg.info.join(' ') + "\n");
 });
 
 var gTestList = [];
 var RunSet = {};
 RunSet.runall = function(e) {
   // Filter tests to include|exclude tests based on data in params.filter.
   // This allows for including or excluding tests from the gTestList
   if (params.testManifest) {
--- a/testing/specialpowers/content/MozillaLogger.js
+++ b/testing/specialpowers/content/MozillaLogger.js
@@ -1,20 +1,19 @@
 /**
  * MozillaLogger, a base class logger that just logs to stdout.
  */
 
 "use strict";
 
 function MozillaLogger(aPath) {
 }
-// This delimiter is used to avoid interleaving Mochitest/Gecko logs.
-var LOG_DELIMITER = String.fromCharCode(0xe175) + String.fromCharCode(0xee31) + String.fromCharCode(0x2c32) + String.fromCharCode(0xacbf);
+
 function formatLogMessage(msg) {
-    return LOG_DELIMITER + msg.info.join(' ') + LOG_DELIMITER + "\n";
+    return msg.info.join(' ') + "\n";
 }
 
 MozillaLogger.prototype = {
   init : function(path) {},
 
   getLogCallback : function() {
     return function (msg) {
       var data = formatLogMessage(msg);