Bug 844852 - Run GTest on make check. r=ted
☠☠ backed out by 31aa900a060b ☠ ☠
authorBenoit Girard <b56girard@gmail.com>
Thu, 25 Apr 2013 13:56:43 -0400
changeset 134985 eb8b971070ebad30c82207b11f4fca785deef9c2
parent 134984 762073f484eee6e96d0c1ed781fae57129b6f7ad
child 134986 19671f660736c53032e0b46929b44c36503a761c
push id29471
push userb56girard@gmail.com
push dateThu, 13 Jun 2013 20:31:03 +0000
treeherdermozilla-inbound@eb8b971070eb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersted
bugs844852
milestone24.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 844852 - Run GTest on make check. r=ted
testing/gtest/Makefile.in
testing/gtest/mozilla/GTestRunner.cpp
testing/gtest/rungtests.py
--- a/testing/gtest/Makefile.in
+++ b/testing/gtest/Makefile.in
@@ -40,11 +40,12 @@ ifeq (browser,$(MOZ_BUILD_APP))
 # Disable because of metro linking error:
 # LNK1181: cannot open input file 'runtimeobject.lib'
 ifndef MOZ_METRO
 check gtest::
 	$(MAKE) -C $(DEPTH)/toolkit/library gtestxul
 ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 	$(MAKE) -C $(DEPTH)/browser/app repackage
 endif
+	$(PYTHON) $(topsrcdir)/testing/gtest/rungtests.py --xre-path=$(DIST)/bin --symbols-path=$(DIST)/crashreporter-symbols $(DIST)/bin/$(MOZ_APP_NAME)$(BIN_SUFFIX)
 endif
 endif
 
--- a/testing/gtest/mozilla/GTestRunner.cpp
+++ b/testing/gtest/mozilla/GTestRunner.cpp
@@ -2,16 +2,20 @@
  * * 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 "GTestRunner.h"
 #include "gtest/gtest.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/NullPtr.h"
+#ifdef MOZ_CRASHREPORTER
+#include "nsICrashReporter.h"
+#endif
+#include "testing/TestHarness.h"
 #include "prenv.h"
 
 using ::testing::EmptyTestEventListener;
 using ::testing::InitGoogleTest;
 using ::testing::Test;
 using ::testing::TestCase;
 using ::testing::TestEventListeners;
 using ::testing::TestInfo;
@@ -73,16 +77,41 @@ int RunGTestFunc()
   InitGoogleTest(&c, static_cast<char**>(nullptr));
 
   if (getenv("MOZ_TBPL_PARSER")) {
     ReplaceGTestLogger();
   }
 
   PR_SetEnv("XPCOM_DEBUG_BREAK=stack-and-abort");
 
