Bug 1164218 - Allow running individual mochitests and reftests in chaos mode. r=roc,froydnj
authorKartikaya Gupta <kgupta@mozilla.com>
Thu, 04 Jun 2015 13:44:55 -0400
changeset 277951 34066854f6450ce7e401d9f2763d66f4c236eec2
parent 277950 455f5316157a3935305d69cbc875fa04f87f3185
child 277952 89ac61464a450e47d2d6586886fcf486f74c2e9b
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-beta@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc, froydnj
bugs1164218
milestone41.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1164218 - Allow running individual mochitests and reftests in chaos mode. r=roc,froydnj
dom/base/nsDOMWindowUtils.cpp
dom/interfaces/base/nsIDOMWindowUtils.idl
layout/tools/reftest/reftest.js
mfbt/ChaosMode.cpp
mfbt/ChaosMode.h
mfbt/objs.mozbuild
testing/mochitest/tests/SimpleTest/SimpleTest.js
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -35,16 +35,17 @@
 #include "nsContentUtils.h"
 
 #include "nsIFrame.h"
 #include "nsIWidget.h"
 #include "nsCharsetSource.h"
 #include "nsJSEnvironment.h"
 #include "nsJSUtils.h"
 
+#include "mozilla/ChaosMode.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/MiscEvents.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/TextEventDispatcher.h"
 #include "mozilla/TouchEvents.h"
 
 #include "nsViewManager.h"
