GTestRunner.cpp (6715B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * * This Source Code Form is subject to the terms of the Mozilla Public 3 * * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "GTestRunner.h" 7 #include "gtest/gtest.h" 8 #include "mozilla/Attributes.h" 9 #include "mozilla/FOG.h" 10 #include "mozilla/Preferences.h" 11 #include "nsICrashReporter.h" 12 #include "nsString.h" 13 #include "testing/TestHarness.h" 14 #include "prenv.h" 15 #ifdef ANDROID 16 # include <android/log.h> 17 #endif 18 #ifdef XP_WIN 19 # include "mozilla/ipc/WindowsMessageLoop.h" 20 #endif 21 22 using ::testing::EmptyTestEventListener; 23 using ::testing::InitGoogleTest; 24 using ::testing::TestEventListeners; 25 using ::testing::TestInfo; 26 using ::testing::TestPartResult; 27 using ::testing::UnitTest; 28 29 namespace mozilla { 30 31 #ifdef ANDROID 32 # define MOZ_STDOUT_PRINT(...) \ 33 __android_log_print(ANDROID_LOG_INFO, "gtest", __VA_ARGS__); 34 #else 35 # define MOZ_STDOUT_PRINT(...) printf(__VA_ARGS__); 36 #endif 37 38 #define MOZ_PRINT(...) \ 39 MOZ_STDOUT_PRINT(__VA_ARGS__); \ 40 if (mLogFile) { \ 41 fprintf(mLogFile, __VA_ARGS__); \ 42 } 43 44 // See gtest.h for method documentation 45 class MozillaPrinter : public EmptyTestEventListener { 46 public: 47 MozillaPrinter() : mLogFile(nullptr) { 48 char* path = PR_GetEnv("MOZ_GTEST_LOG_PATH"); 49 if (path) { 50 mLogFile = fopen(path, "w"); 51 } 52 } 53 virtual void OnTestProgramStart(const UnitTest& /* aUnitTest */) override { 54 MOZ_PRINT("TEST-INFO | GTest unit test starting\n"); 55 } 56 virtual void OnTestProgramEnd(const UnitTest& aUnitTest) override { 57 MOZ_PRINT("TEST-%s | GTest unit test: %s\n", 58 aUnitTest.Passed() ? "PASS" : "UNEXPECTED-FAIL", 59 aUnitTest.Passed() ? "passed" : "failed"); 60 MOZ_PRINT("Skipped: %d\n", aUnitTest.skipped_test_count()); 61 MOZ_PRINT("Passed: %d\n", aUnitTest.successful_test_count()); 62 MOZ_PRINT("Failed: %d\n", aUnitTest.failed_test_count()); 63 if (mLogFile) { 64 fclose(mLogFile); 65 mLogFile = nullptr; 66 } 67 } 68 virtual void OnTestStart(const TestInfo& aTestInfo) override { 69 mTestInfo = &aTestInfo; 70 MOZ_PRINT("TEST-START | %s.%s\n", mTestInfo->test_case_name(), 71 mTestInfo->name()); 72 } 73 virtual void OnTestPartResult( 74 const TestPartResult& aTestPartResult) override { 75 MOZ_PRINT("TEST-%s | %s.%s | %s @ %s:%i\n", 76 !aTestPartResult.failed() ? "PASS" : "UNEXPECTED-FAIL", 77 mTestInfo ? mTestInfo->test_case_name() : "?", 78 mTestInfo ? mTestInfo->name() : "?", aTestPartResult.summary(), 79 aTestPartResult.file_name(), aTestPartResult.line_number()); 80 } 81 virtual void OnTestEnd(const TestInfo& aTestInfo) override { 82 MOZ_PRINT( 83 "TEST-%s | %s.%s | test completed (time: %" PRIi64 "ms)\n", 84 aTestInfo.result()->Passed() 85 ? "PASS" 86 : (aTestInfo.result()->Skipped() ? "FAIL" : "UNEXPECTED-FAIL"), 87 aTestInfo.test_case_name(), aTestInfo.name(), 88 aTestInfo.result()->elapsed_time()); 89 MOZ_ASSERT(&aTestInfo == mTestInfo); 90 mTestInfo = nullptr; 91 } 92 93 const TestInfo* mTestInfo; 94 FILE* mLogFile; 95 }; 96 97 static void ReplaceGTestLogger() { 98 // Replace the GTest logger so that it can be passed 99 // by the mozilla test parsers. 100 // Code is based on: 101 // http://googletest.googlecode.com/svn/trunk/samples/sample9_unittest.cc 102 UnitTest& unitTest = *UnitTest::GetInstance(); 103 TestEventListeners& listeners = unitTest.listeners(); 104 delete listeners.Release(listeners.default_result_printer()); 105 106 listeners.Append(new MozillaPrinter); 107 } 108 109 int RunGTestFunc(int* argc, char** argv) { 110 InitGoogleTest(argc, argv); 111 112 if (getenv("MOZ_TBPL_PARSER")) { 113 ReplaceGTestLogger(); 114 } 115 116 PR_SetEnv("XPCOM_DEBUG_BREAK=stack-and-abort"); 117 118 ScopedXPCOM xpcom("GTest"); 119 120 #ifdef XP_WIN 121 mozilla::ipc::windows::InitUIThread(); 122 #endif 123 #ifdef ANDROID 124 // On Android, gtest is running in an application, which uses a 125 // current working directory of '/' by default. Desktop tests 126 // sometimes assume that support files are in the current 127 // working directory. For compatibility with desktop, the Android 128 // harness pushes test support files to the device at the location 129 // specified by MOZ_GTEST_CWD and gtest changes the cwd to that 130 // location. 131 char* path = PR_GetEnv("MOZ_GTEST_CWD"); 132 chdir(path); 133 #endif 134 nsCOMPtr<nsICrashReporter> crashreporter; 135 char* crashreporterStr = PR_GetEnv("MOZ_CRASHREPORTER"); 136 if (crashreporterStr && !strcmp(crashreporterStr, "1")) { 137 // TODO: move this to an even-more-common location to use in all 138 // C++ unittests 139 crashreporter = do_GetService("@mozilla.org/toolkit/crash-reporter;1"); 140 if (crashreporter) { 141 printf_stderr("Setting up crash reporting\n"); 142 char* path = PR_GetEnv("MOZ_GTEST_MINIDUMPS_PATH"); 143 nsCOMPtr<nsIFile> file; 144 if (path) { 145 nsresult rv = 146 NS_NewUTF8LocalFile(nsDependentCString(path), getter_AddRefs(file)); 147 if (NS_FAILED(rv)) { 148 printf_stderr("Ignoring invalid MOZ_GTEST_MINIDUMPS_PATH\n"); 149 } 150 } 151 if (!file) { 152 nsCOMPtr<nsIProperties> dirsvc = 153 do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID); 154 nsresult rv = dirsvc->Get(NS_OS_CURRENT_WORKING_DIR, 155 NS_GET_IID(nsIFile), getter_AddRefs(file)); 156 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv)); 157 } 158 crashreporter->SetEnabled(true); 159 crashreporter->SetMinidumpPath(file); 160 } 161 } 162 163 // FOG should init exactly once, as early into running as possible, to enable 164 // instrumentation tests to work properly. 165 // However, at init, Glean may decide to send a ping. So let's first tell FOG 166 // that these pings shouldn't actually be uploaded. 167 Preferences::SetInt("telemetry.fog.test.localhost_port", -1); 168 // Though the default user-activity limits ought to be longer than a test, 169 // ensure that they don't trigger unnecessary ping submission (which clears 170 // storage, making it hard to test instrumentation). 171 Preferences::SetInt("telemetry.fog.test.activity_limit", -1); 172 Preferences::SetInt("telemetry.fog.test.inactivity_limit", -1); 173 const nsCString empty; 174 RefPtr<FOG>(FOG::GetSingleton())->InitializeFOG(empty, empty, false); 175 176 return RUN_ALL_TESTS(); 177 } 178 179 // We use a static var 'RunGTest' defined in nsAppRunner.cpp. 180 // RunGTest is initialized to nullptr but if GTest (this file) 181 // is linked in then RunGTest will be set here indicating 182 // GTest is supported. 183 MOZ_RUNINIT class _InitRunGTest { 184 public: 185 _InitRunGTest() { RunGTest = RunGTestFunc; } 186 } InitRunGTest; 187 188 } // namespace mozilla