michael@0: // Copyright 2008, Google Inc. michael@0: // All rights reserved. michael@0: // michael@0: // Redistribution and use in source and binary forms, with or without michael@0: // modification, are permitted provided that the following conditions are michael@0: // met: michael@0: // michael@0: // * Redistributions of source code must retain the above copyright michael@0: // notice, this list of conditions and the following disclaimer. michael@0: // * Redistributions in binary form must reproduce the above michael@0: // copyright notice, this list of conditions and the following disclaimer michael@0: // in the documentation and/or other materials provided with the michael@0: // distribution. michael@0: // * Neither the name of Google Inc. nor the names of its michael@0: // contributors may be used to endorse or promote products derived from michael@0: // this software without specific prior written permission. michael@0: // michael@0: // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS michael@0: // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT michael@0: // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR michael@0: // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT michael@0: // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, michael@0: // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT michael@0: // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, michael@0: // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY michael@0: // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE michael@0: // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: // michael@0: // Authors: vladl@google.com (Vlad Losev), wan@google.com (Zhanyong Wan) michael@0: // michael@0: // This file tests the internal cross-platform support utilities. michael@0: michael@0: #include "gtest/internal/gtest-port.h" michael@0: michael@0: #include michael@0: michael@0: #if GTEST_OS_MAC michael@0: # include michael@0: #endif // GTEST_OS_MAC michael@0: michael@0: #include michael@0: #include // For std::pair and std::make_pair. michael@0: #include michael@0: michael@0: #include "gtest/gtest.h" michael@0: #include "gtest/gtest-spi.h" michael@0: michael@0: // Indicates that this translation unit is part of Google Test's michael@0: // implementation. It must come before gtest-internal-inl.h is michael@0: // included, or there will be a compiler error. This trick is to michael@0: // prevent a user from accidentally including gtest-internal-inl.h in michael@0: // his code. michael@0: #define GTEST_IMPLEMENTATION_ 1 michael@0: #include "src/gtest-internal-inl.h" michael@0: #undef GTEST_IMPLEMENTATION_ michael@0: michael@0: using std::make_pair; michael@0: using std::pair; michael@0: michael@0: namespace testing { michael@0: namespace internal { michael@0: michael@0: TEST(IsXDigitTest, WorksForNarrowAscii) { michael@0: EXPECT_TRUE(IsXDigit('0')); michael@0: EXPECT_TRUE(IsXDigit('9')); michael@0: EXPECT_TRUE(IsXDigit('A')); michael@0: EXPECT_TRUE(IsXDigit('F')); michael@0: EXPECT_TRUE(IsXDigit('a')); michael@0: EXPECT_TRUE(IsXDigit('f')); michael@0: michael@0: EXPECT_FALSE(IsXDigit('-')); michael@0: EXPECT_FALSE(IsXDigit('g')); michael@0: EXPECT_FALSE(IsXDigit('G')); michael@0: } michael@0: michael@0: TEST(IsXDigitTest, ReturnsFalseForNarrowNonAscii) { michael@0: EXPECT_FALSE(IsXDigit(static_cast(0x80))); michael@0: EXPECT_FALSE(IsXDigit(static_cast('0' | 0x80))); michael@0: } michael@0: michael@0: TEST(IsXDigitTest, WorksForWideAscii) { michael@0: EXPECT_TRUE(IsXDigit(L'0')); michael@0: EXPECT_TRUE(IsXDigit(L'9')); michael@0: EXPECT_TRUE(IsXDigit(L'A')); michael@0: EXPECT_TRUE(IsXDigit(L'F')); michael@0: EXPECT_TRUE(IsXDigit(L'a')); michael@0: EXPECT_TRUE(IsXDigit(L'f')); michael@0: michael@0: EXPECT_FALSE(IsXDigit(L'-')); michael@0: EXPECT_FALSE(IsXDigit(L'g')); michael@0: EXPECT_FALSE(IsXDigit(L'G')); michael@0: } michael@0: michael@0: TEST(IsXDigitTest, ReturnsFalseForWideNonAscii) { michael@0: EXPECT_FALSE(IsXDigit(static_cast(0x80))); michael@0: EXPECT_FALSE(IsXDigit(static_cast(L'0' | 0x80))); michael@0: EXPECT_FALSE(IsXDigit(static_cast(L'0' | 0x100))); michael@0: } michael@0: michael@0: class Base { michael@0: public: michael@0: // Copy constructor and assignment operator do exactly what we need, so we michael@0: // use them. michael@0: Base() : member_(0) {} michael@0: explicit Base(int n) : member_(n) {} michael@0: virtual ~Base() {} michael@0: int member() { return member_; } michael@0: michael@0: private: michael@0: int member_; michael@0: }; michael@0: michael@0: class Derived : public Base { michael@0: public: michael@0: explicit Derived(int n) : Base(n) {} michael@0: }; michael@0: michael@0: TEST(ImplicitCastTest, ConvertsPointers) { michael@0: Derived derived(0); michael@0: EXPECT_TRUE(&derived == ::testing::internal::ImplicitCast_(&derived)); michael@0: } michael@0: michael@0: TEST(ImplicitCastTest, CanUseInheritance) { michael@0: Derived derived(1); michael@0: Base base = ::testing::internal::ImplicitCast_(derived); michael@0: EXPECT_EQ(derived.member(), base.member()); michael@0: } michael@0: michael@0: class Castable { michael@0: public: michael@0: explicit Castable(bool* converted) : converted_(converted) {} michael@0: operator Base() { michael@0: *converted_ = true; michael@0: return Base(); michael@0: } michael@0: michael@0: private: michael@0: bool* converted_; michael@0: }; michael@0: michael@0: TEST(ImplicitCastTest, CanUseNonConstCastOperator) { michael@0: bool converted = false; michael@0: Castable castable(&converted); michael@0: Base base = ::testing::internal::ImplicitCast_(castable); michael@0: EXPECT_TRUE(converted); michael@0: } michael@0: michael@0: class ConstCastable { michael@0: public: michael@0: explicit ConstCastable(bool* converted) : converted_(converted) {} michael@0: operator Base() const { michael@0: *converted_ = true; michael@0: return Base(); michael@0: } michael@0: michael@0: private: michael@0: bool* converted_; michael@0: }; michael@0: michael@0: TEST(ImplicitCastTest, CanUseConstCastOperatorOnConstValues) { michael@0: bool converted = false; michael@0: const ConstCastable const_castable(&converted); michael@0: Base base = ::testing::internal::ImplicitCast_(const_castable); michael@0: EXPECT_TRUE(converted); michael@0: } michael@0: michael@0: class ConstAndNonConstCastable { michael@0: public: michael@0: ConstAndNonConstCastable(bool* converted, bool* const_converted) michael@0: : converted_(converted), const_converted_(const_converted) {} michael@0: operator Base() { michael@0: *converted_ = true; michael@0: return Base(); michael@0: } michael@0: operator Base() const { michael@0: *const_converted_ = true; michael@0: return Base(); michael@0: } michael@0: michael@0: private: michael@0: bool* converted_; michael@0: bool* const_converted_; michael@0: }; michael@0: michael@0: TEST(ImplicitCastTest, CanSelectBetweenConstAndNonConstCasrAppropriately) { michael@0: bool converted = false; michael@0: bool const_converted = false; michael@0: ConstAndNonConstCastable castable(&converted, &const_converted); michael@0: Base base = ::testing::internal::ImplicitCast_(castable); michael@0: EXPECT_TRUE(converted); michael@0: EXPECT_FALSE(const_converted); michael@0: michael@0: converted = false; michael@0: const_converted = false; michael@0: const ConstAndNonConstCastable const_castable(&converted, &const_converted); michael@0: base = ::testing::internal::ImplicitCast_(const_castable); michael@0: EXPECT_FALSE(converted); michael@0: EXPECT_TRUE(const_converted); michael@0: } michael@0: michael@0: class To { michael@0: public: michael@0: To(bool* converted) { *converted = true; } // NOLINT michael@0: }; michael@0: michael@0: TEST(ImplicitCastTest, CanUseImplicitConstructor) { michael@0: bool converted = false; michael@0: To to = ::testing::internal::ImplicitCast_(&converted); michael@0: (void)to; michael@0: EXPECT_TRUE(converted); michael@0: } michael@0: michael@0: TEST(IteratorTraitsTest, WorksForSTLContainerIterators) { michael@0: StaticAssertTypeEq::const_iterator>::value_type>(); michael@0: StaticAssertTypeEq::iterator>::value_type>(); michael@0: } michael@0: michael@0: TEST(IteratorTraitsTest, WorksForPointerToNonConst) { michael@0: StaticAssertTypeEq::value_type>(); michael@0: StaticAssertTypeEq::value_type>(); michael@0: } michael@0: michael@0: TEST(IteratorTraitsTest, WorksForPointerToConst) { michael@0: StaticAssertTypeEq::value_type>(); michael@0: StaticAssertTypeEq::value_type>(); michael@0: } michael@0: michael@0: // Tests that the element_type typedef is available in scoped_ptr and refers michael@0: // to the parameter type. michael@0: TEST(ScopedPtrTest, DefinesElementType) { michael@0: StaticAssertTypeEq::element_type>(); michael@0: } michael@0: michael@0: // TODO(vladl@google.com): Implement THE REST of scoped_ptr tests. michael@0: michael@0: TEST(GtestCheckSyntaxTest, BehavesLikeASingleStatement) { michael@0: if (AlwaysFalse()) michael@0: GTEST_CHECK_(false) << "This should never be executed; " michael@0: "It's a compilation test only."; michael@0: michael@0: if (AlwaysTrue()) michael@0: GTEST_CHECK_(true); michael@0: else michael@0: ; // NOLINT michael@0: michael@0: if (AlwaysFalse()) michael@0: ; // NOLINT michael@0: else michael@0: GTEST_CHECK_(true) << ""; michael@0: } michael@0: michael@0: TEST(GtestCheckSyntaxTest, WorksWithSwitch) { michael@0: switch (0) { michael@0: case 1: michael@0: break; michael@0: default: michael@0: GTEST_CHECK_(true); michael@0: } michael@0: michael@0: switch (0) michael@0: case 0: michael@0: GTEST_CHECK_(true) << "Check failed in switch case"; michael@0: } michael@0: michael@0: // Verifies behavior of FormatFileLocation. michael@0: TEST(FormatFileLocationTest, FormatsFileLocation) { michael@0: EXPECT_PRED_FORMAT2(IsSubstring, "foo.cc", FormatFileLocation("foo.cc", 42)); michael@0: EXPECT_PRED_FORMAT2(IsSubstring, "42", FormatFileLocation("foo.cc", 42)); michael@0: } michael@0: michael@0: TEST(FormatFileLocationTest, FormatsUnknownFile) { michael@0: EXPECT_PRED_FORMAT2( michael@0: IsSubstring, "unknown file", FormatFileLocation(NULL, 42)); michael@0: EXPECT_PRED_FORMAT2(IsSubstring, "42", FormatFileLocation(NULL, 42)); michael@0: } michael@0: michael@0: TEST(FormatFileLocationTest, FormatsUknownLine) { michael@0: EXPECT_EQ("foo.cc:", FormatFileLocation("foo.cc", -1)); michael@0: } michael@0: michael@0: TEST(FormatFileLocationTest, FormatsUknownFileAndLine) { michael@0: EXPECT_EQ("unknown file:", FormatFileLocation(NULL, -1)); michael@0: } michael@0: michael@0: // Verifies behavior of FormatCompilerIndependentFileLocation. michael@0: TEST(FormatCompilerIndependentFileLocationTest, FormatsFileLocation) { michael@0: EXPECT_EQ("foo.cc:42", FormatCompilerIndependentFileLocation("foo.cc", 42)); michael@0: } michael@0: michael@0: TEST(FormatCompilerIndependentFileLocationTest, FormatsUknownFile) { michael@0: EXPECT_EQ("unknown file:42", michael@0: FormatCompilerIndependentFileLocation(NULL, 42)); michael@0: } michael@0: michael@0: TEST(FormatCompilerIndependentFileLocationTest, FormatsUknownLine) { michael@0: EXPECT_EQ("foo.cc", FormatCompilerIndependentFileLocation("foo.cc", -1)); michael@0: } michael@0: michael@0: TEST(FormatCompilerIndependentFileLocationTest, FormatsUknownFileAndLine) { michael@0: EXPECT_EQ("unknown file", FormatCompilerIndependentFileLocation(NULL, -1)); michael@0: } michael@0: michael@0: #if GTEST_OS_MAC || GTEST_OS_QNX michael@0: void* ThreadFunc(void* data) { michael@0: pthread_mutex_t* mutex = static_cast(data); michael@0: pthread_mutex_lock(mutex); michael@0: pthread_mutex_unlock(mutex); michael@0: return NULL; michael@0: } michael@0: michael@0: TEST(GetThreadCountTest, ReturnsCorrectValue) { michael@0: EXPECT_EQ(1U, GetThreadCount()); michael@0: pthread_mutex_t mutex; michael@0: pthread_attr_t attr; michael@0: pthread_t thread_id; michael@0: michael@0: // TODO(vladl@google.com): turn mutex into internal::Mutex for automatic michael@0: // destruction. michael@0: pthread_mutex_init(&mutex, NULL); michael@0: pthread_mutex_lock(&mutex); michael@0: ASSERT_EQ(0, pthread_attr_init(&attr)); michael@0: ASSERT_EQ(0, pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE)); michael@0: michael@0: const int status = pthread_create(&thread_id, &attr, &ThreadFunc, &mutex); michael@0: ASSERT_EQ(0, pthread_attr_destroy(&attr)); michael@0: ASSERT_EQ(0, status); michael@0: EXPECT_EQ(2U, GetThreadCount()); michael@0: pthread_mutex_unlock(&mutex); michael@0: michael@0: void* dummy; michael@0: ASSERT_EQ(0, pthread_join(thread_id, &dummy)); michael@0: michael@0: # if GTEST_OS_MAC michael@0: michael@0: // MacOS X may not immediately report the updated thread count after michael@0: // joining a thread, causing flakiness in this test. To counter that, we michael@0: // wait for up to .5 seconds for the OS to report the correct value. michael@0: for (int i = 0; i < 5; ++i) { michael@0: if (GetThreadCount() == 1) michael@0: break; michael@0: michael@0: SleepMilliseconds(100); michael@0: } michael@0: michael@0: # endif // GTEST_OS_MAC michael@0: michael@0: EXPECT_EQ(1U, GetThreadCount()); michael@0: pthread_mutex_destroy(&mutex); michael@0: } michael@0: #else michael@0: TEST(GetThreadCountTest, ReturnsZeroWhenUnableToCountThreads) { michael@0: EXPECT_EQ(0U, GetThreadCount()); michael@0: } michael@0: #endif // GTEST_OS_MAC || GTEST_OS_QNX michael@0: michael@0: TEST(GtestCheckDeathTest, DiesWithCorrectOutputOnFailure) { michael@0: const bool a_false_condition = false; michael@0: const char regex[] = michael@0: #ifdef _MSC_VER michael@0: "gtest-port_test\\.cc\\(\\d+\\):" michael@0: #elif GTEST_USES_POSIX_RE michael@0: "gtest-port_test\\.cc:[0-9]+" michael@0: #else michael@0: "gtest-port_test\\.cc:\\d+" michael@0: #endif // _MSC_VER michael@0: ".*a_false_condition.*Extra info.*"; michael@0: michael@0: EXPECT_DEATH_IF_SUPPORTED(GTEST_CHECK_(a_false_condition) << "Extra info", michael@0: regex); michael@0: } michael@0: michael@0: #if GTEST_HAS_DEATH_TEST michael@0: michael@0: TEST(GtestCheckDeathTest, LivesSilentlyOnSuccess) { michael@0: EXPECT_EXIT({ michael@0: GTEST_CHECK_(true) << "Extra info"; michael@0: ::std::cerr << "Success\n"; michael@0: exit(0); }, michael@0: ::testing::ExitedWithCode(0), "Success"); michael@0: } michael@0: michael@0: #endif // GTEST_HAS_DEATH_TEST michael@0: michael@0: // Verifies that Google Test choose regular expression engine appropriate to michael@0: // the platform. The test will produce compiler errors in case of failure. michael@0: // For simplicity, we only cover the most important platforms here. michael@0: TEST(RegexEngineSelectionTest, SelectsCorrectRegexEngine) { michael@0: #if GTEST_HAS_POSIX_RE michael@0: michael@0: EXPECT_TRUE(GTEST_USES_POSIX_RE); michael@0: michael@0: #else michael@0: michael@0: EXPECT_TRUE(GTEST_USES_SIMPLE_RE); michael@0: michael@0: #endif michael@0: } michael@0: michael@0: #if GTEST_USES_POSIX_RE michael@0: michael@0: # if GTEST_HAS_TYPED_TEST michael@0: michael@0: template michael@0: class RETest : public ::testing::Test {}; michael@0: michael@0: // Defines StringTypes as the list of all string types that class RE michael@0: // supports. michael@0: typedef testing::Types< michael@0: ::std::string, michael@0: # if GTEST_HAS_GLOBAL_STRING michael@0: ::string, michael@0: # endif // GTEST_HAS_GLOBAL_STRING michael@0: const char*> StringTypes; michael@0: michael@0: TYPED_TEST_CASE(RETest, StringTypes); michael@0: michael@0: // Tests RE's implicit constructors. michael@0: TYPED_TEST(RETest, ImplicitConstructorWorks) { michael@0: const RE empty(TypeParam("")); michael@0: EXPECT_STREQ("", empty.pattern()); michael@0: michael@0: const RE simple(TypeParam("hello")); michael@0: EXPECT_STREQ("hello", simple.pattern()); michael@0: michael@0: const RE normal(TypeParam(".*(\\w+)")); michael@0: EXPECT_STREQ(".*(\\w+)", normal.pattern()); michael@0: } michael@0: michael@0: // Tests that RE's constructors reject invalid regular expressions. michael@0: TYPED_TEST(RETest, RejectsInvalidRegex) { michael@0: EXPECT_NONFATAL_FAILURE({ michael@0: const RE invalid(TypeParam("?")); michael@0: }, "\"?\" is not a valid POSIX Extended regular expression."); michael@0: } michael@0: michael@0: // Tests RE::FullMatch(). michael@0: TYPED_TEST(RETest, FullMatchWorks) { michael@0: const RE empty(TypeParam("")); michael@0: EXPECT_TRUE(RE::FullMatch(TypeParam(""), empty)); michael@0: EXPECT_FALSE(RE::FullMatch(TypeParam("a"), empty)); michael@0: michael@0: const RE re(TypeParam("a.*z")); michael@0: EXPECT_TRUE(RE::FullMatch(TypeParam("az"), re)); michael@0: EXPECT_TRUE(RE::FullMatch(TypeParam("axyz"), re)); michael@0: EXPECT_FALSE(RE::FullMatch(TypeParam("baz"), re)); michael@0: EXPECT_FALSE(RE::FullMatch(TypeParam("azy"), re)); michael@0: } michael@0: michael@0: // Tests RE::PartialMatch(). michael@0: TYPED_TEST(RETest, PartialMatchWorks) { michael@0: const RE empty(TypeParam("")); michael@0: EXPECT_TRUE(RE::PartialMatch(TypeParam(""), empty)); michael@0: EXPECT_TRUE(RE::PartialMatch(TypeParam("a"), empty)); michael@0: michael@0: const RE re(TypeParam("a.*z")); michael@0: EXPECT_TRUE(RE::PartialMatch(TypeParam("az"), re)); michael@0: EXPECT_TRUE(RE::PartialMatch(TypeParam("axyz"), re)); michael@0: EXPECT_TRUE(RE::PartialMatch(TypeParam("baz"), re)); michael@0: EXPECT_TRUE(RE::PartialMatch(TypeParam("azy"), re)); michael@0: EXPECT_FALSE(RE::PartialMatch(TypeParam("zza"), re)); michael@0: } michael@0: michael@0: # endif // GTEST_HAS_TYPED_TEST michael@0: michael@0: #elif GTEST_USES_SIMPLE_RE michael@0: michael@0: TEST(IsInSetTest, NulCharIsNotInAnySet) { michael@0: EXPECT_FALSE(IsInSet('\0', "")); michael@0: EXPECT_FALSE(IsInSet('\0', "\0")); michael@0: EXPECT_FALSE(IsInSet('\0', "a")); michael@0: } michael@0: michael@0: TEST(IsInSetTest, WorksForNonNulChars) { michael@0: EXPECT_FALSE(IsInSet('a', "Ab")); michael@0: EXPECT_FALSE(IsInSet('c', "")); michael@0: michael@0: EXPECT_TRUE(IsInSet('b', "bcd")); michael@0: EXPECT_TRUE(IsInSet('b', "ab")); michael@0: } michael@0: michael@0: TEST(IsAsciiDigitTest, IsFalseForNonDigit) { michael@0: EXPECT_FALSE(IsAsciiDigit('\0')); michael@0: EXPECT_FALSE(IsAsciiDigit(' ')); michael@0: EXPECT_FALSE(IsAsciiDigit('+')); michael@0: EXPECT_FALSE(IsAsciiDigit('-')); michael@0: EXPECT_FALSE(IsAsciiDigit('.')); michael@0: EXPECT_FALSE(IsAsciiDigit('a')); michael@0: } michael@0: michael@0: TEST(IsAsciiDigitTest, IsTrueForDigit) { michael@0: EXPECT_TRUE(IsAsciiDigit('0')); michael@0: EXPECT_TRUE(IsAsciiDigit('1')); michael@0: EXPECT_TRUE(IsAsciiDigit('5')); michael@0: EXPECT_TRUE(IsAsciiDigit('9')); michael@0: } michael@0: michael@0: TEST(IsAsciiPunctTest, IsFalseForNonPunct) { michael@0: EXPECT_FALSE(IsAsciiPunct('\0')); michael@0: EXPECT_FALSE(IsAsciiPunct(' ')); michael@0: EXPECT_FALSE(IsAsciiPunct('\n')); michael@0: EXPECT_FALSE(IsAsciiPunct('a')); michael@0: EXPECT_FALSE(IsAsciiPunct('0')); michael@0: } michael@0: michael@0: TEST(IsAsciiPunctTest, IsTrueForPunct) { michael@0: for (const char* p = "^-!\"#$%&'()*+,./:;<=>?@[\\]_`{|}~"; *p; p++) { michael@0: EXPECT_PRED1(IsAsciiPunct, *p); michael@0: } michael@0: } michael@0: michael@0: TEST(IsRepeatTest, IsFalseForNonRepeatChar) { michael@0: EXPECT_FALSE(IsRepeat('\0')); michael@0: EXPECT_FALSE(IsRepeat(' ')); michael@0: EXPECT_FALSE(IsRepeat('a')); michael@0: EXPECT_FALSE(IsRepeat('1')); michael@0: EXPECT_FALSE(IsRepeat('-')); michael@0: } michael@0: michael@0: TEST(IsRepeatTest, IsTrueForRepeatChar) { michael@0: EXPECT_TRUE(IsRepeat('?')); michael@0: EXPECT_TRUE(IsRepeat('*')); michael@0: EXPECT_TRUE(IsRepeat('+')); michael@0: } michael@0: michael@0: TEST(IsAsciiWhiteSpaceTest, IsFalseForNonWhiteSpace) { michael@0: EXPECT_FALSE(IsAsciiWhiteSpace('\0')); michael@0: EXPECT_FALSE(IsAsciiWhiteSpace('a')); michael@0: EXPECT_FALSE(IsAsciiWhiteSpace('1')); michael@0: EXPECT_FALSE(IsAsciiWhiteSpace('+')); michael@0: EXPECT_FALSE(IsAsciiWhiteSpace('_')); michael@0: } michael@0: michael@0: TEST(IsAsciiWhiteSpaceTest, IsTrueForWhiteSpace) { michael@0: EXPECT_TRUE(IsAsciiWhiteSpace(' ')); michael@0: EXPECT_TRUE(IsAsciiWhiteSpace('\n')); michael@0: EXPECT_TRUE(IsAsciiWhiteSpace('\r')); michael@0: EXPECT_TRUE(IsAsciiWhiteSpace('\t')); michael@0: EXPECT_TRUE(IsAsciiWhiteSpace('\v')); michael@0: EXPECT_TRUE(IsAsciiWhiteSpace('\f')); michael@0: } michael@0: michael@0: TEST(IsAsciiWordCharTest, IsFalseForNonWordChar) { michael@0: EXPECT_FALSE(IsAsciiWordChar('\0')); michael@0: EXPECT_FALSE(IsAsciiWordChar('+')); michael@0: EXPECT_FALSE(IsAsciiWordChar('.')); michael@0: EXPECT_FALSE(IsAsciiWordChar(' ')); michael@0: EXPECT_FALSE(IsAsciiWordChar('\n')); michael@0: } michael@0: michael@0: TEST(IsAsciiWordCharTest, IsTrueForLetter) { michael@0: EXPECT_TRUE(IsAsciiWordChar('a')); michael@0: EXPECT_TRUE(IsAsciiWordChar('b')); michael@0: EXPECT_TRUE(IsAsciiWordChar('A')); michael@0: EXPECT_TRUE(IsAsciiWordChar('Z')); michael@0: } michael@0: michael@0: TEST(IsAsciiWordCharTest, IsTrueForDigit) { michael@0: EXPECT_TRUE(IsAsciiWordChar('0')); michael@0: EXPECT_TRUE(IsAsciiWordChar('1')); michael@0: EXPECT_TRUE(IsAsciiWordChar('7')); michael@0: EXPECT_TRUE(IsAsciiWordChar('9')); michael@0: } michael@0: michael@0: TEST(IsAsciiWordCharTest, IsTrueForUnderscore) { michael@0: EXPECT_TRUE(IsAsciiWordChar('_')); michael@0: } michael@0: michael@0: TEST(IsValidEscapeTest, IsFalseForNonPrintable) { michael@0: EXPECT_FALSE(IsValidEscape('\0')); michael@0: EXPECT_FALSE(IsValidEscape('\007')); michael@0: } michael@0: michael@0: TEST(IsValidEscapeTest, IsFalseForDigit) { michael@0: EXPECT_FALSE(IsValidEscape('0')); michael@0: EXPECT_FALSE(IsValidEscape('9')); michael@0: } michael@0: michael@0: TEST(IsValidEscapeTest, IsFalseForWhiteSpace) { michael@0: EXPECT_FALSE(IsValidEscape(' ')); michael@0: EXPECT_FALSE(IsValidEscape('\n')); michael@0: } michael@0: michael@0: TEST(IsValidEscapeTest, IsFalseForSomeLetter) { michael@0: EXPECT_FALSE(IsValidEscape('a')); michael@0: EXPECT_FALSE(IsValidEscape('Z')); michael@0: } michael@0: michael@0: TEST(IsValidEscapeTest, IsTrueForPunct) { michael@0: EXPECT_TRUE(IsValidEscape('.')); michael@0: EXPECT_TRUE(IsValidEscape('-')); michael@0: EXPECT_TRUE(IsValidEscape('^')); michael@0: EXPECT_TRUE(IsValidEscape('$')); michael@0: EXPECT_TRUE(IsValidEscape('(')); michael@0: EXPECT_TRUE(IsValidEscape(']')); michael@0: EXPECT_TRUE(IsValidEscape('{')); michael@0: EXPECT_TRUE(IsValidEscape('|')); michael@0: } michael@0: michael@0: TEST(IsValidEscapeTest, IsTrueForSomeLetter) { michael@0: EXPECT_TRUE(IsValidEscape('d')); michael@0: EXPECT_TRUE(IsValidEscape('D')); michael@0: EXPECT_TRUE(IsValidEscape('s')); michael@0: EXPECT_TRUE(IsValidEscape('S')); michael@0: EXPECT_TRUE(IsValidEscape('w')); michael@0: EXPECT_TRUE(IsValidEscape('W')); michael@0: } michael@0: michael@0: TEST(AtomMatchesCharTest, EscapedPunct) { michael@0: EXPECT_FALSE(AtomMatchesChar(true, '\\', '\0')); michael@0: EXPECT_FALSE(AtomMatchesChar(true, '\\', ' ')); michael@0: EXPECT_FALSE(AtomMatchesChar(true, '_', '.')); michael@0: EXPECT_FALSE(AtomMatchesChar(true, '.', 'a')); michael@0: michael@0: EXPECT_TRUE(AtomMatchesChar(true, '\\', '\\')); michael@0: EXPECT_TRUE(AtomMatchesChar(true, '_', '_')); michael@0: EXPECT_TRUE(AtomMatchesChar(true, '+', '+')); michael@0: EXPECT_TRUE(AtomMatchesChar(true, '.', '.')); michael@0: } michael@0: michael@0: TEST(AtomMatchesCharTest, Escaped_d) { michael@0: EXPECT_FALSE(AtomMatchesChar(true, 'd', '\0')); michael@0: EXPECT_FALSE(AtomMatchesChar(true, 'd', 'a')); michael@0: EXPECT_FALSE(AtomMatchesChar(true, 'd', '.')); michael@0: michael@0: EXPECT_TRUE(AtomMatchesChar(true, 'd', '0')); michael@0: EXPECT_TRUE(AtomMatchesChar(true, 'd', '9')); michael@0: } michael@0: michael@0: TEST(AtomMatchesCharTest, Escaped_D) { michael@0: EXPECT_FALSE(AtomMatchesChar(true, 'D', '0')); michael@0: EXPECT_FALSE(AtomMatchesChar(true, 'D', '9')); michael@0: michael@0: EXPECT_TRUE(AtomMatchesChar(true, 'D', '\0')); michael@0: EXPECT_TRUE(AtomMatchesChar(true, 'D', 'a')); michael@0: EXPECT_TRUE(AtomMatchesChar(true, 'D', '-')); michael@0: } michael@0: michael@0: TEST(AtomMatchesCharTest, Escaped_s) { michael@0: EXPECT_FALSE(AtomMatchesChar(true, 's', '\0')); michael@0: EXPECT_FALSE(AtomMatchesChar(true, 's', 'a')); michael@0: EXPECT_FALSE(AtomMatchesChar(true, 's', '.')); michael@0: EXPECT_FALSE(AtomMatchesChar(true, 's', '9')); michael@0: michael@0: EXPECT_TRUE(AtomMatchesChar(true, 's', ' ')); michael@0: EXPECT_TRUE(AtomMatchesChar(true, 's', '\n')); michael@0: EXPECT_TRUE(AtomMatchesChar(true, 's', '\t')); michael@0: } michael@0: michael@0: TEST(AtomMatchesCharTest, Escaped_S) { michael@0: EXPECT_FALSE(AtomMatchesChar(true, 'S', ' ')); michael@0: EXPECT_FALSE(AtomMatchesChar(true, 'S', '\r')); michael@0: michael@0: EXPECT_TRUE(AtomMatchesChar(true, 'S', '\0')); michael@0: EXPECT_TRUE(AtomMatchesChar(true, 'S', 'a')); michael@0: EXPECT_TRUE(AtomMatchesChar(true, 'S', '9')); michael@0: } michael@0: michael@0: TEST(AtomMatchesCharTest, Escaped_w) { michael@0: EXPECT_FALSE(AtomMatchesChar(true, 'w', '\0')); michael@0: EXPECT_FALSE(AtomMatchesChar(true, 'w', '+')); michael@0: EXPECT_FALSE(AtomMatchesChar(true, 'w', ' ')); michael@0: EXPECT_FALSE(AtomMatchesChar(true, 'w', '\n')); michael@0: michael@0: EXPECT_TRUE(AtomMatchesChar(true, 'w', '0')); michael@0: EXPECT_TRUE(AtomMatchesChar(true, 'w', 'b')); michael@0: EXPECT_TRUE(AtomMatchesChar(true, 'w', 'C')); michael@0: EXPECT_TRUE(AtomMatchesChar(true, 'w', '_')); michael@0: } michael@0: michael@0: TEST(AtomMatchesCharTest, Escaped_W) { michael@0: EXPECT_FALSE(AtomMatchesChar(true, 'W', 'A')); michael@0: EXPECT_FALSE(AtomMatchesChar(true, 'W', 'b')); michael@0: EXPECT_FALSE(AtomMatchesChar(true, 'W', '9')); michael@0: EXPECT_FALSE(AtomMatchesChar(true, 'W', '_')); michael@0: michael@0: EXPECT_TRUE(AtomMatchesChar(true, 'W', '\0')); michael@0: EXPECT_TRUE(AtomMatchesChar(true, 'W', '*')); michael@0: EXPECT_TRUE(AtomMatchesChar(true, 'W', '\n')); michael@0: } michael@0: michael@0: TEST(AtomMatchesCharTest, EscapedWhiteSpace) { michael@0: EXPECT_FALSE(AtomMatchesChar(true, 'f', '\0')); michael@0: EXPECT_FALSE(AtomMatchesChar(true, 'f', '\n')); michael@0: EXPECT_FALSE(AtomMatchesChar(true, 'n', '\0')); michael@0: EXPECT_FALSE(AtomMatchesChar(true, 'n', '\r')); michael@0: EXPECT_FALSE(AtomMatchesChar(true, 'r', '\0')); michael@0: EXPECT_FALSE(AtomMatchesChar(true, 'r', 'a')); michael@0: EXPECT_FALSE(AtomMatchesChar(true, 't', '\0')); michael@0: EXPECT_FALSE(AtomMatchesChar(true, 't', 't')); michael@0: EXPECT_FALSE(AtomMatchesChar(true, 'v', '\0')); michael@0: EXPECT_FALSE(AtomMatchesChar(true, 'v', '\f')); michael@0: michael@0: EXPECT_TRUE(AtomMatchesChar(true, 'f', '\f')); michael@0: EXPECT_TRUE(AtomMatchesChar(true, 'n', '\n')); michael@0: EXPECT_TRUE(AtomMatchesChar(true, 'r', '\r')); michael@0: EXPECT_TRUE(AtomMatchesChar(true, 't', '\t')); michael@0: EXPECT_TRUE(AtomMatchesChar(true, 'v', '\v')); michael@0: } michael@0: michael@0: TEST(AtomMatchesCharTest, UnescapedDot) { michael@0: EXPECT_FALSE(AtomMatchesChar(false, '.', '\n')); michael@0: michael@0: EXPECT_TRUE(AtomMatchesChar(false, '.', '\0')); michael@0: EXPECT_TRUE(AtomMatchesChar(false, '.', '.')); michael@0: EXPECT_TRUE(AtomMatchesChar(false, '.', 'a')); michael@0: EXPECT_TRUE(AtomMatchesChar(false, '.', ' ')); michael@0: } michael@0: michael@0: TEST(AtomMatchesCharTest, UnescapedChar) { michael@0: EXPECT_FALSE(AtomMatchesChar(false, 'a', '\0')); michael@0: EXPECT_FALSE(AtomMatchesChar(false, 'a', 'b')); michael@0: EXPECT_FALSE(AtomMatchesChar(false, '$', 'a')); michael@0: michael@0: EXPECT_TRUE(AtomMatchesChar(false, '$', '$')); michael@0: EXPECT_TRUE(AtomMatchesChar(false, '5', '5')); michael@0: EXPECT_TRUE(AtomMatchesChar(false, 'Z', 'Z')); michael@0: } michael@0: michael@0: TEST(ValidateRegexTest, GeneratesFailureAndReturnsFalseForInvalid) { michael@0: EXPECT_NONFATAL_FAILURE(ASSERT_FALSE(ValidateRegex(NULL)), michael@0: "NULL is not a valid simple regular expression"); michael@0: EXPECT_NONFATAL_FAILURE( michael@0: ASSERT_FALSE(ValidateRegex("a\\")), michael@0: "Syntax error at index 1 in simple regular expression \"a\\\": "); michael@0: EXPECT_NONFATAL_FAILURE(ASSERT_FALSE(ValidateRegex("a\\")), michael@0: "'\\' cannot appear at the end"); michael@0: EXPECT_NONFATAL_FAILURE(ASSERT_FALSE(ValidateRegex("\\n\\")), michael@0: "'\\' cannot appear at the end"); michael@0: EXPECT_NONFATAL_FAILURE(ASSERT_FALSE(ValidateRegex("\\s\\hb")), michael@0: "invalid escape sequence \"\\h\""); michael@0: EXPECT_NONFATAL_FAILURE(ASSERT_FALSE(ValidateRegex("^^")), michael@0: "'^' can only appear at the beginning"); michael@0: EXPECT_NONFATAL_FAILURE(ASSERT_FALSE(ValidateRegex(".*^b")), michael@0: "'^' can only appear at the beginning"); michael@0: EXPECT_NONFATAL_FAILURE(ASSERT_FALSE(ValidateRegex("$$")), michael@0: "'$' can only appear at the end"); michael@0: EXPECT_NONFATAL_FAILURE(ASSERT_FALSE(ValidateRegex("^$a")), michael@0: "'$' can only appear at the end"); michael@0: EXPECT_NONFATAL_FAILURE(ASSERT_FALSE(ValidateRegex("a(b")), michael@0: "'(' is unsupported"); michael@0: EXPECT_NONFATAL_FAILURE(ASSERT_FALSE(ValidateRegex("ab)")), michael@0: "')' is unsupported"); michael@0: EXPECT_NONFATAL_FAILURE(ASSERT_FALSE(ValidateRegex("[ab")), michael@0: "'[' is unsupported"); michael@0: EXPECT_NONFATAL_FAILURE(ASSERT_FALSE(ValidateRegex("a{2")), michael@0: "'{' is unsupported"); michael@0: EXPECT_NONFATAL_FAILURE(ASSERT_FALSE(ValidateRegex("?")), michael@0: "'?' can only follow a repeatable token"); michael@0: EXPECT_NONFATAL_FAILURE(ASSERT_FALSE(ValidateRegex("^*")), michael@0: "'*' can only follow a repeatable token"); michael@0: EXPECT_NONFATAL_FAILURE(ASSERT_FALSE(ValidateRegex("5*+")), michael@0: "'+' can only follow a repeatable token"); michael@0: } michael@0: michael@0: TEST(ValidateRegexTest, ReturnsTrueForValid) { michael@0: EXPECT_TRUE(ValidateRegex("")); michael@0: EXPECT_TRUE(ValidateRegex("a")); michael@0: EXPECT_TRUE(ValidateRegex(".*")); michael@0: EXPECT_TRUE(ValidateRegex("^a_+")); michael@0: EXPECT_TRUE(ValidateRegex("^a\\t\\&?")); michael@0: EXPECT_TRUE(ValidateRegex("09*$")); michael@0: EXPECT_TRUE(ValidateRegex("^Z$")); michael@0: EXPECT_TRUE(ValidateRegex("a\\^Z\\$\\(\\)\\|\\[\\]\\{\\}")); michael@0: } michael@0: michael@0: TEST(MatchRepetitionAndRegexAtHeadTest, WorksForZeroOrOne) { michael@0: EXPECT_FALSE(MatchRepetitionAndRegexAtHead(false, 'a', '?', "a", "ba")); michael@0: // Repeating more than once. michael@0: EXPECT_FALSE(MatchRepetitionAndRegexAtHead(false, 'a', '?', "b", "aab")); michael@0: michael@0: // Repeating zero times. michael@0: EXPECT_TRUE(MatchRepetitionAndRegexAtHead(false, 'a', '?', "b", "ba")); michael@0: // Repeating once. michael@0: EXPECT_TRUE(MatchRepetitionAndRegexAtHead(false, 'a', '?', "b", "ab")); michael@0: EXPECT_TRUE(MatchRepetitionAndRegexAtHead(false, '#', '?', ".", "##")); michael@0: } michael@0: michael@0: TEST(MatchRepetitionAndRegexAtHeadTest, WorksForZeroOrMany) { michael@0: EXPECT_FALSE(MatchRepetitionAndRegexAtHead(false, '.', '*', "a$", "baab")); michael@0: michael@0: // Repeating zero times. michael@0: EXPECT_TRUE(MatchRepetitionAndRegexAtHead(false, '.', '*', "b", "bc")); michael@0: // Repeating once. michael@0: EXPECT_TRUE(MatchRepetitionAndRegexAtHead(false, '.', '*', "b", "abc")); michael@0: // Repeating more than once. michael@0: EXPECT_TRUE(MatchRepetitionAndRegexAtHead(true, 'w', '*', "-", "ab_1-g")); michael@0: } michael@0: michael@0: TEST(MatchRepetitionAndRegexAtHeadTest, WorksForOneOrMany) { michael@0: EXPECT_FALSE(MatchRepetitionAndRegexAtHead(false, '.', '+', "a$", "baab")); michael@0: // Repeating zero times. michael@0: EXPECT_FALSE(MatchRepetitionAndRegexAtHead(false, '.', '+', "b", "bc")); michael@0: michael@0: // Repeating once. michael@0: EXPECT_TRUE(MatchRepetitionAndRegexAtHead(false, '.', '+', "b", "abc")); michael@0: // Repeating more than once. michael@0: EXPECT_TRUE(MatchRepetitionAndRegexAtHead(true, 'w', '+', "-", "ab_1-g")); michael@0: } michael@0: michael@0: TEST(MatchRegexAtHeadTest, ReturnsTrueForEmptyRegex) { michael@0: EXPECT_TRUE(MatchRegexAtHead("", "")); michael@0: EXPECT_TRUE(MatchRegexAtHead("", "ab")); michael@0: } michael@0: michael@0: TEST(MatchRegexAtHeadTest, WorksWhenDollarIsInRegex) { michael@0: EXPECT_FALSE(MatchRegexAtHead("$", "a")); michael@0: michael@0: EXPECT_TRUE(MatchRegexAtHead("$", "")); michael@0: EXPECT_TRUE(MatchRegexAtHead("a$", "a")); michael@0: } michael@0: michael@0: TEST(MatchRegexAtHeadTest, WorksWhenRegexStartsWithEscapeSequence) { michael@0: EXPECT_FALSE(MatchRegexAtHead("\\w", "+")); michael@0: EXPECT_FALSE(MatchRegexAtHead("\\W", "ab")); michael@0: michael@0: EXPECT_TRUE(MatchRegexAtHead("\\sa", "\nab")); michael@0: EXPECT_TRUE(MatchRegexAtHead("\\d", "1a")); michael@0: } michael@0: michael@0: TEST(MatchRegexAtHeadTest, WorksWhenRegexStartsWithRepetition) { michael@0: EXPECT_FALSE(MatchRegexAtHead(".+a", "abc")); michael@0: EXPECT_FALSE(MatchRegexAtHead("a?b", "aab")); michael@0: michael@0: EXPECT_TRUE(MatchRegexAtHead(".*a", "bc12-ab")); michael@0: EXPECT_TRUE(MatchRegexAtHead("a?b", "b")); michael@0: EXPECT_TRUE(MatchRegexAtHead("a?b", "ab")); michael@0: } michael@0: michael@0: TEST(MatchRegexAtHeadTest, michael@0: WorksWhenRegexStartsWithRepetionOfEscapeSequence) { michael@0: EXPECT_FALSE(MatchRegexAtHead("\\.+a", "abc")); michael@0: EXPECT_FALSE(MatchRegexAtHead("\\s?b", " b")); michael@0: michael@0: EXPECT_TRUE(MatchRegexAtHead("\\(*a", "((((ab")); michael@0: EXPECT_TRUE(MatchRegexAtHead("\\^?b", "^b")); michael@0: EXPECT_TRUE(MatchRegexAtHead("\\\\?b", "b")); michael@0: EXPECT_TRUE(MatchRegexAtHead("\\\\?b", "\\b")); michael@0: } michael@0: michael@0: TEST(MatchRegexAtHeadTest, MatchesSequentially) { michael@0: EXPECT_FALSE(MatchRegexAtHead("ab.*c", "acabc")); michael@0: michael@0: EXPECT_TRUE(MatchRegexAtHead("ab.*c", "ab-fsc")); michael@0: } michael@0: michael@0: TEST(MatchRegexAnywhereTest, ReturnsFalseWhenStringIsNull) { michael@0: EXPECT_FALSE(MatchRegexAnywhere("", NULL)); michael@0: } michael@0: michael@0: TEST(MatchRegexAnywhereTest, WorksWhenRegexStartsWithCaret) { michael@0: EXPECT_FALSE(MatchRegexAnywhere("^a", "ba")); michael@0: EXPECT_FALSE(MatchRegexAnywhere("^$", "a")); michael@0: michael@0: EXPECT_TRUE(MatchRegexAnywhere("^a", "ab")); michael@0: EXPECT_TRUE(MatchRegexAnywhere("^", "ab")); michael@0: EXPECT_TRUE(MatchRegexAnywhere("^$", "")); michael@0: } michael@0: michael@0: TEST(MatchRegexAnywhereTest, ReturnsFalseWhenNoMatch) { michael@0: EXPECT_FALSE(MatchRegexAnywhere("a", "bcde123")); michael@0: EXPECT_FALSE(MatchRegexAnywhere("a.+a", "--aa88888888")); michael@0: } michael@0: michael@0: TEST(MatchRegexAnywhereTest, ReturnsTrueWhenMatchingPrefix) { michael@0: EXPECT_TRUE(MatchRegexAnywhere("\\w+", "ab1_ - 5")); michael@0: EXPECT_TRUE(MatchRegexAnywhere(".*=", "=")); michael@0: EXPECT_TRUE(MatchRegexAnywhere("x.*ab?.*bc", "xaaabc")); michael@0: } michael@0: michael@0: TEST(MatchRegexAnywhereTest, ReturnsTrueWhenMatchingNonPrefix) { michael@0: EXPECT_TRUE(MatchRegexAnywhere("\\w+", "$$$ ab1_ - 5")); michael@0: EXPECT_TRUE(MatchRegexAnywhere("\\.+=", "= ...=")); michael@0: } michael@0: michael@0: // Tests RE's implicit constructors. michael@0: TEST(RETest, ImplicitConstructorWorks) { michael@0: const RE empty(""); michael@0: EXPECT_STREQ("", empty.pattern()); michael@0: michael@0: const RE simple("hello"); michael@0: EXPECT_STREQ("hello", simple.pattern()); michael@0: } michael@0: michael@0: // Tests that RE's constructors reject invalid regular expressions. michael@0: TEST(RETest, RejectsInvalidRegex) { michael@0: EXPECT_NONFATAL_FAILURE({ michael@0: const RE normal(NULL); michael@0: }, "NULL is not a valid simple regular expression"); michael@0: michael@0: EXPECT_NONFATAL_FAILURE({ michael@0: const RE normal(".*(\\w+"); michael@0: }, "'(' is unsupported"); michael@0: michael@0: EXPECT_NONFATAL_FAILURE({ michael@0: const RE invalid("^?"); michael@0: }, "'?' can only follow a repeatable token"); michael@0: } michael@0: michael@0: // Tests RE::FullMatch(). michael@0: TEST(RETest, FullMatchWorks) { michael@0: const RE empty(""); michael@0: EXPECT_TRUE(RE::FullMatch("", empty)); michael@0: EXPECT_FALSE(RE::FullMatch("a", empty)); michael@0: michael@0: const RE re1("a"); michael@0: EXPECT_TRUE(RE::FullMatch("a", re1)); michael@0: michael@0: const RE re("a.*z"); michael@0: EXPECT_TRUE(RE::FullMatch("az", re)); michael@0: EXPECT_TRUE(RE::FullMatch("axyz", re)); michael@0: EXPECT_FALSE(RE::FullMatch("baz", re)); michael@0: EXPECT_FALSE(RE::FullMatch("azy", re)); michael@0: } michael@0: michael@0: // Tests RE::PartialMatch(). michael@0: TEST(RETest, PartialMatchWorks) { michael@0: const RE empty(""); michael@0: EXPECT_TRUE(RE::PartialMatch("", empty)); michael@0: EXPECT_TRUE(RE::PartialMatch("a", empty)); michael@0: michael@0: const RE re("a.*z"); michael@0: EXPECT_TRUE(RE::PartialMatch("az", re)); michael@0: EXPECT_TRUE(RE::PartialMatch("axyz", re)); michael@0: EXPECT_TRUE(RE::PartialMatch("baz", re)); michael@0: EXPECT_TRUE(RE::PartialMatch("azy", re)); michael@0: EXPECT_FALSE(RE::PartialMatch("zza", re)); michael@0: } michael@0: michael@0: #endif // GTEST_USES_POSIX_RE michael@0: michael@0: #if !GTEST_OS_WINDOWS_MOBILE michael@0: michael@0: TEST(CaptureTest, CapturesStdout) { michael@0: CaptureStdout(); michael@0: fprintf(stdout, "abc"); michael@0: EXPECT_STREQ("abc", GetCapturedStdout().c_str()); michael@0: michael@0: CaptureStdout(); michael@0: fprintf(stdout, "def%cghi", '\0'); michael@0: EXPECT_EQ(::std::string("def\0ghi", 7), ::std::string(GetCapturedStdout())); michael@0: } michael@0: michael@0: TEST(CaptureTest, CapturesStderr) { michael@0: CaptureStderr(); michael@0: fprintf(stderr, "jkl"); michael@0: EXPECT_STREQ("jkl", GetCapturedStderr().c_str()); michael@0: michael@0: CaptureStderr(); michael@0: fprintf(stderr, "jkl%cmno", '\0'); michael@0: EXPECT_EQ(::std::string("jkl\0mno", 7), ::std::string(GetCapturedStderr())); michael@0: } michael@0: michael@0: // Tests that stdout and stderr capture don't interfere with each other. michael@0: TEST(CaptureTest, CapturesStdoutAndStderr) { michael@0: CaptureStdout(); michael@0: CaptureStderr(); michael@0: fprintf(stdout, "pqr"); michael@0: fprintf(stderr, "stu"); michael@0: EXPECT_STREQ("pqr", GetCapturedStdout().c_str()); michael@0: EXPECT_STREQ("stu", GetCapturedStderr().c_str()); michael@0: } michael@0: michael@0: TEST(CaptureDeathTest, CannotReenterStdoutCapture) { michael@0: CaptureStdout(); michael@0: EXPECT_DEATH_IF_SUPPORTED(CaptureStdout(), michael@0: "Only one stdout capturer can exist at a time"); michael@0: GetCapturedStdout(); michael@0: michael@0: // We cannot test stderr capturing using death tests as they use it michael@0: // themselves. michael@0: } michael@0: michael@0: #endif // !GTEST_OS_WINDOWS_MOBILE michael@0: michael@0: TEST(ThreadLocalTest, DefaultConstructorInitializesToDefaultValues) { michael@0: ThreadLocal t1; michael@0: EXPECT_EQ(0, t1.get()); michael@0: michael@0: ThreadLocal t2; michael@0: EXPECT_TRUE(t2.get() == NULL); michael@0: } michael@0: michael@0: TEST(ThreadLocalTest, SingleParamConstructorInitializesToParam) { michael@0: ThreadLocal t1(123); michael@0: EXPECT_EQ(123, t1.get()); michael@0: michael@0: int i = 0; michael@0: ThreadLocal t2(&i); michael@0: EXPECT_EQ(&i, t2.get()); michael@0: } michael@0: michael@0: class NoDefaultContructor { michael@0: public: michael@0: explicit NoDefaultContructor(const char*) {} michael@0: NoDefaultContructor(const NoDefaultContructor&) {} michael@0: }; michael@0: michael@0: TEST(ThreadLocalTest, ValueDefaultContructorIsNotRequiredForParamVersion) { michael@0: ThreadLocal bar(NoDefaultContructor("foo")); michael@0: bar.pointer(); michael@0: } michael@0: michael@0: TEST(ThreadLocalTest, GetAndPointerReturnSameValue) { michael@0: ThreadLocal thread_local_string; michael@0: michael@0: EXPECT_EQ(thread_local_string.pointer(), &(thread_local_string.get())); michael@0: michael@0: // Verifies the condition still holds after calling set. michael@0: thread_local_string.set("foo"); michael@0: EXPECT_EQ(thread_local_string.pointer(), &(thread_local_string.get())); michael@0: } michael@0: michael@0: TEST(ThreadLocalTest, PointerAndConstPointerReturnSameValue) { michael@0: ThreadLocal thread_local_string; michael@0: const ThreadLocal& const_thread_local_string = thread_local_string; michael@0: michael@0: EXPECT_EQ(thread_local_string.pointer(), const_thread_local_string.pointer()); michael@0: michael@0: thread_local_string.set("foo"); michael@0: EXPECT_EQ(thread_local_string.pointer(), const_thread_local_string.pointer()); michael@0: } michael@0: michael@0: #if GTEST_IS_THREADSAFE michael@0: michael@0: void AddTwo(int* param) { *param += 2; } michael@0: michael@0: TEST(ThreadWithParamTest, ConstructorExecutesThreadFunc) { michael@0: int i = 40; michael@0: ThreadWithParam thread(&AddTwo, &i, NULL); michael@0: thread.Join(); michael@0: EXPECT_EQ(42, i); michael@0: } michael@0: michael@0: TEST(MutexDeathTest, AssertHeldShouldAssertWhenNotLocked) { michael@0: // AssertHeld() is flaky only in the presence of multiple threads accessing michael@0: // the lock. In this case, the test is robust. michael@0: EXPECT_DEATH_IF_SUPPORTED({ michael@0: Mutex m; michael@0: { MutexLock lock(&m); } michael@0: m.AssertHeld(); michael@0: }, michael@0: "thread .*hold"); michael@0: } michael@0: michael@0: TEST(MutexTest, AssertHeldShouldNotAssertWhenLocked) { michael@0: Mutex m; michael@0: MutexLock lock(&m); michael@0: m.AssertHeld(); michael@0: } michael@0: michael@0: class AtomicCounterWithMutex { michael@0: public: michael@0: explicit AtomicCounterWithMutex(Mutex* mutex) : michael@0: value_(0), mutex_(mutex), random_(42) {} michael@0: michael@0: void Increment() { michael@0: MutexLock lock(mutex_); michael@0: int temp = value_; michael@0: { michael@0: // Locking a mutex puts up a memory barrier, preventing reads and michael@0: // writes to value_ rearranged when observed from other threads. michael@0: // michael@0: // We cannot use Mutex and MutexLock here or rely on their memory michael@0: // barrier functionality as we are testing them here. michael@0: pthread_mutex_t memory_barrier_mutex; michael@0: GTEST_CHECK_POSIX_SUCCESS_( michael@0: pthread_mutex_init(&memory_barrier_mutex, NULL)); michael@0: GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_lock(&memory_barrier_mutex)); michael@0: michael@0: SleepMilliseconds(random_.Generate(30)); michael@0: michael@0: GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_unlock(&memory_barrier_mutex)); michael@0: GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_destroy(&memory_barrier_mutex)); michael@0: } michael@0: value_ = temp + 1; michael@0: } michael@0: int value() const { return value_; } michael@0: michael@0: private: michael@0: volatile int value_; michael@0: Mutex* const mutex_; // Protects value_. michael@0: Random random_; michael@0: }; michael@0: michael@0: void CountingThreadFunc(pair param) { michael@0: for (int i = 0; i < param.second; ++i) michael@0: param.first->Increment(); michael@0: } michael@0: michael@0: // Tests that the mutex only lets one thread at a time to lock it. michael@0: TEST(MutexTest, OnlyOneThreadCanLockAtATime) { michael@0: Mutex mutex; michael@0: AtomicCounterWithMutex locked_counter(&mutex); michael@0: michael@0: typedef ThreadWithParam > ThreadType; michael@0: const int kCycleCount = 20; michael@0: const int kThreadCount = 7; michael@0: scoped_ptr counting_threads[kThreadCount]; michael@0: Notification threads_can_start; michael@0: // Creates and runs kThreadCount threads that increment locked_counter michael@0: // kCycleCount times each. michael@0: for (int i = 0; i < kThreadCount; ++i) { michael@0: counting_threads[i].reset(new ThreadType(&CountingThreadFunc, michael@0: make_pair(&locked_counter, michael@0: kCycleCount), michael@0: &threads_can_start)); michael@0: } michael@0: threads_can_start.Notify(); michael@0: for (int i = 0; i < kThreadCount; ++i) michael@0: counting_threads[i]->Join(); michael@0: michael@0: // If the mutex lets more than one thread to increment the counter at a michael@0: // time, they are likely to encounter a race condition and have some michael@0: // increments overwritten, resulting in the lower then expected counter michael@0: // value. michael@0: EXPECT_EQ(kCycleCount * kThreadCount, locked_counter.value()); michael@0: } michael@0: michael@0: template michael@0: void RunFromThread(void (func)(T), T param) { michael@0: ThreadWithParam thread(func, param, NULL); michael@0: thread.Join(); michael@0: } michael@0: michael@0: void RetrieveThreadLocalValue(pair*, String*> param) { michael@0: *param.second = param.first->get(); michael@0: } michael@0: michael@0: TEST(ThreadLocalTest, ParameterizedConstructorSetsDefault) { michael@0: ThreadLocal thread_local_string("foo"); michael@0: EXPECT_STREQ("foo", thread_local_string.get().c_str()); michael@0: michael@0: thread_local_string.set("bar"); michael@0: EXPECT_STREQ("bar", thread_local_string.get().c_str()); michael@0: michael@0: String result; michael@0: RunFromThread(&RetrieveThreadLocalValue, michael@0: make_pair(&thread_local_string, &result)); michael@0: EXPECT_STREQ("foo", result.c_str()); michael@0: } michael@0: michael@0: // DestructorTracker keeps track of whether its instances have been michael@0: // destroyed. michael@0: static std::vector g_destroyed; michael@0: michael@0: class DestructorTracker { michael@0: public: michael@0: DestructorTracker() : index_(GetNewIndex()) {} michael@0: DestructorTracker(const DestructorTracker& /* rhs */) michael@0: : index_(GetNewIndex()) {} michael@0: ~DestructorTracker() { michael@0: // We never access g_destroyed concurrently, so we don't need to michael@0: // protect the write operation under a mutex. michael@0: g_destroyed[index_] = true; michael@0: } michael@0: michael@0: private: michael@0: static int GetNewIndex() { michael@0: g_destroyed.push_back(false); michael@0: return g_destroyed.size() - 1; michael@0: } michael@0: const int index_; michael@0: }; michael@0: michael@0: typedef ThreadLocal* ThreadParam; michael@0: michael@0: void CallThreadLocalGet(ThreadParam thread_local_param) { michael@0: thread_local_param->get(); michael@0: } michael@0: michael@0: // Tests that when a ThreadLocal object dies in a thread, it destroys michael@0: // the managed object for that thread. michael@0: TEST(ThreadLocalTest, DestroysManagedObjectForOwnThreadWhenDying) { michael@0: g_destroyed.clear(); michael@0: michael@0: { michael@0: // The next line default constructs a DestructorTracker object as michael@0: // the default value of objects managed by thread_local_tracker. michael@0: ThreadLocal thread_local_tracker; michael@0: ASSERT_EQ(1U, g_destroyed.size()); michael@0: ASSERT_FALSE(g_destroyed[0]); michael@0: michael@0: // This creates another DestructorTracker object for the main thread. michael@0: thread_local_tracker.get(); michael@0: ASSERT_EQ(2U, g_destroyed.size()); michael@0: ASSERT_FALSE(g_destroyed[0]); michael@0: ASSERT_FALSE(g_destroyed[1]); michael@0: } michael@0: michael@0: // Now thread_local_tracker has died. It should have destroyed both the michael@0: // default value shared by all threads and the value for the main michael@0: // thread. michael@0: ASSERT_EQ(2U, g_destroyed.size()); michael@0: EXPECT_TRUE(g_destroyed[0]); michael@0: EXPECT_TRUE(g_destroyed[1]); michael@0: michael@0: g_destroyed.clear(); michael@0: } michael@0: michael@0: // Tests that when a thread exits, the thread-local object for that michael@0: // thread is destroyed. michael@0: TEST(ThreadLocalTest, DestroysManagedObjectAtThreadExit) { michael@0: g_destroyed.clear(); michael@0: michael@0: { michael@0: // The next line default constructs a DestructorTracker object as michael@0: // the default value of objects managed by thread_local_tracker. michael@0: ThreadLocal thread_local_tracker; michael@0: ASSERT_EQ(1U, g_destroyed.size()); michael@0: ASSERT_FALSE(g_destroyed[0]); michael@0: michael@0: // This creates another DestructorTracker object in the new thread. michael@0: ThreadWithParam thread( michael@0: &CallThreadLocalGet, &thread_local_tracker, NULL); michael@0: thread.Join(); michael@0: michael@0: // Now the new thread has exited. The per-thread object for it michael@0: // should have been destroyed. michael@0: ASSERT_EQ(2U, g_destroyed.size()); michael@0: ASSERT_FALSE(g_destroyed[0]); michael@0: ASSERT_TRUE(g_destroyed[1]); michael@0: } michael@0: michael@0: // Now thread_local_tracker has died. The default value should have been michael@0: // destroyed too. michael@0: ASSERT_EQ(2U, g_destroyed.size()); michael@0: EXPECT_TRUE(g_destroyed[0]); michael@0: EXPECT_TRUE(g_destroyed[1]); michael@0: michael@0: g_destroyed.clear(); michael@0: } michael@0: michael@0: TEST(ThreadLocalTest, ThreadLocalMutationsAffectOnlyCurrentThread) { michael@0: ThreadLocal thread_local_string; michael@0: thread_local_string.set("Foo"); michael@0: EXPECT_STREQ("Foo", thread_local_string.get().c_str()); michael@0: michael@0: String result; michael@0: RunFromThread(&RetrieveThreadLocalValue, michael@0: make_pair(&thread_local_string, &result)); michael@0: EXPECT_TRUE(result.c_str() == NULL); michael@0: } michael@0: michael@0: #endif // GTEST_IS_THREADSAFE michael@0: michael@0: } // namespace internal michael@0: } // namespace testing