+  ScopedXPCOM xpcom("GTestScopedXPCOM");
+
+#ifdef MOZ_CRASHREPORTER
+  nsCOMPtr<nsICrashReporter> crashreporter;
+  char *crashreporterStr = PR_GetEnv("MOZ_CRASHREPORTER");
+  if (crashreporterStr && !strcmp(crashreporterStr, "1")) {
+    //TODO: move this to an even-more-common location to use in all
+    // C++ unittests
+    crashreporter = do_GetService("@mozilla.org/toolkit/crash-reporter;1");
+    if (crashreporter) {
+      std::cerr << "Setting up crash reporting" << std::endl;
+
+      nsCOMPtr<nsIProperties> dirsvc =
+          do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
+      nsCOMPtr<nsIFile> cwd;
+      nsresult rv = dirsvc->Get(NS_OS_CURRENT_WORKING_DIR,
+                       NS_GET_IID(nsIFile),
+                       getter_AddRefs(cwd));
+      MOZ_ASSERT(NS_SUCCEEDED(rv));
+      crashreporter->SetEnabled(true);
+      crashreporter->SetMinidumpPath(cwd);
+    }
+  }
+#endif
+
   return RUN_ALL_TESTS();
 }
 
 // We use a static var 'RunGTest' defined in nsAppRunner.cpp.
 // RunGTest is initialized to NULL but if GTest (this file)
 // is linked in then RunGTest will be set here indicating
 // GTest is supported.
 class _InitRunGTest {
new file mode 100644
--- /dev/null
+++ b/testing/gtest/rungtests.py
@@ -0,0 +1,129 @@
+#!/usr/bin/env 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/.
+
+from __future__ import with_statement
+import sys, os, tempfile, shutil
+from optparse import OptionParser
+import mozprocess, mozinfo, mozlog, mozcrash
+from contextlib import contextmanager
+
+log = mozlog.getLogger('gtests')
+
+class GTests(object):
+    # Time (seconds) to wait for test process to complete
+    TEST_PROC_TIMEOUT = 1200
+    # Time (seconds) in which process will be killed if it produces no output.
+    TEST_PROC_NO_OUTPUT_TIMEOUT = 300
+
+    def run_gtest(self, prog, xre_path, symbols_path=None):
+        """
+        Run a single C++ unit test program.
+
+        Arguments:
+        * prog: The path to the test program to run.
+        * env: The environment to use for running the program.
+        * symbols_path: A path to a directory containing Breakpad-formatted
+                        symbol files for producing stack traces on crash.
+
+        Return True if the program exits with a zero status, False otherwise.
+        """
+        self.xre_path = xre_path
+        env = self.build_environment()
+        basename = os.path.basename(prog)
+        log.info("Running test %s", basename)
+        proc = mozprocess.ProcessHandler([prog, "-unittest"],
+                                         cwd=os.getcwd(),
+                                         env=env)
+        #TODO: After bug 811320 is fixed, don't let .run() kill the process,
+        # instead use a timeout in .wait() and then kill to get a stack.
+        proc.run(timeout=GTests.TEST_PROC_TIMEOUT,
+                 outputTimeout=GTests.TEST_PROC_NO_OUTPUT_TIMEOUT)
+        proc.wait()
+        if proc.timedOut:
+            log.testFail("%s | timed out after %d seconds",
+                         basename, GTests.TEST_PROC_TIMEOUT)
+            return False
+        if mozcrash.check_for_crashes(os.getcwd(), symbols_path,
+                                      test_name=basename):
+            log.testFail("%s | test crashed", basename)
+            return False
+        result = proc.proc.returncode == 0
+        if not result:
+            log.testFail("%s | test failed with return code %d",
+                         basename, proc.proc.returncode)
+        return result
+
+    def build_core_environment(self, env = {}):
+        """
+        Add environment variables likely to be used across all platforms, including remote systems.
+        """
+        env["MOZILLA_FIVE_HOME"] = self.xre_path
+        env["MOZ_XRE_DIR"] = self.xre_path
+        env["XPCOM_DEBUG_BREAK"] = "stack-and-abort"
+        env["MOZ_CRASHREPORTER_NO_REPORT"] = "1"
+        env["MOZ_CRASHREPORTER"] = "1"
+        env["MOZ_RUN_GTEST"] = "1"
+        # Normally we run with GTest default output, override this to use the TBPL test format.
+        env["MOZ_TBPL_PARSER"] = "1"
+        return env
+
+    def build_environment(self):
+        """
+        Create and return a dictionary of all the appropriate env variables and values.
+        On a remote system, we overload this to set different values and are missing things like os.environ and PATH.
+        """
+        if not os.path.isdir(self.xre_path):
+            raise Exception("xre_path does not exist: %s", self.xre_path)
+        env = dict(os.environ)
+        env = self.build_core_environment(env)
+        pathvar = ""
+        if mozinfo.os == "linux":
+            pathvar = "LD_LIBRARY_PATH"
+        elif mozinfo.os == "mac":
+            pathvar = "DYLD_LIBRARY_PATH"
+        elif mozinfo.os == "win":
+            pathvar = "PATH"
+        if pathvar:
+            if pathvar in env:
+                env[pathvar] = "%s%s%s" % (self.xre_path, os.pathsep, env[pathvar])
+            else:
+                env[pathvar] = self.xre_path
+        return env
+
+class gtestOptions(OptionParser):
+    def __init__(self):
+        OptionParser.__init__(self)
+        self.add_option("--xre-path",
+                        action = "store", type = "string", dest = "xre_path",
+                        default = None,
+                        help = "absolute path to directory containing XRE (probably xulrunner)")
+        self.add_option("--symbols-path",
+                        action = "store", type = "string", dest = "symbols_path",
+                        default = None,
+                        help = "absolute path to directory containing breakpad symbols, or the URL of a zip file containing symbols")
+
+def main():
+    parser = gtestOptions()
+    options, args = parser.parse_args()
+    if not args:
+        print >>sys.stderr, """Usage: %s <binary>""" % sys.argv[0]
+        sys.exit(1)
+    if not options.xre_path:
+        print >>sys.stderr, """Error: --xre-path is required"""
+        sys.exit(1)
+    prog = os.path.abspath(args[0])
+    options.xre_path = os.path.abspath(options.xre_path)
+    tester = GTests()
+    try:
+        result = tester.run_gtest(prog, options.xre_path, options.symbols_path)
+    except Exception, e:
+        log.error(str(e))
+        result = False
+    sys.exit(0 if result else 1)
+
+if __name__ == '__main__':
+    main()
+