@@ -3771,16 +3772,32 @@ nsDOMWindowUtils::GetServiceWorkersTesti
   nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
   NS_ENSURE_STATE(window);
 
   *aEnabled = window->GetServiceWorkersTestingEnabled();
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsDOMWindowUtils::EnterChaosMode()
+{
+  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
+  ChaosMode::enterChaosMode();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWindowUtils::LeaveChaosMode()
+{
+  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
+  ChaosMode::leaveChaosMode();
+  return NS_OK;
+}
+
 NS_INTERFACE_MAP_BEGIN(nsTranslationNodeList)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_INTERFACE_MAP_ENTRY(nsITranslationNodeList)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(nsTranslationNodeList)
 NS_IMPL_RELEASE(nsTranslationNodeList)
 
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -44,17 +44,17 @@ interface nsIDOMClientRect;
 interface nsIURI;
 interface nsIDOMEventTarget;
 interface nsIRunnable;
 interface nsITranslationNodeList;
 interface nsIJSRAIIHelper;
 interface nsIContentPermissionRequest;
 interface nsIObserver;
 
-[scriptable, uuid(7719798a-62fa-4dff-a6ed-782256649232)]
+[scriptable, uuid(098d9f0d-7809-4d3c-8fc6-e5b3fb71835b)]
 interface nsIDOMWindowUtils : nsISupports {
 
   /**
    * Image animation mode of the window. When this attribute's value
    * is changed, the implementation should set all images in the window
    * to the given value. That is, when set to kDontAnimMode, all images
    * will stop animating. The attribute's value must be one of the
    * animationMode values from imgIContainer.
@@ -1807,16 +1807,29 @@ interface nsIDOMWindowUtils : nsISupport
                        in int32_t aRight,
                        in int32_t aBottom,
                        in int32_t aLeft);
 
   /**
    * Enable some service workers testing features.
    */
   attribute boolean serviceWorkersTestingEnabled;
+
+  /**
+   * Increase the chaos mode activation level. An equivalent number of
+   * calls to leaveChaosMode must be made in order to restore the original
+   * chaos mode state. If the activation level is nonzero all chaos mode
+   * features are activated.
+   */
+  void enterChaosMode();
+
+  /**
+   * Decrease the chaos mode activation level. See enterChaosMode().
+   */
+  void leaveChaosMode();
 };
 
 [scriptable, uuid(c694e359-7227-4392-a138-33c0cc1f15a6)]
 interface nsITranslationNodeList : nsISupports {
   readonly attribute unsigned long length;
   nsIDOMNode item(in unsigned long index);
 
   // A translation root is a block element, or an inline element
--- a/layout/tools/reftest/reftest.js
+++ b/layout/tools/reftest/reftest.js
@@ -880,18 +880,19 @@ function ReadManifest(aURL, inherited_st
         var minAsserts = 0;
         var maxAsserts = 0;
         var needs_focus = false;
         var slow = false;
         var testPrefSettings = defaultTestPrefSettings.concat();
         var refPrefSettings = defaultRefPrefSettings.concat();
         var fuzzy_max_delta = 2;
         var fuzzy_max_pixels = 1;
+        var chaosMode = false;
 
-        while (items[0].match(/^(fails|needs-focus|random|skip|asserts|slow|require-or|silentfail|pref|test-pref|ref-pref|fuzzy)/)) {
+        while (items[0].match(/^(fails|needs-focus|random|skip|asserts|slow|require-or|silentfail|pref|test-pref|ref-pref|fuzzy|chaos-mode)/)) {
             var item = items.shift();
             var stat;
             var cond;
             var m = item.match(/^(fails|random|skip|silentfail)-if(\(.*\))$/);
             if (m) {
                 stat = m[1];
                 // Note: m[2] contains the parentheses, and we want them.
                 cond = Components.utils.evalInSandbox(m[2], sandbox);
@@ -960,16 +961,19 @@ function ReadManifest(aURL, inherited_st
               fuzzy_max_pixels = Number(m[2]);
             } else if ((m = item.match(/^fuzzy-if\((.*?),(\d+),(\d+)\)$/))) {
               cond = false;
               if (Components.utils.evalInSandbox("(" + m[1] + ")", sandbox)) {
                 expected_status = EXPECTED_FUZZY;
                 fuzzy_max_delta = Number(m[2]);
                 fuzzy_max_pixels = Number(m[3]);
               }
+            } else if (item == "chaos-mode") {
+                cond = false;
+                chaosMode = true;
             } else {
                 throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": unexpected item " + item;
             }
 
             if (cond) {
                 if (stat == "fails") {
                     expected_status = EXPECTED_FAIL;
                 } else if (stat == "random") {
@@ -1048,17 +1052,18 @@ function ReadManifest(aURL, inherited_st
                           maxAsserts: maxAsserts,
                           needsFocus: needs_focus,
                           slow: slow,
                           prefSettings1: testPrefSettings,
                           prefSettings2: refPrefSettings,
                           fuzzyMaxDelta: fuzzy_max_delta,
                           fuzzyMaxPixels: fuzzy_max_pixels,
                           url1: testURI,
-                          url2: null });
+                          url2: null,
+                          chaosMode: chaosMode });
         } else if (items[0] == TYPE_SCRIPT) {
             if (items.length != 2)
                 throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": incorrect number of arguments to script";
             var [testURI] = runHttp
                             ? ServeFiles(principal, httpDepth,
                                          listURL, [items[1]])
                             : [gIOService.newURI(items[1], null, listURL)];
             var prettyPath = runHttp
@@ -1074,17 +1079,18 @@ function ReadManifest(aURL, inherited_st
                           maxAsserts: maxAsserts,
                           needsFocus: needs_focus,
                           slow: slow,
                           prefSettings1: testPrefSettings,
                           prefSettings2: refPrefSettings,
                           fuzzyMaxDelta: fuzzy_max_delta,
                           fuzzyMaxPixels: fuzzy_max_pixels,
                           url1: testURI,
-                          url2: null });
+                          url2: null,
+                          chaosMode: chaosMode });
         } else if (items[0] == TYPE_REFTEST_EQUAL || items[0] == TYPE_REFTEST_NOTEQUAL) {
             if (items.length != 3)
                 throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": incorrect number of arguments to " + items[0];
             var [testURI, refURI] = runHttp
                                   ? ServeFiles(principal, httpDepth,
                                                listURL, [items[1], items[2]])
                                   : [gIOService.newURI(items[1], null, listURL),
                                      gIOService.newURI(items[2], null, listURL)];
@@ -1103,17 +1109,18 @@ function ReadManifest(aURL, inherited_st
                           maxAsserts: maxAsserts,
                           needsFocus: needs_focus,
                           slow: slow,
                           prefSettings1: testPrefSettings,
                           prefSettings2: refPrefSettings,
                           fuzzyMaxDelta: fuzzy_max_delta,
                           fuzzyMaxPixels: fuzzy_max_pixels,
                           url1: testURI,
-                          url2: refURI });
+                          url2: refURI,
+                          chaosMode: chaosMode });
         } else {
             throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": unknown test type " + items[0];
         }
     }
 }
 
 function AddURIUseCount(uri)
 {
@@ -1237,16 +1244,19 @@ function StartCurrentTest()
     }
 
     if (gURLs.length == 0) {
         RestoreChangedPreferences();
         DoneTests();
     }
     else {
         gDumpLog("REFTEST TEST-START | " + gURLs[0].prettyPath + "\n");
+        if (gURLs[0].chaosMode) {
+            gWindowUtils.enterChaosMode();
+        }
         if (!gURLs[0].needsFocus) {
             Blur();
         }
         var currentTest = gTotalTests - gURLs.length;
         gContainingWindow.document.title = "reftest: " + currentTest + " / " + gTotalTests +
             " (" + Math.floor(100 * (currentTest / gTotalTests)) + "%)";
         StartCurrentURI(1);
     }
