diff --git a/docs/advanced.md b/docs/advanced.md index c71ba413..b18be2dc 100644 --- a/docs/advanced.md +++ b/docs/advanced.md @@ -1919,6 +1919,58 @@ time. If you combine this with `--gtest_repeat=N`, googletest will pick a different random seed and re-shuffle the tests in each iteration. +### Distributing Test Functions to Multiple Machines + +If you have more than one machine you can use to run a test program, you might +want to run the test functions in parallel and get the result faster. We call +this technique *sharding*, where each machine is called a *shard*. + +GoogleTest is compatible with test sharding. To take advantage of this feature, +your test runner (not part of GoogleTest) needs to do the following: + +1. Allocate a number of machines (shards) to run the tests. +1. On each shard, set the `GTEST_TOTAL_SHARDS` environment variable to the total + number of shards. It must be the same for all shards. +1. On each shard, set the `GTEST_SHARD_INDEX` environment variable to the index + of the shard. Different shards must be assigned different indices, which + must be in the range `[0, GTEST_TOTAL_SHARDS - 1]`. +1. Run the same test program on all shards. When GoogleTest sees the above two + environment variables, it will select a subset of the test functions to run. + Across all shards, each test function in the program will be run exactly + once. +1. Wait for all shards to finish, then collect and report the results. + +Your project may have tests that were written without GoogleTest and thus don't +understand this protocol. In order for your test runner to figure out which test +supports sharding, it can set the environment variable `GTEST_SHARD_STATUS_FILE` +to a non-existent file path. If a test program supports sharding, it will create +this file to acknowledge that fact; otherwise it will not create it. The actual +contents of the file are not important at this time, although we may put some +useful information in it in the future. + +Here's an example to make it clear. Suppose you have a test program `foo_test` +that contains the following 5 test functions: + +``` +TEST(A, V) +TEST(A, W) +TEST(B, X) +TEST(B, Y) +TEST(B, Z) +``` + +Suppose you have 3 machines at your disposal. To run the test functions in +parallel, you would set `GTEST_TOTAL_SHARDS` to 3 on all machines, and set +`GTEST_SHARD_INDEX` to 0, 1, and 2 on the machines respectively. Then you would +run the same `foo_test` on each machine. + +GoogleTest reserves the right to change how the work is distributed across the +shards, but here's one possible scenario: + +* Machine #0 runs `A.V` and `B.X`. +* Machine #1 runs `A.W` and `B.Y`. +* Machine #2 runs `B.Z`. + ### Controlling Test Output #### Colored Terminal Output diff --git a/googlemock/test/gmock-internal-utils_test.cc b/googlemock/test/gmock-internal-utils_test.cc index 8722418e..fbb6a83d 100644 --- a/googlemock/test/gmock-internal-utils_test.cc +++ b/googlemock/test/gmock-internal-utils_test.cc @@ -394,7 +394,7 @@ TEST_F(LogIsVisibleTest, WorksWhenVerbosityIsWarning) { // and log severity. void TestLogWithSeverity(const std::string& verbosity, LogSeverity severity, bool should_print) { - const std::string old_flag = GMOCK_FLAG(verbose); + const std::string old_flag = GMOCK_FLAG_GET(verbose); GMOCK_FLAG_SET(verbose, verbosity); CaptureStdout(); Log(severity, "Test log.\n", 0); @@ -413,7 +413,7 @@ void TestLogWithSeverity(const std::string& verbosity, LogSeverity severity, // Tests that when the stack_frames_to_skip parameter is negative, // Log() doesn't include the stack trace in the output. TEST(LogTest, NoStackTraceWhenStackFramesToSkipIsNegative) { - const std::string saved_flag = GMOCK_FLAG(verbose); + const std::string saved_flag = GMOCK_FLAG_GET(verbose); GMOCK_FLAG_SET(verbose, kInfoVerbosity); CaptureStdout(); Log(kInfo, "Test log.\n", -1); @@ -499,7 +499,7 @@ TEST(LogTest, OnlyWarningsArePrintedWhenVerbosityIsInvalid) { // Verifies that Log() behaves correctly for the given verbosity level // and log severity. std::string GrabOutput(void(*logger)(), const char* verbosity) { - const std::string saved_flag = GMOCK_FLAG(verbose); + const std::string saved_flag = GMOCK_FLAG_GET(verbose); GMOCK_FLAG_SET(verbose, verbosity); CaptureStdout(); logger(); diff --git a/googletest/include/gtest/gtest.h b/googletest/include/gtest/gtest.h index 3d04b897..cdde1aa4 100644 --- a/googletest/include/gtest/gtest.h +++ b/googletest/include/gtest/gtest.h @@ -1123,6 +1123,9 @@ class TestEventListener { // Fired before the test starts. virtual void OnTestStart(const TestInfo& test_info) = 0; + // Fired when a test is disabled + virtual void OnTestDisabled(const TestInfo& /*test_info*/) {} + // Fired after a failed assertion or a SUCCEED() invocation. // If you want to throw an exception from this function to skip to the next // TEST, it must be AssertionException defined above, or inherited from it. @@ -1172,6 +1175,7 @@ class EmptyTestEventListener : public TestEventListener { #endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ void OnTestStart(const TestInfo& /*test_info*/) override {} + void OnTestDisabled(const TestInfo& /*test_info*/) override {} void OnTestPartResult(const TestPartResult& /*test_part_result*/) override {} void OnTestEnd(const TestInfo& /*test_info*/) override {} void OnTestSuiteEnd(const TestSuite& /*test_suite*/) override {} diff --git a/googletest/include/gtest/internal/gtest-port.h b/googletest/include/gtest/internal/gtest-port.h index d3eab57b..cc6fa6b3 100644 --- a/googletest/include/gtest/internal/gtest-port.h +++ b/googletest/include/gtest/internal/gtest-port.h @@ -260,9 +260,17 @@ #include #include +// #include // Guarded by GTEST_IS_THREADSAFE below #include +#include #include +#include +#include +#include +// #include // Guarded by GTEST_IS_THREADSAFE below +#include #include +#include #ifndef _WIN32_WCE # include @@ -274,13 +282,6 @@ # include #endif -#include // NOLINT -#include -#include -#include // NOLINT -#include -#include // NOLINT - #include "gtest/internal/custom/gtest-port.h" #include "gtest/internal/gtest-port-arch.h" @@ -757,6 +758,12 @@ typedef struct _RTL_CRITICAL_SECTION GTEST_CRITICAL_SECTION; #endif // GTEST_IS_THREADSAFE +#if GTEST_IS_THREADSAFE +// Some platforms don't support including these threading related headers. +#include // NOLINT +#include // NOLINT +#endif // GTEST_IS_THREADSAFE + // GTEST_API_ qualifies all symbols that must be exported. The definitions below // are guarded by #ifndef to give embedders a chance to define GTEST_API_ in // gtest/internal/custom/gtest-port.h @@ -1161,71 +1168,8 @@ void ClearInjectableArgvs(); // Defines synchronization primitives. #if GTEST_IS_THREADSAFE -# if GTEST_HAS_PTHREAD -// Sleeps for (roughly) n milliseconds. This function is only for testing -// Google Test's own constructs. Don't use it in user tests, either -// directly or indirectly. -inline void SleepMilliseconds(int n) { - const timespec time = { - 0, // 0 seconds. - n * 1000L * 1000L, // And n ms. - }; - nanosleep(&time, nullptr); -} -# endif // GTEST_HAS_PTHREAD - -# if GTEST_HAS_NOTIFICATION_ -// Notification has already been imported into the namespace. -// Nothing to do here. - -# elif GTEST_HAS_PTHREAD -// Allows a controller thread to pause execution of newly created -// threads until notified. Instances of this class must be created -// and destroyed in the controller thread. -// -// This class is only for testing Google Test's own constructs. Do not -// use it in user tests, either directly or indirectly. -class Notification { - public: - Notification() : notified_(false) { - GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, nullptr)); - } - ~Notification() { - pthread_mutex_destroy(&mutex_); - } - - // Notifies all threads created with this notification to start. Must - // be called from the controller thread. - void Notify() { - pthread_mutex_lock(&mutex_); - notified_ = true; - pthread_mutex_unlock(&mutex_); - } - - // Blocks until the controller thread notifies. Must be called from a test - // thread. - void WaitForNotification() { - for (;;) { - pthread_mutex_lock(&mutex_); - const bool notified = notified_; - pthread_mutex_unlock(&mutex_); - if (notified) - break; - SleepMilliseconds(10); - } - } - - private: - pthread_mutex_t mutex_; - bool notified_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(Notification); -}; - -# elif GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT - -GTEST_API_ void SleepMilliseconds(int n); +# if GTEST_OS_WINDOWS // Provides leak-safe Windows kernel handle ownership. // Used in death tests and in threading support. class GTEST_API_ AutoHandle { @@ -1254,23 +1198,45 @@ class GTEST_API_ AutoHandle { GTEST_DISALLOW_COPY_AND_ASSIGN_(AutoHandle); }; +# endif +# if GTEST_HAS_NOTIFICATION_ +// Notification has already been imported into the namespace. +// Nothing to do here. + +# else // Allows a controller thread to pause execution of newly created // threads until notified. Instances of this class must be created // and destroyed in the controller thread. // // This class is only for testing Google Test's own constructs. Do not // use it in user tests, either directly or indirectly. +// TODO(b/203539622): Replace unconditionally with absl::Notification. class GTEST_API_ Notification { public: - Notification(); - void Notify(); - void WaitForNotification(); + Notification() : notified_(false) {} + Notification(const Notification&) = delete; + Notification& operator=(const Notification&) = delete; + + // Notifies all threads created with this notification to start. Must + // be called from the controller thread. + void Notify() { + std::lock_guard lock(mu_); + notified_ = true; + cv_.notify_all(); + } + + // Blocks until the controller thread notifies. Must be called from a test + // thread. + void WaitForNotification() { + std::unique_lock lock(mu_); + cv_.wait(lock, [this]() { return notified_; }); + } private: - AutoHandle event_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(Notification); + std::mutex mu_; + std::condition_variable cv_; + bool notified_; }; # endif // GTEST_HAS_NOTIFICATION_ diff --git a/googletest/src/gtest-port.cc b/googletest/src/gtest-port.cc index c3c93e61..f63625b5 100644 --- a/googletest/src/gtest-port.cc +++ b/googletest/src/gtest-port.cc @@ -280,10 +280,6 @@ size_t GetThreadCount() { #if GTEST_IS_THREADSAFE && GTEST_OS_WINDOWS -void SleepMilliseconds(int n) { - ::Sleep(static_cast(n)); -} - AutoHandle::AutoHandle() : handle_(INVALID_HANDLE_VALUE) {} @@ -322,23 +318,6 @@ bool AutoHandle::IsCloseable() const { return handle_ != nullptr && handle_ != INVALID_HANDLE_VALUE; } -Notification::Notification() - : event_(::CreateEvent(nullptr, // Default security attributes. - TRUE, // Do not reset automatically. - FALSE, // Initially unset. - nullptr)) { // Anonymous event. - GTEST_CHECK_(event_.Get() != nullptr); -} - -void Notification::Notify() { - GTEST_CHECK_(::SetEvent(event_.Get()) != FALSE); -} - -void Notification::WaitForNotification() { - GTEST_CHECK_( - ::WaitForSingleObject(event_.Get(), INFINITE) == WAIT_OBJECT_0); -} - Mutex::Mutex() : owner_thread_id_(0), type_(kDynamic), diff --git a/googletest/src/gtest.cc b/googletest/src/gtest.cc index 384b1716..82e223be 100644 --- a/googletest/src/gtest.cc +++ b/googletest/src/gtest.cc @@ -2855,20 +2855,20 @@ void UnitTestImpl::RegisterParameterizedTests() { // Creates the test object, runs it, records its result, and then // deletes it. void TestInfo::Run() { - if (!should_run_) return; + TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater(); + if (!should_run_) { + if (is_disabled_) repeater->OnTestDisabled(*this); + return; + } // Tells UnitTest where to store test result. internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); impl->set_current_test_info(this); - TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater(); - // Notifies the unit test event listeners that a test is about to start. repeater->OnTestStart(*this); - result_.set_start_timestamp(internal::GetTimeInMillis()); internal::Timer timer; - impl->os_stack_trace_getter()->UponLeavingGTest(); // Creates the test object. @@ -3398,6 +3398,7 @@ class PrettyUnitTestResultPrinter : public TestEventListener { #endif // OnTestCaseStart void OnTestStart(const TestInfo& test_info) override; + void OnTestDisabled(const TestInfo& test_info) override; void OnTestPartResult(const TestPartResult& result) override; void OnTestEnd(const TestInfo& test_info) override; @@ -3497,6 +3498,13 @@ void PrettyUnitTestResultPrinter::OnTestStart(const TestInfo& test_info) { fflush(stdout); } +void PrettyUnitTestResultPrinter::OnTestDisabled(const TestInfo& test_info) { + ColoredPrintf(GTestColor::kYellow, "[ DISABLED ] "); + PrintTestName(test_info.test_suite_name(), test_info.name()); + printf("\n"); + fflush(stdout); +} + // Called after an assertion failure. void PrettyUnitTestResultPrinter::OnTestPartResult( const TestPartResult& result) { @@ -3705,6 +3713,7 @@ class BriefUnitTestResultPrinter : public TestEventListener { #endif // OnTestCaseStart void OnTestStart(const TestInfo& /*test_info*/) override {} + void OnTestDisabled(const TestInfo& /*test_info*/) override {} void OnTestPartResult(const TestPartResult& result) override; void OnTestEnd(const TestInfo& test_info) override; @@ -3817,6 +3826,7 @@ class TestEventRepeater : public TestEventListener { #endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ void OnTestSuiteStart(const TestSuite& parameter) override; void OnTestStart(const TestInfo& test_info) override; + void OnTestDisabled(const TestInfo& test_info) override; void OnTestPartResult(const TestPartResult& result) override; void OnTestEnd(const TestInfo& test_info) override; // Legacy API is deprecated but still available @@ -3887,6 +3897,7 @@ GTEST_REPEATER_METHOD_(OnTestCaseStart, TestSuite) #endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ GTEST_REPEATER_METHOD_(OnTestSuiteStart, TestSuite) GTEST_REPEATER_METHOD_(OnTestStart, TestInfo) +GTEST_REPEATER_METHOD_(OnTestDisabled, TestInfo) GTEST_REPEATER_METHOD_(OnTestPartResult, TestPartResult) GTEST_REPEATER_METHOD_(OnEnvironmentsTearDownStart, UnitTest) GTEST_REVERSE_REPEATER_METHOD_(OnEnvironmentsSetUpEnd, UnitTest) diff --git a/googletest/test/googletest-port-test.cc b/googletest/test/googletest-port-test.cc index 16d30c46..b14e1f76 100644 --- a/googletest/test/googletest-port-test.cc +++ b/googletest/test/googletest-port-test.cc @@ -36,8 +36,10 @@ # include #endif // GTEST_OS_MAC +#include // NOLINT #include #include +#include // NOLINT #include // For std::pair and std::make_pair. #include @@ -333,7 +335,7 @@ TEST(GetThreadCountTest, ReturnsCorrectValue) { break; } - SleepMilliseconds(100); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); } // Retry if an arbitrary other thread was created or destroyed. @@ -1050,7 +1052,7 @@ class AtomicCounterWithMutex { int temp = value_; { // We need to put up a memory barrier to prevent reads and writes to - // value_ rearranged with the call to SleepMilliseconds when observed + // value_ rearranged with the call to sleep_for when observed // from other threads. #if GTEST_HAS_PTHREAD // On POSIX, locking a mutex puts up a memory barrier. We cannot use @@ -1061,7 +1063,8 @@ class AtomicCounterWithMutex { pthread_mutex_init(&memory_barrier_mutex, nullptr)); GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_lock(&memory_barrier_mutex)); - SleepMilliseconds(static_cast(random_.Generate(30))); + std::this_thread::sleep_for( + std::chrono::milliseconds(random_.Generate(30))); GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_unlock(&memory_barrier_mutex)); GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_destroy(&memory_barrier_mutex)); @@ -1069,7 +1072,8 @@ class AtomicCounterWithMutex { // On Windows, performing an interlocked access puts up a memory barrier. volatile LONG dummy = 0; ::InterlockedIncrement(&dummy); - SleepMilliseconds(static_cast(random_.Generate(30))); + std::this_thread::sleep_for( + std::chrono::milliseconds(random_.Generate(30))); ::InterlockedIncrement(&dummy); #else # error "Memory barrier not implemented on this platform." diff --git a/googletest/test/gtest_test_utils.py b/googletest/test/gtest_test_utils.py index 13fc6f59..eecc5334 100755 --- a/googletest/test/gtest_test_utils.py +++ b/googletest/test/gtest_test_utils.py @@ -32,6 +32,7 @@ # pylint: disable-msg=C6204 import os +import subprocess import sys IS_WINDOWS = os.name == 'nt' @@ -42,13 +43,6 @@ import atexit import shutil import tempfile import unittest as _test_module - -try: - import subprocess - _SUBPROCESS_MODULE_AVAILABLE = True -except: - import popen2 - _SUBPROCESS_MODULE_AVAILABLE = False # pylint: enable-msg=C6204 GTEST_OUTPUT_VAR_NAME = 'GTEST_OUTPUT' @@ -224,69 +218,18 @@ class Subprocess: combined in a string. """ - # The subprocess module is the preferable way of running programs - # since it is available and behaves consistently on all platforms, - # including Windows. But it is only available starting in python 2.4. - # In earlier python versions, we revert to the popen2 module, which is - # available in python 2.0 and later but doesn't provide required - # functionality (Popen4) under Windows. This allows us to support Mac - # OS X 10.4 Tiger, which has python 2.3 installed. - if _SUBPROCESS_MODULE_AVAILABLE: - if capture_stderr: - stderr = subprocess.STDOUT - else: - stderr = subprocess.PIPE - - p = subprocess.Popen(command, - stdout=subprocess.PIPE, stderr=stderr, - cwd=working_dir, universal_newlines=True, env=env) - # communicate returns a tuple with the file object for the child's - # output. - self.output = p.communicate()[0] - self._return_code = p.returncode + if capture_stderr: + stderr = subprocess.STDOUT else: - old_dir = os.getcwd() + stderr = subprocess.PIPE - def _ReplaceEnvDict(dest, src): - # Changes made by os.environ.clear are not inheritable by child - # processes until Python 2.6. To produce inheritable changes we have - # to delete environment items with the del statement. - for key in dest.keys(): - del dest[key] - dest.update(src) - - # When 'env' is not None, backup the environment variables and replace - # them with the passed 'env'. When 'env' is None, we simply use the - # current 'os.environ' for compatibility with the subprocess.Popen - # semantics used above. - if env is not None: - old_environ = os.environ.copy() - _ReplaceEnvDict(os.environ, env) - - try: - if working_dir is not None: - os.chdir(working_dir) - if capture_stderr: - p = popen2.Popen4(command) - else: - p = popen2.Popen3(command) - p.tochild.close() - self.output = p.fromchild.read() - ret_code = p.wait() - finally: - os.chdir(old_dir) - - # Restore the old environment variables - # if they were replaced. - if env is not None: - _ReplaceEnvDict(os.environ, old_environ) - - # Converts ret_code to match the semantics of - # subprocess.Popen.returncode. - if os.WIFSIGNALED(ret_code): - self._return_code = -os.WTERMSIG(ret_code) - else: # os.WIFEXITED(ret_code) should return True here. - self._return_code = os.WEXITSTATUS(ret_code) + p = subprocess.Popen(command, + stdout=subprocess.PIPE, stderr=stderr, + cwd=working_dir, universal_newlines=True, env=env) + # communicate returns a tuple with the file object for the child's + # output. + self.output = p.communicate()[0] + self._return_code = p.returncode if bool(self._return_code & 0x80000000): self.terminated_by_signal = True