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