@@ -1858,16 +1868,19 @@ function DoAssertionCheck(numAsserts)
         } else if (numAsserts != 0) {
             ++gTestResults.AssertionKnown;
             gDumpLog("REFTEST TEST-KNOWN-FAIL | " + gURLs[0].prettyPath +
                  " | assertion count " + numAsserts + " matches " +
                  expectedAssertions + "\n");
         }
     }
 
+    if (gURLs[0].chaosMode) {
+        gWindowUtils.leaveChaosMode();
+    }
     gDumpLog("REFTEST TEST-END | " + gURLs[0].prettyPath + "\n");
 
     // And start the next test.
     gURLs.shift();
     StartCurrentTest();
 }
 
 function ResetRenderingState()
new file mode 100644
--- /dev/null
+++ b/mfbt/ChaosMode.cpp
@@ -0,0 +1,15 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include "mozilla/ChaosMode.h"
+
+namespace mozilla {
+namespace detail {
+
+Atomic<uint32_t> gChaosModeCounter(0);
+
+} /* namespace detail */
+} /* namespace mozilla */
--- a/mfbt/ChaosMode.h
+++ b/mfbt/ChaosMode.h
@@ -2,23 +2,28 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #ifndef mozilla_ChaosMode_h
 #define mozilla_ChaosMode_h
 
