diff options
-rw-r--r-- | docs/advanced.md | 52 | ||||
-rw-r--r-- | googlemock/test/gmock-internal-utils_test.cc | 6 | ||||
-rw-r--r-- | googletest/include/gtest/gtest.h | 6 | ||||
-rw-r--r-- | googletest/include/gtest/internal/gtest-port.h | 120 | ||||
-rw-r--r-- | googletest/src/gtest-port.cc | 21 | ||||
-rw-r--r-- | googletest/src/gtest.cc | 13 | ||||
-rw-r--r-- | googletest/test/googletest-port-test.cc | 12 | ||||
-rwxr-xr-x | googletest/test/gtest_test_utils.py | 81 |
8 files changed, 127 insertions, 184 deletions
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 fe9b4332..db4c6203 100644 --- a/googletest/include/gtest/gtest.h +++ b/googletest/include/gtest/gtest.h @@ -1123,8 +1123,8 @@ class TestEventListener { // Fired before the test starts. virtual void OnTestStart(const TestInfo& test_info) = 0; - // Fired when skipping a test - virtual void OnTestSkipped(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 @@ -1175,7 +1175,7 @@ class EmptyTestEventListener : public TestEventListener { #endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ void OnTestStart(const TestInfo& /*test_info*/) override {} - void OnTestSkipped(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 <string.h> #include <cerrno> +// #include <condition_variable> // Guarded by GTEST_IS_THREADSAFE below #include <cstdint> +#include <iostream> #include <limits> +#include <locale> +#include <memory> +#include <string> +// #include <mutex> // Guarded by GTEST_IS_THREADSAFE below +#include <tuple> #include <type_traits> +#include <vector> #ifndef _WIN32_WCE # include <sys/types.h> @@ -274,13 +282,6 @@ # include <TargetConditionals.h> #endif -#include <iostream> // NOLINT -#include <locale> -#include <memory> -#include <string> // NOLINT -#include <tuple> -#include <vector> // 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 <condition_variable> // NOLINT +#include <mutex> // 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; - private: - AutoHandle event_; + // Notifies all threads created with this notification to start. Must + // be called from the controller thread. + void Notify() { + std::lock_guard<std::mutex> 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<std::mutex> lock(mu_); + cv_.wait(lock, [this]() { return notified_; }); + } - GTEST_DISALLOW_COPY_AND_ASSIGN_(Notification); + private: + 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<DWORD>(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 5d654de3..3eb9505f 100644 --- a/googletest/src/gtest.cc +++ b/googletest/src/gtest.cc @@ -2857,8 +2857,7 @@ void UnitTestImpl::RegisterParameterizedTests() { void TestInfo::Run() { TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater(); if (!should_run_) { - if (is_disabled_) - repeater->OnTestSkipped(*this); + if (is_disabled_) repeater->OnTestDisabled(*this); return; } @@ -3397,7 +3396,7 @@ class PrettyUnitTestResultPrinter : public TestEventListener { #endif // OnTestCaseStart void OnTestStart(const TestInfo& test_info) override; - void OnTestSkipped(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,7 +3496,7 @@ void PrettyUnitTestResultPrinter::OnTestStart(const TestInfo& test_info) { fflush(stdout); } -void PrettyUnitTestResultPrinter::OnTestSkipped(const TestInfo& test_info) { +void PrettyUnitTestResultPrinter::OnTestDisabled(const TestInfo& test_info) { ColoredPrintf(GTestColor::kYellow, "[ DISABLED ] "); PrintTestName(test_info.test_suite_name(), test_info.name()); printf("\n"); @@ -3706,7 +3705,7 @@ class BriefUnitTestResultPrinter : public TestEventListener { #endif // OnTestCaseStart void OnTestStart(const TestInfo& /*test_info*/) override {} - void OnTestSkipped(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; @@ -3813,7 +3812,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 OnTestSkipped(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 @@ -3884,7 +3883,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_(OnTestSkipped, 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 <time.h> #endif // GTEST_OS_MAC +#include <chrono> // NOLINT #include <list> #include <memory> +#include <thread> // NOLINT #include <utility> // For std::pair and std::make_pair. #include <vector> @@ -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<int>(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<int>(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() - - 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) + 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 bool(self._return_code & 0x80000000): self.terminated_by_signal = True |