From 2534ae201e47986d36d5fab0e523a7f046b8ec1e Mon Sep 17 00:00:00 2001 From: "zhanyong.wan" Date: Mon, 21 Sep 2009 19:42:03 +0000 Subject: Adds a Random class to support --gtest_shuffle (by Josh Kelley); Makes the scons script build in a deterministic order (by Zhanyong Wan). --- include/gtest/internal/gtest-internal.h | 22 +++++++++++++++++ scons/SConstruct.common | 5 ---- src/gtest.cc | 21 +++++++++++++++- test/gtest_unittest.cc | 44 +++++++++++++++++++++++++++++++++ 4 files changed, 86 insertions(+), 6 deletions(-) diff --git a/include/gtest/internal/gtest-internal.h b/include/gtest/internal/gtest-internal.h index 4775524..f5c30fb 100644 --- a/include/gtest/internal/gtest-internal.h +++ b/include/gtest/internal/gtest-internal.h @@ -748,6 +748,28 @@ String GetCurrentOsStackTraceExceptTop(UnitTest* unit_test, int skip_count); // A helper for suppressing warnings on unreachable code in some macros. bool AlwaysTrue(); +// A simple Linear Congruential Generator for generating random +// numbers with a uniform distribution. Unlike rand() and srand(), it +// doesn't use global state (and therefore can't interfere with user +// code). Unlike rand_r(), it's portable. An LCG isn't very random, +// but it's good enough for our purposes. +class Random { + public: + static const UInt32 kMaxRange = 1u << 31; + + explicit Random(UInt32 seed) : state_(seed) {} + + void Reseed(UInt32 seed) { state_ = seed; } + + // Generates a random number from [0, range). Crashes if 'range' is + // 0 or greater than kMaxRange. + UInt32 Generate(UInt32 range); + + private: + UInt32 state_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(Random); +}; + } // namespace internal } // namespace testing diff --git a/scons/SConstruct.common b/scons/SConstruct.common index 193fab2..f849d72 100644 --- a/scons/SConstruct.common +++ b/scons/SConstruct.common @@ -155,11 +155,6 @@ class SConstructHelper: '/OPT:NOICF', ] ) - # Tell SCons to build depdendencies in random order (apart from the - # actual dependency order). This helps ensure we don't introduce - # build files that "accidentally" work sometimes (e.g. when you are - # building some targets) and not other times. - debug_env.SetOption('random', 1) return debug_env def MakeWinOptimizedEnvironment(self, base_environment, name): diff --git a/src/gtest.cc b/src/gtest.cc index bb5ffac..30652fe 100644 --- a/src/gtest.cc +++ b/src/gtest.cc @@ -260,6 +260,25 @@ GTEST_DEFINE_bool_( namespace internal { +// Generates a random number from [0, range), using a Linear +// Congruential Generator (LCG). Crashes if 'range' is 0 or greater +// than kMaxRange. +UInt32 Random::Generate(UInt32 range) { + // These constants are the same as are used in glibc's rand(3). + state_ = (1103515245U*state_ + 12345U) % kMaxRange; + + GTEST_CHECK_(range > 0) + << "Cannot generate a number in the range [0, 0)."; + GTEST_CHECK_(range <= kMaxRange) + << "Generation of a number in [0, " << range << ") was requested, " + << "but this can only generate numbers in [0, " << kMaxRange << ")."; + + // Converting via modulus introduces a bit of downward bias, but + // it's simple, and a linear congruential generator isn't too good + // to begin with. + return state_ % range; +} + // g_help_flag is true iff the --help flag or an equivalent form is // specified on the command line. static bool g_help_flag = false; @@ -3815,7 +3834,7 @@ static void TearDownEnvironment(Environment* env) { env->TearDown(); } // considered to be failed, but the rest of the tests will still be // run. (We disable exceptions on Linux and Mac OS X, so the issue // doesn't apply there.) -// When parameterized tests are enabled, it explands and registers +// When parameterized tests are enabled, it expands and registers // parameterized tests first in RegisterParameterizedTests(). // All other functions called from RunAllTests() may safely assume that // parameterized tests are ready to be counted and run. diff --git a/test/gtest_unittest.cc b/test/gtest_unittest.cc index 3b1457d..d5315c4 100644 --- a/test/gtest_unittest.cc +++ b/test/gtest_unittest.cc @@ -183,6 +183,7 @@ using testing::internal::TestProperty; using testing::internal::TestResult; using testing::internal::TestResultAccessor; using testing::internal::ThreadLocal; +using testing::internal::UInt32; using testing::internal::Vector; using testing::internal::WideStringToUtf8; using testing::internal::kTestTypeIdInGoogleTest; @@ -499,6 +500,49 @@ TEST(WideStringToUtf8Test, ConcatenatesCodepointsCorrectly) { } #endif // !GTEST_WIDE_STRING_USES_UTF16_ +// Tests the Random class. + +TEST(RandomDeathTest, GeneratesCrashesOnInvalidRange) { + testing::internal::Random random(42); + EXPECT_DEATH_IF_SUPPORTED( + random.Generate(0), + "Cannot generate a number in the range \\[0, 0\\)"); + EXPECT_DEATH_IF_SUPPORTED( + random.Generate(testing::internal::Random::kMaxRange + 1), + "Generation of a number in \\[0, 2147483649\\) was requested, " + "but this can only generate numbers in \\[0, 2147483648\\)"); +} + +TEST(RandomTest, GeneratesNumbersWithinRange) { + const UInt32 kRange = 10000; + testing::internal::Random random(12345); + for (int i = 0; i < 10; i++) { + EXPECT_LT(random.Generate(kRange), kRange) << " for iteration " << i; + } + + testing::internal::Random random2(testing::internal::Random::kMaxRange); + for (int i = 0; i < 10; i++) { + EXPECT_LT(random2.Generate(kRange), kRange) << " for iteration " << i; + } +} + +TEST(RandomTest, RepeatsWhenReseeded) { + const int kSeed = 123; + const int kArraySize = 10; + const UInt32 kRange = 10000; + UInt32 values[kArraySize]; + + testing::internal::Random random(kSeed); + for (int i = 0; i < kArraySize; i++) { + values[i] = random.Generate(kRange); + } + + random.Reseed(kSeed); + for (int i = 0; i < kArraySize; i++) { + EXPECT_EQ(values[i], random.Generate(kRange)) << " for iteration " << i; + } +} + // Tests the Vector class template. // Tests Vector::Clear(). -- cgit v0.12