+#include "mozilla/Atomics.h"
 #include "mozilla/EnumSet.h"
 
 #include <stdint.h>
 #include <stdlib.h>
 
 namespace mozilla {
 
+namespace detail {
+extern MFBT_DATA Atomic<uint32_t> gChaosModeCounter;
+}
+
 /**
  * When "chaos mode" is activated, code that makes implicitly nondeterministic
  * choices is encouraged to make random and extreme choices, to test more
  * code paths and uncover bugs.
  */
 class ChaosMode
 {
 public:
@@ -39,20 +44,43 @@ public:
 
 private:
   // Change this to any non-None value to activate ChaosMode.
   static const ChaosFeature sChaosFeatures = None;
 
 public:
   static bool isActive(ChaosFeature aFeature)
   {
+    if (detail::gChaosModeCounter > 0) {
+      return true;
+    }
     return sChaosFeatures & aFeature;
   }
 
   /**
+   * Increase the chaos mode activation level. An equivalent number of
+   * calls to leaveChaosMode must be made in order to restore the original
+   * chaos mode state. If the activation level is nonzero all chaos mode
+   * features are activated.
+   */
+  static void enterChaosMode()
+  {
+    detail::gChaosModeCounter++;
+  }
+
+  /**
+   * Decrease the chaos mode activation level. See enterChaosMode().
+   */
+  static void leaveChaosMode()
+  {
+    MOZ_ASSERT(detail::gChaosModeCounter > 0);
+    detail::gChaosModeCounter--;
+  }
+
+  /**
    * Returns a somewhat (but not uniformly) random uint32_t < aBound.
    * Not to be used for anything except ChaosMode, since it's not very random.
    */
   static uint32_t randomUint32LessThan(uint32_t aBound)
   {
     return uint32_t(rand()) % aBound;
   }
 };
--- a/mfbt/objs.mozbuild
+++ b/mfbt/objs.mozbuild
@@ -1,15 +1,16 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 mfbt_src_lcppsrcs = [
+    'ChaosMode.cpp',
     'double-conversion/bignum-dtoa.cc',
     'double-conversion/bignum.cc',
     'double-conversion/cached-powers.cc',
     'double-conversion/diy-fp.cc',
     'double-conversion/double-conversion.cc',
     'double-conversion/fast-dtoa.cc',
     'double-conversion/fixed-dtoa.cc',
     'double-conversion/strtod.cc',
--- a/testing/mochitest/tests/SimpleTest/SimpleTest.js
+++ b/testing/mochitest/tests/SimpleTest/SimpleTest.js
@@ -235,16 +235,17 @@ SimpleTest.testPluginIsOOP = function ()
     return testPluginIsOOP;
 };
 
 SimpleTest._tests = [];
 SimpleTest._stopOnLoad = true;
 SimpleTest._cleanupFunctions = [];
 SimpleTest.expected = 'pass';
 SimpleTest.num_failed = 0;
+SimpleTest._inChaosMode = false;
 
 SimpleTest.setExpected = function () {
   if (parent.TestRunner) {
     SimpleTest.expected = parent.TestRunner.expected;
   }
 }
 SimpleTest.setExpected();
 
@@ -987,16 +988,25 @@ SimpleTest.executeSoon = function(aFunc)
     setTimeout(aFunc, 0);
     return null;		// Avoid warning.
 };
 
 SimpleTest.registerCleanupFunction = function(aFunc) {
     SimpleTest._cleanupFunctions.push(aFunc);
 };
 
+SimpleTest.testInChaosMode = function() {
+    if (SimpleTest._inChaosMode) {
+      // It's already enabled for this test, don't enter twice
+      return;
+    }
+    SpecialPowers.DOMWindowUtils.enterChaosMode();
+    SimpleTest._inChaosMode = true;
+};
+
 /**
  * Finishes the tests. This is automatically called, except when
  * SimpleTest.waitForExplicitFinish() has been invoked.
 **/
 SimpleTest.finish = function() {
     if (SimpleTest._alreadyFinished) {
         var err = "[SimpleTest.finish()] this test already called finish!";
         if (parentRunner) {
@@ -1015,16 +1025,21 @@ SimpleTest.finish = function() {
         SimpleTest._logResult(test, successInfo, failureInfo);
         SimpleTest._tests.push(test);
     }
 
     SimpleTest.testsLength = SimpleTest._tests.length;
 
     SimpleTest._alreadyFinished = true;
 
+    if (SimpleTest._inChaosMode) {
+        SpecialPowers.DOMWindowUtils.leaveChaosMode();
+        SimpleTest._inChaosMode = false;
+    }
+
     var afterCleanup = function() {
         if (SpecialPowers.DOMWindowUtils.isTestControllingRefreshes) {
             SimpleTest.ok(false, "test left refresh driver under test control");
             SpecialPowers.DOMWindowUtils.restoreNormalRefresh();
         }
         if (SimpleTest._expectingUncaughtException) {
             SimpleTest.ok(false, "expectUncaughtException was called but no uncaught exception was detected!");
         }