diff options
Diffstat (limited to 'tests/gtest/src/gtest.cc')
-rw-r--r-- | tests/gtest/src/gtest.cc | 1593 |
1 files changed, 993 insertions, 600 deletions
diff --git a/tests/gtest/src/gtest.cc b/tests/gtest/src/gtest.cc index ee5eb1c49..7fd5f298d 100644 --- a/tests/gtest/src/gtest.cc +++ b/tests/gtest/src/gtest.cc @@ -39,10 +39,15 @@ #include <stdarg.h> #include <stdio.h> #include <stdlib.h> +#include <time.h> #include <wchar.h> #include <wctype.h> #include <algorithm> +#include <iomanip> +#include <limits> +#include <list> +#include <map> #include <ostream> // NOLINT #include <sstream> #include <vector> @@ -77,6 +82,7 @@ #elif GTEST_OS_WINDOWS_MOBILE // We are on Windows CE. # include <windows.h> // NOLINT +# undef min #elif GTEST_OS_WINDOWS // We are on Windows proper. @@ -99,6 +105,7 @@ // cpplint thinks that the header is already included, so we want to // silence it. # include <windows.h> // NOLINT +# undef min #else @@ -121,6 +128,8 @@ #if GTEST_CAN_STREAM_RESULTS_ # include <arpa/inet.h> // NOLINT # include <netdb.h> // NOLINT +# include <sys/socket.h> // NOLINT +# include <sys/types.h> // NOLINT #endif // Indicates that this translation unit is part of Google Test's @@ -179,6 +188,10 @@ bool g_help_flag = false; } // namespace internal +static const char* GetDefaultFilter() { + return kUniversalFilter; +} + GTEST_DEFINE_bool_( also_run_disabled_tests, internal::BoolFromGTestEnv("also_run_disabled_tests", false), @@ -201,11 +214,11 @@ GTEST_DEFINE_string_( "Whether to use colors in the output. Valid values: yes, no, " "and auto. 'auto' means to use colors if the output is " "being sent to a terminal and the TERM environment variable " - "is set to xterm, xterm-color, xterm-256color, linux or cygwin."); + "is set to a terminal type that supports colors."); GTEST_DEFINE_string_( filter, - internal::StringFromGTestEnv("filter", kUniversalFilter), + internal::StringFromGTestEnv("filter", GetDefaultFilter()), "A colon-separated list of glob (not regex) patterns " "for filtering the tests to run, optionally followed by a " "'-' and a : separated list of negative patterns (tests to " @@ -305,7 +318,7 @@ UInt32 Random::Generate(UInt32 range) { // Test. g_init_gtest_count is set to the number of times // InitGoogleTest() has been called. We don't protect this variable // under a mutex as it is only accessed in the main thread. -int g_init_gtest_count = 0; +GTEST_API_ int g_init_gtest_count = 0; static bool GTestIsInitialized() { return g_init_gtest_count != 0; } // Iterates over a vector of TestCases, keeping a running sum of the @@ -360,10 +373,10 @@ void AssertHelper::operator=(const Message& message) const { } // Mutex for linked pointers. -GTEST_DEFINE_STATIC_MUTEX_(g_linked_ptr_mutex); +GTEST_API_ GTEST_DEFINE_STATIC_MUTEX_(g_linked_ptr_mutex); // Application pathname gotten in InitGoogleTest. -String g_executable_path; +std::string g_executable_path; // Returns the current application's name, removing directory path if that // is present. @@ -382,29 +395,29 @@ FilePath GetCurrentExecutableName() { // Functions for processing the gtest_output flag. // Returns the output format, or "" for normal printed output. -String UnitTestOptions::GetOutputFormat() { +std::string UnitTestOptions::GetOutputFormat() { const char* const gtest_output_flag = GTEST_FLAG(output).c_str(); - if (gtest_output_flag == NULL) return String(""); + if (gtest_output_flag == NULL) return std::string(""); const char* const colon = strchr(gtest_output_flag, ':'); return (colon == NULL) ? - String(gtest_output_flag) : - String(gtest_output_flag, colon - gtest_output_flag); + std::string(gtest_output_flag) : + std::string(gtest_output_flag, colon - gtest_output_flag); } // Returns the name of the requested output file, or the default if none // was explicitly specified. -String UnitTestOptions::GetAbsolutePathToOutputFile() { +std::string UnitTestOptions::GetAbsolutePathToOutputFile() { const char* const gtest_output_flag = GTEST_FLAG(output).c_str(); if (gtest_output_flag == NULL) - return String(""); + return ""; const char* const colon = strchr(gtest_output_flag, ':'); if (colon == NULL) - return String(internal::FilePath::ConcatPaths( - internal::FilePath( - UnitTest::GetInstance()->original_working_dir()), - internal::FilePath(kDefaultOutputFile)).ToString() ); + return internal::FilePath::ConcatPaths( + internal::FilePath( + UnitTest::GetInstance()->original_working_dir()), + internal::FilePath(kDefaultOutputFile)).string(); internal::FilePath output_name(colon + 1); if (!output_name.IsAbsolutePath()) @@ -417,12 +430,12 @@ String UnitTestOptions::GetAbsolutePathToOutputFile() { internal::FilePath(colon + 1)); if (!output_name.IsDirectory()) - return output_name.ToString(); + return output_name.string(); internal::FilePath result(internal::FilePath::GenerateUniqueFileName( output_name, internal::GetCurrentExecutableName(), GetOutputFormat().c_str())); - return result.ToString(); + return result.string(); } // Returns true iff the wildcard pattern matches the string. The @@ -447,7 +460,8 @@ bool UnitTestOptions::PatternMatchesString(const char *pattern, } } -bool UnitTestOptions::MatchesFilter(const String& name, const char* filter) { +bool UnitTestOptions::MatchesFilter( + const std::string& name, const char* filter) { const char *cur_pattern = filter; for (;;) { if (PatternMatchesString(cur_pattern, name.c_str())) { @@ -467,28 +481,24 @@ bool UnitTestOptions::MatchesFilter(const String& name, const char* filter) { } } -// TODO(keithray): move String function implementations to gtest-string.cc. - // Returns true iff the user-specified filter matches the test case // name and the test name. -bool UnitTestOptions::FilterMatchesTest(const String &test_case_name, - const String &test_name) { - const String& full_name = String::Format("%s.%s", - test_case_name.c_str(), - test_name.c_str()); +bool UnitTestOptions::FilterMatchesTest(const std::string &test_case_name, + const std::string &test_name) { + const std::string& full_name = test_case_name + "." + test_name.c_str(); // Split --gtest_filter at '-', if there is one, to separate into // positive filter and negative filter portions const char* const p = GTEST_FLAG(filter).c_str(); const char* const dash = strchr(p, '-'); - String positive; - String negative; + std::string positive; + std::string negative; if (dash == NULL) { positive = GTEST_FLAG(filter).c_str(); // Whole string is a positive filter - negative = String(""); + negative = ""; } else { - positive = String(p, dash - p); // Everything up to the dash - negative = String(dash+1); // Everything after the dash + positive = std::string(p, dash); // Everything up to the dash + negative = std::string(dash + 1); // Everything after the dash if (positive.empty()) { // Treat '-test1' as the same as '*-test1' positive = kUniversalFilter; @@ -608,7 +618,7 @@ AssertionResult HasOneFailure(const char* /* results_expr */, const TestPartResultArray& results, TestPartResult::Type type, const string& substr) { - const String expected(type == TestPartResult::kFatalFailure ? + const std::string expected(type == TestPartResult::kFatalFailure ? "1 fatal failure" : "1 non-fatal failure"); Message msg; @@ -731,11 +741,22 @@ int UnitTestImpl::failed_test_count() const { return SumOverTestCaseList(test_cases_, &TestCase::failed_test_count); } +// Gets the number of disabled tests that will be reported in the XML report. +int UnitTestImpl::reportable_disabled_test_count() const { + return SumOverTestCaseList(test_cases_, + &TestCase::reportable_disabled_test_count); +} + // Gets the number of disabled tests. int UnitTestImpl::disabled_test_count() const { return SumOverTestCaseList(test_cases_, &TestCase::disabled_test_count); } +// Gets the number of tests to be printed in the XML report. +int UnitTestImpl::reportable_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::reportable_test_count); +} + // Gets the number of all tests. int UnitTestImpl::total_test_count() const { return SumOverTestCaseList(test_cases_, &TestCase::total_test_count); @@ -746,7 +767,7 @@ int UnitTestImpl::test_to_run_count() const { return SumOverTestCaseList(test_cases_, &TestCase::test_to_run_count); } -// Returns the current OS stack trace as a String. +// Returns the current OS stack trace as an std::string. // // The maximum number of stack frames to be included is specified by // the gtest_stack_trace_depth flag. The skip_count parameter @@ -756,9 +777,9 @@ int UnitTestImpl::test_to_run_count() const { // For example, if Foo() calls Bar(), which in turn calls // CurrentOsStackTraceExceptTop(1), Foo() will be included in the // trace but Bar() and CurrentOsStackTraceExceptTop() won't. -String UnitTestImpl::CurrentOsStackTraceExceptTop(int skip_count) { +std::string UnitTestImpl::CurrentOsStackTraceExceptTop(int skip_count) { (void)skip_count; - return String(""); + return ""; } // Returns the current time in milliseconds. @@ -787,21 +808,13 @@ TimeInMillis GetTimeInMillis() { #elif GTEST_OS_WINDOWS && !GTEST_HAS_GETTIMEOFDAY_ __timeb64 now; -# ifdef _MSC_VER - // MSVC 8 deprecates _ftime64(), so we want to suppress warning 4996 // (deprecated function) there. // TODO(kenton@google.com): Use GetTickCount()? Or use // SystemTimeToFileTime() -# pragma warning(push) // Saves the current warning state. -# pragma warning(disable:4996) // Temporarily disables warning 4996. + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4996) _ftime64(&now); -# pragma warning(pop) // Restores the warning state. -# else - - _ftime64(&now); - -# endif // _MSC_VER + GTEST_DISABLE_MSC_WARNINGS_POP_() return static_cast<TimeInMillis>(now.time) * 1000 + now.millitm; #elif GTEST_HAS_GETTIMEOFDAY_ @@ -815,41 +828,7 @@ TimeInMillis GetTimeInMillis() { // Utilities -// class String - -// Returns the input enclosed in double quotes if it's not NULL; -// otherwise returns "(null)". For example, "\"Hello\"" is returned -// for input "Hello". -// -// This is useful for printing a C string in the syntax of a literal. -// -// Known issue: escape sequences are not handled yet. -String String::ShowCStringQuoted(const char* c_str) { - return c_str ? String::Format("\"%s\"", c_str) : String("(null)"); -} - -// Copies at most length characters from str into a newly-allocated -// piece of memory of size length+1. The memory is allocated with new[]. -// A terminating null byte is written to the memory, and a pointer to it -// is returned. If str is NULL, NULL is returned. -static char* CloneString(const char* str, size_t length) { - if (str == NULL) { - return NULL; - } else { - char* const clone = new char[length + 1]; - posix::StrNCpy(clone, str, length); - clone[length] = '\0'; - return clone; - } -} - -// Clones a 0-terminated C string, allocating memory using new. The -// caller is responsible for deleting[] the return value. Returns the -// cloned string, or NULL if the input is NULL. -const char * String::CloneCString(const char* c_str) { - return (c_str == NULL) ? - NULL : CloneString(c_str, strlen(c_str)); -} +// class String. #if GTEST_OS_WINDOWS_MOBILE // Creates a UTF-16 wide string from the given ANSI string, allocating @@ -906,11 +885,6 @@ bool String::CStringEquals(const char * lhs, const char * rhs) { // encoding, and streams the result to the given Message object. static void StreamWideCharsToMessage(const wchar_t* wstr, size_t length, Message* msg) { - // TODO(wan): consider allowing a testing::String object to - // contain '\0'. This will make it behave more like std::string, - // and will allow ToUtf8String() to return the correct encoding - // for '\0' s.t. we can get rid of the conditional here (and in - // several other places). for (size_t i = 0; i != length; ) { // NOLINT if (wstr[i] != L'\0') { *msg << WideStringToUtf8(wstr + i, static_cast<int>(length - i)); @@ -927,6 +901,26 @@ static void StreamWideCharsToMessage(const wchar_t* wstr, size_t length, } // namespace internal +// Constructs an empty Message. +// We allocate the stringstream separately because otherwise each use of +// ASSERT/EXPECT in a procedure adds over 200 bytes to the procedure's +// stack frame leading to huge stack frames in some cases; gcc does not reuse +// the stack space. +Message::Message() : ss_(new ::std::stringstream) { + // By default, we want there to be enough precision when printing + // a double to a Message. + *ss_ << std::setprecision(std::numeric_limits<double>::digits10 + 2); +} + +// These two overloads allow streaming a wide C string to a Message +// using the UTF-8 encoding. +Message& Message::operator <<(const wchar_t* wide_c_str) { + return *this << internal::String::ShowWideCString(wide_c_str); +} +Message& Message::operator <<(wchar_t* wide_c_str) { + return *this << internal::String::ShowWideCString(wide_c_str); +} + #if GTEST_HAS_STD_WSTRING // Converts the given wide string to a narrow string using the UTF-8 // encoding, and streams the result to this Message object. @@ -945,6 +939,12 @@ Message& Message::operator <<(const ::wstring& wstr) { } #endif // GTEST_HAS_GLOBAL_WSTRING +// Gets the text streamed to this object so far as an std::string. +// Each '\0' character in the buffer is replaced with "\\0". +std::string Message::GetString() const { + return internal::StringStreamToString(ss_.get()); +} + // AssertionResult constructors. // Used in EXPECT_TRUE/FALSE(assertion_result). AssertionResult::AssertionResult(const AssertionResult& other) @@ -954,6 +954,13 @@ AssertionResult::AssertionResult(const AssertionResult& other) static_cast< ::std::string*>(NULL)) { } +// Swaps two AssertionResults. +void AssertionResult::swap(AssertionResult& other) { + using std::swap; + swap(success_, other.success_); + swap(message_, other.message_); +} + // Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE. AssertionResult AssertionResult::operator!() const { AssertionResult negation(!success_); @@ -980,6 +987,276 @@ AssertionResult AssertionFailure(const Message& message) { namespace internal { +namespace edit_distance { +std::vector<EditType> CalculateOptimalEdits(const std::vector<size_t>& left, + const std::vector<size_t>& right) { + std::vector<std::vector<double> > costs( + left.size() + 1, std::vector<double>(right.size() + 1)); + std::vector<std::vector<EditType> > best_move( + left.size() + 1, std::vector<EditType>(right.size() + 1)); + + // Populate for empty right. + for (size_t l_i = 0; l_i < costs.size(); ++l_i) { + costs[l_i][0] = static_cast<double>(l_i); + best_move[l_i][0] = kRemove; + } + // Populate for empty left. + for (size_t r_i = 1; r_i < costs[0].size(); ++r_i) { + costs[0][r_i] = static_cast<double>(r_i); + best_move[0][r_i] = kAdd; + } + + for (size_t l_i = 0; l_i < left.size(); ++l_i) { + for (size_t r_i = 0; r_i < right.size(); ++r_i) { + if (left[l_i] == right[r_i]) { + // Found a match. Consume it. + costs[l_i + 1][r_i + 1] = costs[l_i][r_i]; + best_move[l_i + 1][r_i + 1] = kMatch; + continue; + } + + const double add = costs[l_i + 1][r_i]; + const double remove = costs[l_i][r_i + 1]; + const double replace = costs[l_i][r_i]; + if (add < remove && add < replace) { + costs[l_i + 1][r_i + 1] = add + 1; + best_move[l_i + 1][r_i + 1] = kAdd; + } else if (remove < add && remove < replace) { + costs[l_i + 1][r_i + 1] = remove + 1; + best_move[l_i + 1][r_i + 1] = kRemove; + } else { + // We make replace a little more expensive than add/remove to lower + // their priority. + costs[l_i + 1][r_i + 1] = replace + 1.00001; + best_move[l_i + 1][r_i + 1] = kReplace; + } + } + } + + // Reconstruct the best path. We do it in reverse order. + std::vector<EditType> best_path; + for (size_t l_i = left.size(), r_i = right.size(); l_i > 0 || r_i > 0;) { + EditType move = best_move[l_i][r_i]; + best_path.push_back(move); + l_i -= move != kAdd; + r_i -= move != kRemove; + } + std::reverse(best_path.begin(), best_path.end()); + return best_path; +} + +namespace { + +// Helper class to convert string into ids with deduplication. +class InternalStrings { + public: + size_t GetId(const std::string& str) { + IdMap::iterator it = ids_.find(str); + if (it != ids_.end()) return it->second; + size_t id = ids_.size(); + return ids_[str] = id; + } + + private: + typedef std::map<std::string, size_t> IdMap; + IdMap ids_; +}; + +} // namespace + +std::vector<EditType> CalculateOptimalEdits( + const std::vector<std::string>& left, + const std::vector<std::string>& right) { + std::vector<size_t> left_ids, right_ids; + { + InternalStrings intern_table; + for (size_t i = 0; i < left.size(); ++i) { + left_ids.push_back(intern_table.GetId(left[i])); + } + for (size_t i = 0; i < right.size(); ++i) { + right_ids.push_back(intern_table.GetId(right[i])); + } + } + return CalculateOptimalEdits(left_ids, right_ids); +} + +namespace { + +// Helper class that holds the state for one hunk and prints it out to the +// stream. +// It reorders adds/removes when possible to group all removes before all +// adds. It also adds the hunk header before printint into the stream. +class Hunk { + public: + Hunk(size_t left_start, size_t right_start) + : left_start_(left_start), + right_start_(right_start), + adds_(), + removes_(), + common_() {} + + void PushLine(char edit, const char* line) { + switch (edit) { + case ' ': + ++common_; + FlushEdits(); + hunk_.push_back(std::make_pair(' ', line)); + break; + case '-': + ++removes_; + hunk_removes_.push_back(std::make_pair('-', line)); + break; + case '+': + ++adds_; + hunk_adds_.push_back(std::make_pair('+', line)); + break; + } + } + + void PrintTo(std::ostream* os) { + PrintHeader(os); + FlushEdits(); + for (std::list<std::pair<char, const char*> >::const_iterator it = + hunk_.begin(); + it != hunk_.end(); ++it) { + *os << it->first << it->second << "\n"; + } + } + + bool has_edits() const { return adds_ || removes_; } + + private: + void FlushEdits() { + hunk_.splice(hunk_.end(), hunk_removes_); + hunk_.splice(hunk_.end(), hunk_adds_); + } + + // Print a unified diff header for one hunk. + // The format is + // "@@ -<left_start>,<left_length> +<right_start>,<right_length> @@" + // where the left/right parts are ommitted if unnecessary. + void PrintHeader(std::ostream* ss) const { + *ss << "@@ "; + if (removes_) { + *ss << "-" << left_start_ << "," << (removes_ + common_); + } + if (removes_ && adds_) { + *ss << " "; + } + if (adds_) { + *ss << "+" << right_start_ << "," << (adds_ + common_); + } + *ss << " @@\n"; + } + + size_t left_start_, right_start_; + size_t adds_, removes_, common_; + std::list<std::pair<char, const char*> > hunk_, hunk_adds_, hunk_removes_; +}; + +} // namespace + +// Create a list of diff hunks in Unified diff format. +// Each hunk has a header generated by PrintHeader above plus a body with +// lines prefixed with ' ' for no change, '-' for deletion and '+' for +// addition. +// 'context' represents the desired unchanged prefix/suffix around the diff. +// If two hunks are close enough that their contexts overlap, then they are +// joined into one hunk. +std::string CreateUnifiedDiff(const std::vector<std::string>& left, + const std::vector<std::string>& right, + size_t context) { + const std::vector<EditType> edits = CalculateOptimalEdits(left, right); + + size_t l_i = 0, r_i = 0, edit_i = 0; + std::stringstream ss; + while (edit_i < edits.size()) { + // Find first edit. + while (edit_i < edits.size() && edits[edit_i] == kMatch) { + ++l_i; + ++r_i; + ++edit_i; + } + + // Find the first line to include in the hunk. + const size_t prefix_context = std::min(l_i, context); + Hunk hunk(l_i - prefix_context + 1, r_i - prefix_context + 1); + for (size_t i = prefix_context; i > 0; --i) { + hunk.PushLine(' ', left[l_i - i].c_str()); + } + + // Iterate the edits until we found enough suffix for the hunk or the input + // is over. + size_t n_suffix = 0; + for (; edit_i < edits.size(); ++edit_i) { + if (n_suffix >= context) { + // Continue only if the next hunk is very close. + std::vector<EditType>::const_iterator it = edits.begin() + edit_i; + while (it != edits.end() && *it == kMatch) ++it; + if (it == edits.end() || (it - edits.begin()) - edit_i >= context) { + // There is no next edit or it is too far away. + break; + } + } + + EditType edit = edits[edit_i]; + // Reset count when a non match is found. + n_suffix = edit == kMatch ? n_suffix + 1 : 0; + + if (edit == kMatch || edit == kRemove || edit == kReplace) { + hunk.PushLine(edit == kMatch ? ' ' : '-', left[l_i].c_str()); + } + if (edit == kAdd || edit == kReplace) { + hunk.PushLine('+', right[r_i].c_str()); + } + + // Advance indices, depending on edit type. + l_i += edit != kAdd; + r_i += edit != kRemove; + } + + if (!hunk.has_edits()) { + // We are done. We don't want this hunk. + break; + } + + hunk.PrintTo(&ss); + } + return ss.str(); +} + +} // namespace edit_distance + +namespace { + +// The string representation of the values received in EqFailure() are already +// escaped. Split them on escaped '\n' boundaries. Leave all other escaped +// characters the same. +std::vector<std::string> SplitEscapedString(const std::string& str) { + std::vector<std::string> lines; + size_t start = 0, end = str.size(); + if (end > 2 && str[0] == '"' && str[end - 1] == '"') { + ++start; + --end; + } + bool escaped = false; + for (size_t i = start; i + 1 < end; ++i) { + if (escaped) { + escaped = false; + if (str[i] == 'n') { + lines.push_back(str.substr(start, i - start - 1)); + start = i + 1; + } + } else { + escaped = str[i] == '\\'; + } + } + lines.push_back(str.substr(start, end - start)); + return lines; +} + +} // namespace + // Constructs and returns the message for an equality assertion // (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure. // @@ -997,8 +1274,8 @@ namespace internal { // be inserted into the message. AssertionResult EqFailure(const char* expected_expression, const char* actual_expression, - const String& expected_value, - const String& actual_value, + const std::string& expected_value, + const std::string& actual_value, bool ignoring_case) { Message msg; msg << "Value of: " << actual_expression; @@ -1014,14 +1291,26 @@ AssertionResult EqFailure(const char* expected_expression, msg << "\nWhich is: " << expected_value; } + if (!expected_value.empty() && !actual_value.empty()) { + const std::vector<std::string> expected_lines = + SplitEscapedString(expected_value); + const std::vector<std::string> actual_lines = + SplitEscapedString(actual_value); + if (expected_lines.size() > 1 || actual_lines.size() > 1) { + msg << "\nWith diff:\n" + << edit_distance::CreateUnifiedDiff(expected_lines, actual_lines); + } + } + return AssertionFailure() << msg; } // Constructs a failure message for Boolean assertions such as EXPECT_TRUE. -String GetBoolAssertionFailureMessage(const AssertionResult& assertion_result, - const char* expression_text, - const char* actual_predicate_value, - const char* expected_predicate_value) { +std::string GetBoolAssertionFailureMessage( + const AssertionResult& assertion_result, + const char* expression_text, + const char* actual_predicate_value, + const char* expected_predicate_value) { const char* actual_message = assertion_result.message(); Message msg; msg << "Value of: " << expression_text @@ -1168,8 +1457,8 @@ AssertionResult CmpHelperSTREQ(const char* expected_expression, return EqFailure(expected_expression, actual_expression, - String::ShowCStringQuoted(expected), - String::ShowCStringQuoted(actual), + PrintToString(expected), + PrintToString(actual), false); } @@ -1184,8 +1473,8 @@ AssertionResult CmpHelperSTRCASEEQ(const char* expected_expression, return EqFailure(expected_expression, actual_expression, - String::ShowCStringQuoted(expected), - String::ShowCStringQuoted(actual), + PrintToString(expected), + PrintToString(actual), true); } @@ -1349,7 +1638,7 @@ AssertionResult HRESULTFailureHelper(const char* expr, // want inserts expanded. const DWORD kFlags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; - const DWORD kBufSize = 4096; // String::Format can't exceed this length. + const DWORD kBufSize = 4096; // Gets the system's human readable message string for this HRESULT. char error_text[kBufSize] = { '\0' }; DWORD message_length = ::FormatMessageA(kFlags, @@ -1359,7 +1648,7 @@ AssertionResult HRESULTFailureHelper(const char* expr, error_text, // output buffer kBufSize, // buf size NULL); // no arguments for inserts - // Trims tailing white space (FormatMessage leaves a trailing cr-lf) + // Trims tailing white space (FormatMessage leaves a trailing CR-LF) for (; message_length && IsSpace(error_text[message_length - 1]); --message_length) { error_text[message_length - 1] = '\0'; @@ -1367,10 +1656,10 @@ AssertionResult HRESULTFailureHelper(const char* expr, # endif // GTEST_OS_WINDOWS_MOBILE - const String error_hex(String::Format("0x%08X ", hr)); + const std::string error_hex("0x" + String::FormatHexInt(hr)); return ::testing::AssertionFailure() << "Expected: " << expr << " " << expected << ".\n" - << " Actual: " << error_hex << error_text << "\n"; + << " Actual: " << error_hex << " " << error_text << "\n"; } } // namespace @@ -1427,12 +1716,15 @@ inline UInt32 ChopLowBits(UInt32* bits, int n) { // Converts a Unicode code point to a narrow string in UTF-8 encoding. // code_point parameter is of type UInt32 because wchar_t may not be // wide enough to contain a code point. -// The output buffer str must containt at least 32 characters. -// The function returns the address of the output buffer. // If the code_point is not a valid Unicode code point -// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be output -// as '(Invalid Unicode 0xXXXXXXXX)'. -char* CodePointToUtf8(UInt32 code_point, char* str) { +// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be converted +// to "(Invalid Unicode 0xXXXXXXXX)". +std::string CodePointToUtf8(UInt32 code_point) { + if (code_point > kMaxCodePoint4) { + return "(Invalid Unicode 0x" + String::FormatHexInt(code_point) + ")"; + } + + char str[5]; // Big enough for the largest valid code point. if (code_point <= kMaxCodePoint1) { str[1] = '\0'; str[0] = static_cast<char>(code_point); // 0xxxxxxx @@ -1445,22 +1737,12 @@ char* CodePointToUtf8(UInt32 code_point, char* str) { str[2] = static_cast<char>(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx str[1] = static_cast<char>(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx str[0] = static_cast<char>(0xE0 | code_point); // 1110xxxx - } else if (code_point <= kMaxCodePoint4) { + } else { // code_point <= kMaxCodePoint4 str[4] = '\0'; str[3] = static_cast<char>(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx str[2] = static_cast<char>(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx str[1] = static_cast<char>(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx str[0] = static_cast<char>(0xF0 | code_point); // 11110xxx - } else { - // The longest string String::Format can produce when invoked - // with these parameters is 28 character long (not including - // the terminating nul character). We are asking for 32 character - // buffer just in case. This is also enough for strncpy to - // null-terminate the destination string. - posix::StrNCpy( - str, String::Format("(Invalid Unicode 0x%X)", code_point).c_str(), 32); - str[31] = '\0'; // Makes sure no change in the format to strncpy leaves - // the result unterminated. } return str; } @@ -1501,7 +1783,7 @@ inline UInt32 CreateCodePointFromUtf16SurrogatePair(wchar_t first, // as '(Invalid Unicode 0xXXXXXXXX)'. If the string is in UTF16 encoding // and contains invalid UTF-16 surrogate pairs, values in those pairs // will be encoded as individual Unicode characters from Basic Normal Plane. -String WideStringToUtf8(const wchar_t* str, int num_chars) { +std::string WideStringToUtf8(const wchar_t* str, int num_chars) { if (num_chars == -1) num_chars = static_cast<int>(wcslen(str)); @@ -1519,27 +1801,17 @@ String WideStringToUtf8(const wchar_t* str, int num_chars) { unicode_code_point = static_cast<UInt32>(str[i]); } - char buffer[32]; // CodePointToUtf8 requires a buffer this big. - stream << CodePointToUtf8(unicode_code_point, buffer); + stream << CodePointToUtf8(unicode_code_point); } return StringStreamToString(&stream); } -// Converts a wide C string to a String using the UTF-8 encoding. +// Converts a wide C string to an std::string using the UTF-8 encoding. // NULL will be converted to "(null)". -String String::ShowWideCString(const wchar_t * wide_c_str) { - if (wide_c_str == NULL) return String("(null)"); - - return String(internal::WideStringToUtf8(wide_c_str, -1).c_str()); -} - -// Similar to ShowWideCString(), except that this function encloses -// the converted string in double quotes. -String String::ShowWideCStringQuoted(const wchar_t* wide_c_str) { - if (wide_c_str == NULL) return String("(null)"); +std::string String::ShowWideCString(const wchar_t * wide_c_str) { + if (wide_c_str == NULL) return "(null)"; - return String::Format("L\"%s\"", - String::ShowWideCString(wide_c_str).c_str()); + return internal::WideStringToUtf8(wide_c_str, -1); } // Compares two wide C strings. Returns true iff they have the same @@ -1567,8 +1839,8 @@ AssertionResult CmpHelperSTREQ(const char* expected_expression, return EqFailure(expected_expression, actual_expression, - String::ShowWideCStringQuoted(expected), - String::ShowWideCStringQuoted(actual), + PrintToString(expected), + PrintToString(actual), false); } @@ -1583,8 +1855,8 @@ AssertionResult CmpHelperSTRNE(const char* s1_expression, return AssertionFailure() << "Expected: (" << s1_expression << ") != (" << s2_expression << "), actual: " - << String::ShowWideCStringQuoted(s1) - << " vs " << String::ShowWideCStringQuoted(s2); + << PrintToString(s1) + << " vs " << PrintToString(s2); } // Compares two C strings, ignoring case. Returns true iff they have @@ -1635,135 +1907,69 @@ bool String::CaseInsensitiveWideCStringEquals(const wchar_t* lhs, #endif // OS selector } -// Compares this with another String. -// Returns < 0 if this is less than rhs, 0 if this is equal to rhs, or > 0 -// if this is greater than rhs. -int String::Compare(const String & rhs) const { - const char* const lhs_c_str = c_str(); - const char* const rhs_c_str = rhs.c_str(); - - if (lhs_c_str == NULL) { - return rhs_c_str == NULL ? 0 : -1; // NULL < anything except NULL - } else if (rhs_c_str == NULL) { - return 1; - } - - const size_t shorter_str_len = - length() <= rhs.length() ? length() : rhs.length(); - for (size_t i = 0; i != shorter_str_len; i++) { - if (lhs_c_str[i] < rhs_c_str[i]) { - return -1; - } else if (lhs_c_str[i] > rhs_c_str[i]) { - return 1; - } - } - return (length() < rhs.length()) ? -1 : - (length() > rhs.length()) ? 1 : 0; +// Returns true iff str ends with the given suffix, ignoring case. +// Any string is considered to end with an empty suffix. +bool String::EndsWithCaseInsensitive( + const std::string& str, const std::string& suffix) { + const size_t str_len = str.length(); + const size_t suffix_len = suffix.length(); + return (str_len >= suffix_len) && + CaseInsensitiveCStringEquals(str.c_str() + str_len - suffix_len, + suffix.c_str()); } -// Returns true iff this String ends with the given suffix. *Any* -// String is considered to end with a NULL or empty suffix. -bool String::EndsWith(const char* suffix) const { - if (suffix == NULL || CStringEquals(suffix, "")) return true; - - if (c_str() == NULL) return false; - - const size_t this_len = strlen(c_str()); - const size_t suffix_len = strlen(suffix); - return (this_len >= suffix_len) && - CStringEquals(c_str() + this_len - suffix_len, suffix); +// Formats an int value as "%02d". +std::string String::FormatIntWidth2(int value) { + std::stringstream ss; + ss << std::setfill('0') << std::setw(2) << value; + return ss.str(); } -// Returns true iff this String ends with the given suffix, ignoring case. -// Any String is considered to end with a NULL or empty suffix. -bool String::EndsWithCaseInsensitive(const char* suffix) const { - if (suffix == NULL || CStringEquals(suffix, "")) return true; - - if (c_str() == NULL) return false; - - const size_t this_len = strlen(c_str()); - const size_t suffix_len = strlen(suffix); - return (this_len >= suffix_len) && - CaseInsensitiveCStringEquals(c_str() + this_len - suffix_len, suffix); +// Formats an int value as "%X". +std::string String::FormatHexInt(int value) { + std::stringstream ss; + ss << std::hex << std::uppercase << value; + return ss.str(); } -// Formats a list of arguments to a String, using the same format -// spec string as for printf. -// -// We do not use the StringPrintf class as it is not universally -// available. -// -// The result is limited to 4096 characters (including the tailing 0). -// If 4096 characters are not enough to format the input, or if -// there's an error, "<formatting error or buffer exceeded>" is -// returned. -String String::Format(const char * format, ...) { - va_list args; - va_start(args, format); - - char buffer[4096]; - const int kBufferSize = sizeof(buffer)/sizeof(buffer[0]); - - // MSVC 8 deprecates vsnprintf(), so we want to suppress warning - // 4996 (deprecated function) there. -#ifdef _MSC_VER // We are using MSVC. -# pragma warning(push) // Saves the current warning state. -# pragma warning(disable:4996) // Temporarily disables warning 4996. - - const int size = vsnprintf(buffer, kBufferSize, format, args); - -# pragma warning(pop) // Restores the warning state. -#else // We are not using MSVC. - const int size = vsnprintf(buffer, kBufferSize, format, args); -#endif // _MSC_VER - va_end(args); - - // vsnprintf()'s behavior is not portable. When the buffer is not - // big enough, it returns a negative value in MSVC, and returns the - // needed buffer size on Linux. When there is an output error, it - // always returns a negative value. For simplicity, we lump the two - // error cases together. - if (size < 0 || size >= kBufferSize) { - return String("<formatting error or buffer exceeded>"); - } else { - return String(buffer, size); - } +// Formats a byte as "%02X". +std::string String::FormatByte(unsigned char value) { + std::stringstream ss; + ss << std::setfill('0') << std::setw(2) << std::hex << std::uppercase + << static_cast<unsigned int>(value); + return ss.str(); } -// Converts the buffer in a stringstream to a String, converting NUL +// Converts the buffer in a stringstream to an std::string, converting NUL // bytes to "\\0" along the way. -String StringStreamToString(::std::stringstream* ss) { +std::string StringStreamToString(::std::stringstream* ss) { const ::std::string& str = ss->str(); const char* const start = str.c_str(); const char* const end = start + str.length(); - // We need to use a helper stringstream to do this transformation - // because String doesn't support push_back(). - ::std::stringstream helper; + std::string result; + result.reserve(2 * (end - start)); for (const char* ch = start; ch != end; ++ch) { if (*ch == '\0') { - helper << "\\0"; // Replaces NUL with "\\0"; + result += "\\0"; // Replaces NUL with "\\0"; } else { - helper.put(*ch); + result += *ch; } } - return String(helper.str().c_str()); + return result; } // Appends the user-supplied message to the Google-Test-generated message. -String AppendUserMessage(const String& gtest_msg, - const Message& user_msg) { +std::string AppendUserMessage(const std::string& gtest_msg, + const Message& user_msg) { // Appends the user message if it's non-empty. - const String user_msg_string = user_msg.GetString(); + const std::string user_msg_string = user_msg.GetString(); if (user_msg_string.empty()) { return gtest_msg; } - Message msg; - msg << gtest_msg << "\n" << user_msg_string; - - return msg.GetString(); + return gtest_msg + "\n" + user_msg_string; } } // namespace internal @@ -1811,8 +2017,9 @@ void TestResult::AddTestPartResult(const TestPartResult& test_part_result) { // Adds a test property to the list. If a property with the same key as the // supplied property is already represented, the value of this test_property // replaces the old value for that key. -void TestResult::RecordProperty(const TestProperty& test_property) { - if (!ValidateTestProperty(test_property)) { +void TestResult::RecordProperty(const std::string& xml_element, + const TestProperty& test_property) { + if (!ValidateTestProperty(xml_element, test_property)) { return; } internal::MutexLock lock(&test_properites_mutex_); @@ -1826,21 +2033,94 @@ void TestResult::RecordProperty(const TestProperty& test_property) { property_with_matching_key->SetValue(test_property.value()); } -// Adds a failure if the key is a reserved attribute of Google Test -// testcase tags. Returns true if the property is valid. -bool TestResult::ValidateTestProperty(const TestProperty& test_property) { - internal::String key(test_property.key()); - if (key == "name" || key == "status" || key == "time" || key == "classname") { - ADD_FAILURE() - << "Reserved key used in RecordProperty(): " - << key - << " ('name', 'status', 'time', and 'classname' are reserved by " - << GTEST_NAME_ << ")"; +// The list of reserved attributes used in the <testsuites> element of XML +// output. +static const char* const kReservedTestSuitesAttributes[] = { + "disabled", + "errors", + "failures", + "name", + "random_seed", + "tests", + "time", + "timestamp" +}; + +// The list of reserved attributes used in the <testsuite> element of XML +// output. +static const char* const kReservedTestSuiteAttributes[] = { + "disabled", + "errors", + "failures", + "name", + "tests", + "time" +}; + +// The list of reserved attributes used in the <testcase> element of XML output. +static const char* const kReservedTestCaseAttributes[] = { + "classname", + "name", + "status", + "time", + "type_param", + "value_param" +}; + +template <int kSize> +std::vector<std::string> ArrayAsVector(const char* const (&array)[kSize]) { + return std::vector<std::string>(array, array + kSize); +} + +static std::vector<std::string> GetReservedAttributesForElement( + const std::string& xml_element) { + if (xml_element == "testsuites") { + return ArrayAsVector(kReservedTestSuitesAttributes); + } else if (xml_element == "testsuite") { + return ArrayAsVector(kReservedTestSuiteAttributes); + } else if (xml_element == "testcase") { + return ArrayAsVector(kReservedTestCaseAttributes); + } else { + GTEST_CHECK_(false) << "Unrecognized xml_element provided: " << xml_element; + } + // This code is unreachable but some compilers may not realizes that. + return std::vector<std::string>(); +} + +static std::string FormatWordList(const std::vector<std::string>& words) { + Message word_list; + for (size_t i = 0; i < words.size(); ++i) { + if (i > 0 && words.size() > 2) { + word_list << ", "; + } + if (i == words.size() - 1) { + word_list << "and "; + } + word_list << "'" << words[i] << "'"; + } + return word_list.GetString(); +} + +bool ValidateTestPropertyName(const std::string& property_name, + const std::vector<std::string>& reserved_names) { + if (std::find(reserved_names.begin(), reserved_names.end(), property_name) != + reserved_names.end()) { + ADD_FAILURE() << "Reserved key used in RecordProperty(): " << property_name + << " (" << FormatWordList(reserved_names) + << " are reserved by " << GTEST_NAME_ << ")"; return false; } return true; } +// Adds a failure if the key is a reserved attribute of the element named +// xml_element. Returns true if the property is valid. +bool TestResult::ValidateTestProperty(const std::string& xml_element, + const TestProperty& test_property) { + return ValidateTestPropertyName(test_property.key(), + GetReservedAttributesForElement(xml_element)); +} + // Clears the object. void TestResult::Clear() { test_part_results_.clear(); @@ -1916,12 +2196,12 @@ void Test::TearDown() { } // Allows user supplied key value pairs to be recorded for later output. -void Test::RecordProperty(const char* key, const char* value) { - UnitTest::GetInstance()->RecordPropertyForCurrentTest(key, value); +void Test::RecordProperty(const std::string& key, const std::string& value) { + UnitTest::GetInstance()->RecordProperty(key, value); } // Allows user supplied key value pairs to be recorded for later output. -void Test::RecordProperty(const char* key, int value) { +void Test::RecordProperty(const std::string& key, int value) { Message value_message; value_message << value; RecordProperty(key, value_message.GetString().c_str()); @@ -1930,7 +2210,7 @@ void Test::RecordProperty(const char* key, int value) { namespace internal { void ReportFailureInUnknownLocation(TestPartResult::Type result_type, - const String& message) { + const std::string& message) { // This function is a friend of UnitTest and as such has access to // AddTestPartResult. UnitTest::GetInstance()->AddTestPartResult( @@ -1938,7 +2218,7 @@ void ReportFailureInUnknownLocation(TestPartResult::Type result_type, NULL, // No info about the source file where the exception occurred. -1, // We have no info on which line caused the exception. message, - String()); // No stack trace, either. + ""); // No stack trace, either. } } // namespace internal @@ -1969,8 +2249,8 @@ bool Test::HasSameFixtureClass() { const bool this_is_TEST = this_fixture_id == internal::GetTestTypeId(); if (first_is_TEST || this_is_TEST) { - // The user mixed TEST and TEST_F in this test case - we'll tell - // him/her how to fix it. + // Both TEST and TEST_F appear in same test case, which is incorrect. + // Tell the user how to fix this. // Gets the name of the TEST and the name of the TEST_F. Note // that first_is_TEST and this_is_TEST cannot both be true, as @@ -1990,8 +2270,8 @@ bool Test::HasSameFixtureClass() { << "want to change the TEST to TEST_F or move it to another test\n" << "case."; } else { - // The user defined two fixture classes with the same name in - // two namespaces - we'll tell him/her how to fix it. + // Two fixture classes with the same name appear in two different + // namespaces, which is not allowed. Tell the user how to fix this. ADD_FAILURE() << "All tests in the same test case must use the same test fixture\n" << "class. However, in test case " @@ -2015,22 +2295,24 @@ bool Test::HasSameFixtureClass() { // function returns its result via an output parameter pointer because VC++ // prohibits creation of objects with destructors on stack in functions // using __try (see error C2712). -static internal::String* FormatSehExceptionMessage(DWORD exception_code, - const char* location) { +static std::string* FormatSehExceptionMessage(DWORD exception_code, + const char* location) { Message message; message << "SEH exception with code 0x" << std::setbase(16) << exception_code << std::setbase(10) << " thrown in " << location << "."; - return new internal::String(message.GetString()); + return new std::string(message.GetString()); } #endif // GTEST_HAS_SEH +namespace internal { + #if GTEST_HAS_EXCEPTIONS // Adds an "exception thrown" fatal failure to the current test. -static internal::String FormatCxxExceptionMessage(const char* description, - const char* location) { +static std::string FormatCxxExceptionMessage(const char* description, + const char* location) { Message message; if (description != NULL) { message << "C++ exception with description \"" << description << "\""; @@ -2042,23 +2324,15 @@ static internal::String FormatCxxExceptionMessage(const char* description, return message.GetString(); } -static internal::String PrintTestPartResultToString( +static std::string PrintTestPartResultToString( const TestPartResult& test_part_result); -// A failed Google Test assertion will throw an exception of this type when -// GTEST_FLAG(throw_on_failure) is true (if exceptions are enabled). We -// derive it from std::runtime_error, which is for errors presumably -// detectable only at run time. Since std::runtime_error inherits from -// std::exception, many testing frameworks know how to extract and print the -// message inside it. -class GoogleTestFailureException : public ::std::runtime_error { - public: - explicit GoogleTestFailureException(const TestPartResult& failure) - : ::std::runtime_error(PrintTestPartResultToString(failure).c_str()) {} -}; +GoogleTestFailureException::GoogleTestFailureException( + const TestPartResult& failure) + : ::std::runtime_error(PrintTestPartResultToString(failure).c_str()) {} + #endif // GTEST_HAS_EXCEPTIONS -namespace internal { // We put these helper functions in the internal namespace as IBM's xlC // compiler rejects the code if they were declared static. @@ -2078,7 +2352,7 @@ Result HandleSehExceptionsInMethodIfSupported( // We create the exception message on the heap because VC++ prohibits // creation of objects with destructors on stack in functions using __try // (see error C2712). - internal::String* exception_message = FormatSehExceptionMessage( + std::string* exception_message = FormatSehExceptionMessage( GetExceptionCode(), location); internal::ReportFailureInUnknownLocation(TestPartResult::kFatalFailure, *exception_message); @@ -2124,9 +2398,10 @@ Result HandleExceptionsInMethodIfSupported( #if GTEST_HAS_EXCEPTIONS try { return HandleSehExceptionsInMethodIfSupported(object, method, location); - } catch (const GoogleTestFailureException&) { // NOLINT - // This exception doesn't originate in code under test. It makes no - // sense to report it as a test failure. + } catch (const internal::GoogleTestFailureException&) { // NOLINT + // This exception type can only be thrown by a failed Google + // Test assertion with the intention of letting another testing + // framework catch it. Therefore we just re-throw it. throw; } catch (const std::exception& e) { // NOLINT internal::ReportFailureInUnknownLocation( @@ -2185,10 +2460,8 @@ bool Test::HasNonfatalFailure() { // Constructs a TestInfo object. It assumes ownership of the test factory // object. -// TODO(vladl@google.com): Make a_test_case_name and a_name const string&'s -// to signify they cannot be NULLs. -TestInfo::TestInfo(const char* a_test_case_name, - const char* a_name, +TestInfo::TestInfo(const std::string& a_test_case_name, + const std::string& a_name, const char* a_type_param, const char* a_value_param, internal::TypeId fixture_class_id, @@ -2227,7 +2500,8 @@ namespace internal { // The newly created TestInfo instance will assume // ownership of the factory object. TestInfo* MakeAndRegisterTestInfo( - const char* test_case_name, const char* name, + const char* test_case_name, + const char* name, const char* type_param, const char* value_param, TypeId fixture_class_id, @@ -2282,11 +2556,11 @@ class TestNameIs { // Returns true iff the test name of test_info matches name_. bool operator()(const TestInfo * test_info) const { - return test_info && internal::String(test_info->name()).Compare(name_) == 0; + return test_info && test_info->name() == name_; } private: - internal::String name_; + std::string name_; }; } // namespace @@ -2365,10 +2639,21 @@ int TestCase::failed_test_count() const { return CountIf(test_info_list_, TestFailed); } +// Gets the number of disabled tests that will be reported in the XML report. +int TestCase::reportable_disabled_test_count() const { + return CountIf(test_info_list_, TestReportableDisabled); +} + +// Gets the number of disabled tests in this test case. int TestCase::disabled_test_count() const { return CountIf(test_info_list_, TestDisabled); } +// Gets the number of tests to be printed in the XML report. +int TestCase::reportable_test_count() const { + return CountIf(test_info_list_, TestReportable); +} + // Get the number of tests in this test case that should run. int TestCase::test_to_run_count() const { return CountIf(test_info_list_, ShouldRunTest); @@ -2456,6 +2741,7 @@ void TestCase::Run() { // Clears the results of all tests in this test case. void TestCase::ClearResult() { + ad_hoc_test_result_.Clear(); ForEach(test_info_list_, TestInfo::ClearTestResult); } @@ -2476,20 +2762,20 @@ void TestCase::UnshuffleTests() { // // FormatCountableNoun(1, "formula", "formuli") returns "1 formula". // FormatCountableNoun(5, "book", "books") returns "5 books". -static internal::String FormatCountableNoun(int count, - const char * singular_form, - const char * plural_form) { - return internal::String::Format("%d %s", count, - count == 1 ? singular_form : plural_form); +static std::string FormatCountableNoun(int count, + const char * singular_form, + const char * plural_form) { + return internal::StreamableToString(count) + " " + + (count == 1 ? singular_form : plural_form); } // Formats the count of tests. -static internal::String FormatTestCount(int test_count) { +static std::string FormatTestCount(int test_count) { return FormatCountableNoun(test_count, "test", "tests"); } // Formats the count of test cases. -static internal::String FormatTestCaseCount(int test_case_count) { +static std::string FormatTestCaseCount(int test_case_count) { return FormatCountableNoun(test_case_count, "test case", "test cases"); } @@ -2514,8 +2800,10 @@ static const char * TestPartResultTypeToString(TestPartResult::Type type) { } } -// Prints a TestPartResult to a String. -static internal::String PrintTestPartResultToString( +namespace internal { + +// Prints a TestPartResult to an std::string. +static std::string PrintTestPartResultToString( const TestPartResult& test_part_result) { return (Message() << internal::FormatFileLocation(test_part_result.file_name(), @@ -2526,7 +2814,7 @@ static internal::String PrintTestPartResultToString( // Prints a TestPartResult. static void PrintTestPartResult(const TestPartResult& test_part_result) { - const internal::String& result = + const std::string& result = PrintTestPartResultToString(test_part_result); printf("%s\n", result.c_str()); fflush(stdout); @@ -2545,8 +2833,6 @@ static void PrintTestPartResult(const TestPartResult& test_part_result) { // class PrettyUnitTestResultPrinter -namespace internal { - enum GTestColor { COLOR_DEFAULT, COLOR_RED, @@ -2554,7 +2840,8 @@ enum GTestColor { COLOR_YELLOW }; -#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE && \ + !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT // Returns the character attribute for the given color. WORD GetColorAttribute(GTestColor color) { @@ -2598,6 +2885,7 @@ bool ShouldUseColor(bool stdout_is_tty) { String::CStringEquals(term, "xterm-color") || String::CStringEquals(term, "xterm-256color") || String::CStringEquals(term, "screen") || + String::CStringEquals(term, "screen-256color") || String::CStringEquals(term, "linux") || String::CStringEquals(term, "cygwin"); return stdout_is_tty && term_supports_color; @@ -2621,8 +2909,9 @@ void ColoredPrintf(GTestColor color, const char* fmt, ...) { va_list args; va_start(args, fmt); -#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || GTEST_OS_ZOS - const bool use_color = false; +#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || GTEST_OS_ZOS || \ + GTEST_OS_IOS || GTEST_OS_WINDOWS_PHONE || GTEST_OS_WINDOWS_RT + const bool use_color = AlwaysFalse(); #else static const bool in_color_mode = ShouldUseColor(posix::IsATTY(posix::FileNo(stdout)) != 0); @@ -2636,7 +2925,8 @@ void ColoredPrintf(GTestColor color, const char* fmt, ...) { return; } -#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE && \ + !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT const HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); // Gets the current text color. @@ -2663,6 +2953,11 @@ void ColoredPrintf(GTestColor color, const char* fmt, ...) { va_end(args); } +// Text printed in Google Test's text output and --gunit_list_tests +// output to label the type parameter and value parameter for a test. +static const char kTypeParamLabel[] = "TypeParam"; +static const char kValueParamLabel[] = "GetParam()"; + void PrintFullTestCommentIfPresent(const TestInfo& test_info) { const char* const type_param = test_info.type_param(); const char* const value_param = test_info.value_param(); @@ -2670,12 +2965,12 @@ void PrintFullTestCommentIfPresent(const TestInfo& test_info) { if (type_param != NULL || value_param != NULL) { printf(", where "); if (type_param != NULL) { - printf("TypeParam = %s", type_param); + printf("%s = %s", kTypeParamLabel, type_param); if (value_param != NULL) printf(" and "); } if (value_param != NULL) { - printf("GetParam() = %s", value_param); + printf("%s = %s", kValueParamLabel, value_param); } } } @@ -2707,8 +3002,6 @@ class PrettyUnitTestResultPrinter : public TestEventListener { private: static void PrintFailedTests(const UnitTest& unit_test); - - internal::String test_case_name_; }; // Fired before each iteration of tests starts. @@ -2721,7 +3014,7 @@ void PrettyUnitTestResultPrinter::OnTestIterationStart( // Prints the filter if it's not *. This reminds the user that some // tests may be skipped. - if (!internal::String::CStringEquals(filter, kUniversalFilter)) { + if (!String::CStringEquals(filter, kUniversalFilter)) { ColoredPrintf(COLOR_YELLOW, "Note: %s filter = %s\n", GTEST_NAME_, filter); } @@ -2755,22 +3048,21 @@ void PrettyUnitTestResultPrinter::OnEnvironmentsSetUpStart( } void PrettyUnitTestResultPrinter::OnTestCaseStart(const TestCase& test_case) { - test_case_name_ = test_case.name(); - const internal::String counts = + const std::string counts = FormatCountableNoun(test_case.test_to_run_count(), "test", "tests"); ColoredPrintf(COLOR_GREEN, "[----------] "); - printf("%s from %s", counts.c_str(), test_case_name_.c_str()); + printf("%s from %s", counts.c_str(), test_case.name()); if (test_case.type_param() == NULL) { printf("\n"); } else { - printf(", where TypeParam = %s\n", test_case.type_param()); + printf(", where %s = %s\n", kTypeParamLabel, test_case.type_param()); } fflush(stdout); } void PrettyUnitTestResultPrinter::OnTestStart(const TestInfo& test_info) { ColoredPrintf(COLOR_GREEN, "[ RUN ] "); - PrintTestName(test_case_name_.c_str(), test_info.name()); + PrintTestName(test_info.test_case_name(), test_info.name()); printf("\n"); fflush(stdout); } @@ -2793,7 +3085,7 @@ void PrettyUnitTestResultPrinter::OnTestEnd(const TestInfo& test_info) { } else { ColoredPrintf(COLOR_RED, "[ FAILED ] "); } - PrintTestName(test_case_name_.c_str(), test_info.name()); + PrintTestName(test_info.test_case_name(), test_info.name()); if (test_info.result()->Failed()) PrintFullTestCommentIfPresent(test_info); @@ -2809,12 +3101,11 @@ void PrettyUnitTestResultPrinter::OnTestEnd(const TestInfo& test_info) { void PrettyUnitTestResultPrinter::OnTestCaseEnd(const TestCase& test_case) { if (!GTEST_FLAG(print_time)) return; - test_case_name_ = test_case.name(); - const internal::String counts = + const std::string counts = FormatCountableNoun(test_case.test_to_run_count(), "test", "tests"); ColoredPrintf(COLOR_GREEN, "[----------] "); printf("%s from %s (%s ms total)\n\n", - counts.c_str(), test_case_name_.c_str(), + counts.c_str(), test_case.name(), internal::StreamableToString(test_case.elapsed_time()).c_str()); fflush(stdout); } @@ -2875,7 +3166,7 @@ void PrettyUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, num_failures == 1 ? "TEST" : "TESTS"); } - int num_disabled = unit_test.disabled_test_count(); + int num_disabled = unit_test.reportable_disabled_test_count(); if (num_disabled && !GTEST_FLAG(also_run_disabled_tests)) { if (!num_failures) { printf("\n"); // Add a spacer if no FAILURE banner is displayed. @@ -3029,18 +3320,27 @@ class XmlUnitTestResultPrinter : public EmptyTestEventListener { // is_attribute is true, the text is meant to appear as an attribute // value, and normalizable whitespace is preserved by replacing it // with character references. - static String EscapeXml(const char* str, bool is_attribute); + static std::string EscapeXml(const std::string& str, bool is_attribute); // Returns the given string with all characters invalid in XML removed. - static string RemoveInvalidXmlCharacters(const string& str); + static std::string RemoveInvalidXmlCharacters(const std::string& str); // Convenience wrapper around EscapeXml when str is an attribute value. - static String EscapeXmlAttribute(const char* str) { + static std::string EscapeXmlAttribute(const std::string& str) { return EscapeXml(str, true); } // Convenience wrapper around EscapeXml when str is not an attribute value. - static String EscapeXmlText(const char* str) { return EscapeXml(str, false); } + static std::string EscapeXmlText(const char* str) { + return EscapeXml(str, false); + } + + // Verifies that the given attribute belongs to the given element and + // streams the attribute as XML. + static void OutputXmlAttribute(std::ostream* stream, + const std::string& element_name, + const std::string& name, + const std::string& value); // Streams an XML CDATA section, escaping invalid CDATA sequences as needed. static void OutputXmlCDataSection(::std::ostream* stream, const char* data); @@ -3051,19 +3351,21 @@ class XmlUnitTestResultPrinter : public EmptyTestEventListener { const TestInfo& test_info); // Prints an XML representation of a TestCase object - static void PrintXmlTestCase(FILE* out, const TestCase& test_case); + static void PrintXmlTestCase(::std::ostream* stream, + const TestCase& test_case); // Prints an XML summary of unit_test to output stream out. - static void PrintXmlUnitTest(FILE* out, const UnitTest& unit_test); + static void PrintXmlUnitTest(::std::ostream* stream, + const UnitTest& unit_test); // Produces a string representing the test properties in a result as space // delimited XML attributes based on the property key="value" pairs. - // When the String is not empty, it includes a space at the beginning, + // When the std::string is not empty, it includes a space at the beginning, // to delimit this attribute from prior attributes. - static String TestPropertiesAsXmlAttributes(const TestResult& result); + static std::string TestPropertiesAsXmlAttributes(const TestResult& result); // The output file. - const String output_file_; + const std::string output_file_; GTEST_DISALLOW_COPY_AND_ASSIGN_(XmlUnitTestResultPrinter); }; @@ -3105,7 +3407,9 @@ void XmlUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, fflush(stderr); exit(EXIT_FAILURE); } - PrintXmlUnitTest(xmlout, unit_test); + std::stringstream stream; + PrintXmlUnitTest(&stream, unit_test); + fprintf(xmlout, "%s", StringStreamToString(&stream).c_str()); fclose(xmlout); } @@ -3121,42 +3425,43 @@ void XmlUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, // most invalid characters can be retained using character references. // TODO(wan): It might be nice to have a minimally invasive, human-readable // escaping scheme for invalid characters, rather than dropping them. -String XmlUnitTestResultPrinter::EscapeXml(const char* str, bool is_attribute) { +std::string XmlUnitTestResultPrinter::EscapeXml( + const std::string& str, bool is_attribute) { Message m; - if (str != NULL) { - for (const char* src = str; *src; ++src) { - switch (*src) { - case '<': - m << "<"; - break; - case '>': - m << ">"; - break; - case '&': - m << "&"; - break; - case '\'': - if (is_attribute) - m << "'"; - else - m << '\''; - break; - case '"': - if (is_attribute) - m << """; + for (size_t i = 0; i < str.size(); ++i) { + const char ch = str[i]; + switch (ch) { + case '<': + m << "<"; + break; + case '>': + m << ">"; + break; + case '&': + m << "&"; + break; + case '\'': + if (is_attribute) + m << "'"; + else + m << '\''; + break; + case '"': + if (is_attribute) + m << """; + else + m << '"'; + break; + default: + if (IsValidXmlCharacter(ch)) { + if (is_attribute && IsNormalizableWhitespace(ch)) + m << "&#x" << String::FormatByte(static_cast<unsigned char>(ch)) + << ";"; else - m << '"'; - break; - default: - if (IsValidXmlCharacter(*src)) { - if (is_attribute && IsNormalizableWhitespace(*src)) - m << String::Format("&#x%02X;", unsigned(*src)); - else - m << *src; - } - break; - } + m << ch; + } + break; } } @@ -3166,10 +3471,11 @@ String XmlUnitTestResultPrinter::EscapeXml(const char* str, bool is_attribute) { // Returns the given string with all characters invalid in XML removed. // Currently invalid characters are dropped from the string. An // alternative is to replace them with certain characters such as . or ?. -string XmlUnitTestResultPrinter::RemoveInvalidXmlCharacters(const string& str) { - string output; +std::string XmlUnitTestResultPrinter::RemoveInvalidXmlCharacters( + const std::string& str) { + std::string output; output.reserve(str.size()); - for (string::const_iterator it = str.begin(); it != str.end(); ++it) + for (std::string::const_iterator it = str.begin(); it != str.end(); ++it) if (IsValidXmlCharacter(*it)) output.push_back(*it); @@ -3199,6 +3505,37 @@ std::string FormatTimeInMillisAsSeconds(TimeInMillis ms) { return ss.str(); } +static bool PortableLocaltime(time_t seconds, struct tm* out) { +#if defined(_MSC_VER) + return localtime_s(out, &seconds) == 0; +#elif defined(__MINGW32__) || defined(__MINGW64__) + // MINGW <time.h> provides neither localtime_r nor localtime_s, but uses + // Windows' localtime(), which has a thread-local tm buffer. + struct tm* tm_ptr = localtime(&seconds); // NOLINT + if (tm_ptr == NULL) + return false; + *out = *tm_ptr; + return true; +#else + return localtime_r(&seconds, out) != NULL; +#endif +} + +// Converts the given epoch time in milliseconds to a date string in the ISO +// 8601 format, without the timezone information. +std::string FormatEpochTimeInMillisAsIso8601(TimeInMillis ms) { + struct tm time_struct; + if (!PortableLocaltime(static_cast<time_t>(ms / 1000), &time_struct)) + return ""; + // YYYY-MM-DDThh:mm:ss + return StreamableToString(time_struct.tm_year + 1900) + "-" + + String::FormatIntWidth2(time_struct.tm_mon + 1) + "-" + + String::FormatIntWidth2(time_struct.tm_mday) + "T" + + String::FormatIntWidth2(time_struct.tm_hour) + ":" + + String::FormatIntWidth2(time_struct.tm_min) + ":" + + String::FormatIntWidth2(time_struct.tm_sec); +} + // Streams an XML CDATA section, escaping invalid CDATA sequences as needed. void XmlUnitTestResultPrinter::OutputXmlCDataSection(::std::ostream* stream, const char* data) { @@ -3219,48 +3556,63 @@ void XmlUnitTestResultPrinter::OutputXmlCDataSection(::std::ostream* stream, *stream << "]]>"; } +void XmlUnitTestResultPrinter::OutputXmlAttribute( + std::ostream* stream, + const std::string& element_name, + const std::string& name, + const std::string& value) { + const std::vector<std::string>& allowed_names = + GetReservedAttributesForElement(element_name); + + GTEST_CHECK_(std::find(allowed_names.begin(), allowed_names.end(), name) != + allowed_names.end()) + << "Attribute " << name << " is not allowed for element <" << element_name + << ">."; + + *stream << " " << name << "=\"" << EscapeXmlAttribute(value) << "\""; +} + // Prints an XML representation of a TestInfo object. // TODO(wan): There is also value in printing properties with the plain printer. void XmlUnitTestResultPrinter::OutputXmlTestInfo(::std::ostream* stream, const char* test_case_name, const TestInfo& test_info) { - if (test_info.filtered_out ()) - return; - const TestResult& result = *test_info.result(); - *stream << " <testcase name=\"" - << EscapeXmlAttribute(test_info.name()).c_str() << "\""; + const std::string kTestcase = "testcase"; + + *stream << " <testcase"; + OutputXmlAttribute(stream, kTestcase, "name", test_info.name()); if (test_info.value_param() != NULL) { - *stream << " value_param=\"" << EscapeXmlAttribute(test_info.value_param()) - << "\""; + OutputXmlAttribute(stream, kTestcase, "value_param", + test_info.value_param()); } if (test_info.type_param() != NULL) { - *stream << " type_param=\"" << EscapeXmlAttribute(test_info.type_param()) - << "\""; + OutputXmlAttribute(stream, kTestcase, "type_param", test_info.type_param()); } - *stream << " status=\"" - << (test_info.should_run() ? "run" : "notrun") - << "\" time=\"" - << FormatTimeInMillisAsSeconds(result.elapsed_time()) - << "\" classname=\"" << EscapeXmlAttribute(test_case_name).c_str() - << "\"" << TestPropertiesAsXmlAttributes(result).c_str(); + OutputXmlAttribute(stream, kTestcase, "status", + test_info.should_run() ? "run" : "notrun"); + OutputXmlAttribute(stream, kTestcase, "time", + FormatTimeInMillisAsSeconds(result.elapsed_time())); + OutputXmlAttribute(stream, kTestcase, "classname", test_case_name); + *stream << TestPropertiesAsXmlAttributes(result); int failures = 0; for (int i = 0; i < result.total_part_count(); ++i) { const TestPartResult& part = result.GetTestPartResult(i); if (part.failed()) { - if (++failures == 1) + if (++failures == 1) { *stream << ">\n"; - *stream << " <failure message=\"" - << EscapeXmlAttribute(part.summary()).c_str() - << "\" type=\"\">"; + } const string location = internal::FormatCompilerIndependentFileLocation( part.file_name(), part.line_number()); - const string message = location + "\n" + part.message(); - OutputXmlCDataSection(stream, - RemoveInvalidXmlCharacters(message).c_str()); + const string summary = location + "\n" + part.summary(); + *stream << " <failure message=\"" + << EscapeXmlAttribute(summary.c_str()) + << "\" type=\"\">"; + const string detail = location + "\n" + part.message(); + OutputXmlCDataSection(stream, RemoveInvalidXmlCharacters(detail).c_str()); *stream << "</failure>\n"; } } @@ -3272,52 +3624,73 @@ void XmlUnitTestResultPrinter::OutputXmlTestInfo(::std::ostream* stream, } // Prints an XML representation of a TestCase object -void XmlUnitTestResultPrinter::PrintXmlTestCase(FILE* out, +void XmlUnitTestResultPrinter::PrintXmlTestCase(std::ostream* stream, const TestCase& test_case) { - if (test_case.should_skip_report ()) - return; + const std::string kTestsuite = "testsuite"; + *stream << " <" << kTestsuite; + OutputXmlAttribute(stream, kTestsuite, "name", test_case.name()); + OutputXmlAttribute(stream, kTestsuite, "tests", + StreamableToString(test_case.reportable_test_count())); + OutputXmlAttribute(stream, kTestsuite, "failures", + StreamableToString(test_case.failed_test_count())); + OutputXmlAttribute( + stream, kTestsuite, "disabled", + StreamableToString(test_case.reportable_disabled_test_count())); + OutputXmlAttribute(stream, kTestsuite, "errors", "0"); + OutputXmlAttribute(stream, kTestsuite, "time", + FormatTimeInMillisAsSeconds(test_case.elapsed_time())); + *stream << TestPropertiesAsXmlAttributes(test_case.ad_hoc_test_result()) + << ">\n"; - fprintf(out, - " <testsuite name=\"%s\" tests=\"%d\" failures=\"%d\" " - "disabled=\"%d\" ", - EscapeXmlAttribute(test_case.name()).c_str(), - test_case.total_test_count(), - test_case.failed_test_count(), - test_case.disabled_test_count()); - fprintf(out, - "errors=\"0\" time=\"%s\">\n", - FormatTimeInMillisAsSeconds(test_case.elapsed_time()).c_str()); for (int i = 0; i < test_case.total_test_count(); ++i) { - ::std::stringstream stream; - OutputXmlTestInfo(&stream, test_case.name(), *test_case.GetTestInfo(i)); - fprintf(out, "%s", StringStreamToString(&stream).c_str()); + if (test_case.GetTestInfo(i)->is_reportable()) + OutputXmlTestInfo(stream, test_case.name(), *test_case.GetTestInfo(i)); } - fprintf(out, " </testsuite>\n"); + *stream << " </" << kTestsuite << ">\n"; } // Prints an XML summary of unit_test to output stream out. -void XmlUnitTestResultPrinter::PrintXmlUnitTest(FILE* out, +void XmlUnitTestResultPrinter::PrintXmlUnitTest(std::ostream* stream, const UnitTest& unit_test) { - fprintf(out, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); - fprintf(out, - "<testsuites tests=\"%d\" failures=\"%d\" disabled=\"%d\" " - "errors=\"0\" time=\"%s\" ", - unit_test.total_test_count(), - unit_test.failed_test_count(), - unit_test.disabled_test_count(), - FormatTimeInMillisAsSeconds(unit_test.elapsed_time()).c_str()); + const std::string kTestsuites = "testsuites"; + + *stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; + *stream << "<" << kTestsuites; + + OutputXmlAttribute(stream, kTestsuites, "tests", + StreamableToString(unit_test.reportable_test_count())); + OutputXmlAttribute(stream, kTestsuites, "failures", + StreamableToString(unit_test.failed_test_count())); + OutputXmlAttribute( + stream, kTestsuites, "disabled", + StreamableToString(unit_test.reportable_disabled_test_count())); + OutputXmlAttribute(stream, kTestsuites, "errors", "0"); + OutputXmlAttribute( + stream, kTestsuites, "timestamp", + FormatEpochTimeInMillisAsIso8601(unit_test.start_timestamp())); + OutputXmlAttribute(stream, kTestsuites, "time", + FormatTimeInMillisAsSeconds(unit_test.elapsed_time())); + if (GTEST_FLAG(shuffle)) { - fprintf(out, "random_seed=\"%d\" ", unit_test.random_seed()); + OutputXmlAttribute(stream, kTestsuites, "random_seed", + StreamableToString(unit_test.random_seed())); + } + + *stream << TestPropertiesAsXmlAttributes(unit_test.ad_hoc_test_result()); + + OutputXmlAttribute(stream, kTestsuites, "name", "AllTests"); + *stream << ">\n"; + + for (int i = 0; i < unit_test.total_test_case_count(); ++i) { + if (unit_test.GetTestCase(i)->reportable_test_count() > 0) + PrintXmlTestCase(stream, *unit_test.GetTestCase(i)); } - fprintf(out, "name=\"AllTests\">\n"); - for (int i = 0; i < unit_test.total_test_case_count(); ++i) - PrintXmlTestCase(out, *unit_test.GetTestCase(i)); - fprintf(out, "</testsuites>\n"); + *stream << "</" << kTestsuites << ">\n"; } // Produces a string representing the test properties in a result as space // delimited XML attributes based on the property key="value" pairs. -String XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes( +std::string XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes( const TestResult& result) { Message attributes; for (int i = 0; i < result.test_property_count(); ++i) { @@ -3332,112 +3705,6 @@ String XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes( #if GTEST_CAN_STREAM_RESULTS_ -// Streams test results to the given port on the given host machine. -class StreamingListener : public EmptyTestEventListener { - public: - // Escapes '=', '&', '%', and '\n' characters in str as "%xx". - static string UrlEncode(const char* str); - - StreamingListener(const string& host, const string& port) - : sockfd_(-1), host_name_(host), port_num_(port) { - MakeConnection(); - Send("gtest_streaming_protocol_version=1.0\n"); - } - - virtual ~StreamingListener() { - if (sockfd_ != -1) - CloseConnection(); - } - - void OnTestProgramStart(const UnitTest& /* unit_test */) { - Send("event=TestProgramStart\n"); - } - - void OnTestProgramEnd(const UnitTest& unit_test) { - // Note that Google Test current only report elapsed time for each - // test iteration, not for the entire test program. - Send(String::Format("event=TestProgramEnd&passed=%d\n", - unit_test.Passed())); - - // Notify the streaming server to stop. - CloseConnection(); - } - - void OnTestIterationStart(const UnitTest& /* unit_test */, int iteration) { - Send(String::Format("event=TestIterationStart&iteration=%d\n", - iteration)); - } - - void OnTestIterationEnd(const UnitTest& unit_test, int /* iteration */) { - Send(String::Format("event=TestIterationEnd&passed=%d&elapsed_time=%sms\n", - unit_test.Passed(), - StreamableToString(unit_test.elapsed_time()).c_str())); - } - - void OnTestCaseStart(const TestCase& test_case) { - Send(String::Format("event=TestCaseStart&name=%s\n", test_case.name())); - } - - void OnTestCaseEnd(const TestCase& test_case) { - Send(String::Format("event=TestCaseEnd&passed=%d&elapsed_time=%sms\n", - test_case.Passed(), - StreamableToString(test_case.elapsed_time()).c_str())); - } - - void OnTestStart(const TestInfo& test_info) { - Send(String::Format("event=TestStart&name=%s\n", test_info.name())); - } - - void OnTestEnd(const TestInfo& test_info) { - Send(String::Format( - "event=TestEnd&passed=%d&elapsed_time=%sms\n", - (test_info.result())->Passed(), - StreamableToString((test_info.result())->elapsed_time()).c_str())); - } - - void OnTestPartResult(const TestPartResult& test_part_result) { - const char* file_name = test_part_result.file_name(); - if (file_name == NULL) - file_name = ""; - Send(String::Format("event=TestPartResult&file=%s&line=%d&message=", - UrlEncode(file_name).c_str(), - test_part_result.line_number())); - Send(UrlEncode(test_part_result.message()) + "\n"); - } - - private: - // Creates a client socket and connects to the server. - void MakeConnection(); - - // Closes the socket. - void CloseConnection() { - GTEST_CHECK_(sockfd_ != -1) - << "CloseConnection() can be called only when there is a connection."; - - close(sockfd_); - sockfd_ = -1; - } - - // Sends a string to the socket. - void Send(const string& message) { - GTEST_CHECK_(sockfd_ != -1) - << "Send() can be called only when there is a connection."; - - const int len = static_cast<int>(message.length()); - if (write(sockfd_, message.c_str(), len) != len) { - GTEST_LOG_(WARNING) - << "stream_result_to: failed to stream to " - << host_name_ << ":" << port_num_; - } - } - - int sockfd_; // socket file descriptor - const string host_name_; - const string port_num_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamingListener); -}; // class StreamingListener - // Checks if str contains '=', '&', '%' or '\n' characters. If yes, // replaces them by "%xx" where xx is their hexadecimal value. For // example, replaces "=" with "%3D". This algorithm is O(strlen(str)) @@ -3452,7 +3719,7 @@ string StreamingListener::UrlEncode(const char* str) { case '=': case '&': case '\n': - result.append(String::Format("%%%02x", static_cast<unsigned char>(ch))); + result.append("%" + String::FormatByte(static_cast<unsigned char>(ch))); break; default: result.push_back(ch); @@ -3462,7 +3729,7 @@ string StreamingListener::UrlEncode(const char* str) { return result; } -void StreamingListener::MakeConnection() { +void StreamingListener::SocketWriter::MakeConnection() { GTEST_CHECK_(sockfd_ == -1) << "MakeConnection() can't be called when there is already a connection."; @@ -3510,8 +3777,8 @@ void StreamingListener::MakeConnection() { // Pushes the given source file location and message onto a per-thread // trace stack maintained by Google Test. -// L < UnitTest::mutex_ -ScopedTrace::ScopedTrace(const char* file, int line, const Message& message) { +ScopedTrace::ScopedTrace(const char* file, int line, const Message& message) + GTEST_LOCK_EXCLUDED_(&UnitTest::mutex_) { TraceInfo trace; trace.file = file; trace.line = line; @@ -3521,35 +3788,64 @@ ScopedTrace::ScopedTrace(const char* file, int line, const Message& message) { } // Pops the info pushed by the c'tor. -// L < UnitTest::mutex_ -ScopedTrace::~ScopedTrace() { +ScopedTrace::~ScopedTrace() + GTEST_LOCK_EXCLUDED_(&UnitTest::mutex_) { UnitTest::GetInstance()->PopGTestTrace(); } // class OsStackTraceGetter -// Returns the current OS stack trace as a String. Parameters: +// Returns the current OS stack trace as an std::string. Parameters: // // max_depth - the maximum number of stack frames to be included // in the trace. // skip_count - the number of top frames to be skipped; doesn't count // against max_depth. // -// L < mutex_ -// We use "L < mutex_" to denote that the function may acquire mutex_. -String OsStackTraceGetter::CurrentStackTrace(int, int) { - return String(""); +string OsStackTraceGetter::CurrentStackTrace(int /* max_depth */, + int /* skip_count */) + GTEST_LOCK_EXCLUDED_(mutex_) { + return ""; } -// L < mutex_ -void OsStackTraceGetter::UponLeavingGTest() { +void OsStackTraceGetter::UponLeavingGTest() + GTEST_LOCK_EXCLUDED_(mutex_) { } const char* const OsStackTraceGetter::kElidedFramesMarker = "... " GTEST_NAME_ " internal frames ..."; +// A helper class that creates the premature-exit file in its +// constructor and deletes the file in its destructor. +class ScopedPrematureExitFile { + public: + explicit ScopedPrematureExitFile(const char* premature_exit_filepath) + : premature_exit_filepath_(premature_exit_filepath) { + // If a path to the premature-exit file is specified... + if (premature_exit_filepath != NULL && *premature_exit_filepath != '\0') { + // create the file with a single "0" character in it. I/O + // errors are ignored as there's nothing better we can do and we + // don't want to fail the test because of this. + FILE* pfile = posix::FOpen(premature_exit_filepath, "w"); + fwrite("0", 1, 1, pfile); + fclose(pfile); + } + } + + ~ScopedPrematureExitFile() { + if (premature_exit_filepath_ != NULL && *premature_exit_filepath_ != '\0') { + remove(premature_exit_filepath_); + } + } + + private: + const char* const premature_exit_filepath_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedPrematureExitFile); +}; + } // namespace internal // class TestEventListeners @@ -3636,7 +3932,7 @@ void TestEventListeners::SuppressEventForwarding() { // We don't protect this under mutex_ as a user is not supposed to // call this before main() starts, from which point on the return // value will never change. -UnitTest * UnitTest::GetInstance() { +UnitTest* UnitTest::GetInstance() { // When compiled with MSVC 7.1 in optimized mode, destroying the // UnitTest object upon exiting the program messes up the exit code, // causing successful tests to appear failed. We have to use a @@ -3686,17 +3982,33 @@ int UnitTest::successful_test_count() const { // Gets the number of failed tests. int UnitTest::failed_test_count() const { return impl()->failed_test_count(); } +// Gets the number of disabled tests that will be reported in the XML report. +int UnitTest::reportable_disabled_test_count() const { + return impl()->reportable_disabled_test_count(); +} + // Gets the number of disabled tests. int UnitTest::disabled_test_count() const { return impl()->disabled_test_count(); } +// Gets the number of tests to be printed in the XML report. +int UnitTest::reportable_test_count() const { + return impl()->reportable_test_count(); +} + // Gets the number of all tests. int UnitTest::total_test_count() const { return impl()->total_test_count(); } // Gets the number of tests that should run. int UnitTest::test_to_run_count() const { return impl()->test_to_run_count(); } +// Gets the time of the test program start, in ms from the start of the +// UNIX epoch. +internal::TimeInMillis UnitTest::start_timestamp() const { + return impl()->start_timestamp(); +} + // Gets the elapsed time, in milliseconds. internal::TimeInMillis UnitTest::elapsed_time() const { return impl()->elapsed_time(); @@ -3715,6 +4027,12 @@ const TestCase* UnitTest::GetTestCase(int i) const { return impl()->GetTestCase(i); } +// Returns the TestResult containing information on test failures and +// properties logged outside of individual test cases. +const TestResult& UnitTest::ad_hoc_test_result() const { + return *impl()->ad_hoc_test_result(); +} + // Gets the i-th test case among all the test cases. i can range from 0 to // total_test_case_count() - 1. If i is not in that range, returns NULL. TestCase* UnitTest::GetMutableTestCase(int i) { @@ -3750,12 +4068,12 @@ Environment* UnitTest::AddEnvironment(Environment* env) { // assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc) eventually call // this to report their results. The user code should use the // assertion macros instead of calling this directly. -// L < mutex_ -void UnitTest::AddTestPartResult(TestPartResult::Type result_type, - const char* file_name, - int line_number, - const internal::String& message, - const internal::String& os_stack_trace) { +void UnitTest::AddTestPartResult( + TestPartResult::Type result_type, + const char* file_name, + int line_number, + const std::string& message, + const std::string& os_stack_trace) GTEST_LOCK_EXCLUDED_(mutex_) { Message msg; msg << message; @@ -3788,7 +4106,7 @@ void UnitTest::AddTestPartResult(TestPartResult::Type result_type, // with another testing framework) and specify the former on the // command line for debugging. if (GTEST_FLAG(break_on_failure)) { -#if GTEST_OS_WINDOWS +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT // Using DebugBreak on Windows allows gtest to still break into a debugger // when a failure happens and both the --gtest_break_on_failure and // the --gtest_catch_exceptions flags are specified. @@ -3802,7 +4120,7 @@ void UnitTest::AddTestPartResult(TestPartResult::Type result_type, #endif // GTEST_OS_WINDOWS } else if (GTEST_FLAG(throw_on_failure)) { #if GTEST_HAS_EXCEPTIONS - throw GoogleTestFailureException(result); + throw internal::GoogleTestFailureException(result); #else // We cannot call abort() as it generates a pop-up in debug mode // that cannot be suppressed in VC 7.1 or below. @@ -3812,12 +4130,14 @@ void UnitTest::AddTestPartResult(TestPartResult::Type result_type, } } -// Creates and adds a property to the current TestResult. If a property matching -// the supplied value already exists, updates its value instead. -void UnitTest::RecordPropertyForCurrentTest(const char* key, - const char* value) { - const TestProperty test_property(key, value); - impl_->current_test_result()->RecordProperty(test_property); +// Adds a TestProperty to the current TestResult object when invoked from +// inside a test, to current TestCase's ad_hoc_test_result_ when invoked +// from SetUpTestCase or TearDownTestCase, or to the global property set +// when invoked elsewhere. If the result already contains a property with +// the same key, the value will be updated. +void UnitTest::RecordProperty(const std::string& key, + const std::string& value) { + impl_->RecordProperty(TestProperty(key, value)); } // Runs all tests in this UnitTest object and prints the result. @@ -3826,21 +4146,45 @@ void UnitTest::RecordPropertyForCurrentTest(const char* key, // We don't protect this under mutex_, as we only support calling it // from the main thread. int UnitTest::Run() { + const bool in_death_test_child_process = + internal::GTEST_FLAG(internal_run_death_test).length() > 0; + + // Google Test implements this protocol for catching that a test + // program exits before returning control to Google Test: + // + // 1. Upon start, Google Test creates a file whose absolute path + // is specified by the environment variable + // TEST_PREMATURE_EXIT_FILE. + // 2. When Google Test has finished its work, it deletes the file. + // + // This allows a test runner to set TEST_PREMATURE_EXIT_FILE before + // running a Google-Test-based test program and check the existence + // of the file at the end of the test execution to see if it has + // exited prematurely. + + // If we are in the child process of a death test, don't + // create/delete the premature exit file, as doing so is unnecessary + // and will confuse the parent process. Otherwise, create/delete + // the file upon entering/leaving this function. If the program + // somehow exits before this function has a chance to return, the + // premature-exit file will be left undeleted, causing a test runner + // that understands the premature-exit-file protocol to report the + // test as having failed. + const internal::ScopedPrematureExitFile premature_exit_file( + in_death_test_child_process ? + NULL : internal::posix::GetEnv("TEST_PREMATURE_EXIT_FILE")); + // Captures the value of GTEST_FLAG(catch_exceptions). This value will be // used for the duration of the program. impl()->set_catch_exceptions(GTEST_FLAG(catch_exceptions)); #if GTEST_HAS_SEH - const bool in_death_test_child_process = - internal::GTEST_FLAG(internal_run_death_test).length() > 0; - // Either the user wants Google Test to catch exceptions thrown by the // tests or this is executing in the context of death test child // process. In either case the user does not want to see pop-up dialogs // about crashes - they are expected. if (impl()->catch_exceptions() || in_death_test_child_process) { - -# if !GTEST_OS_WINDOWS_MOBILE +# if !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT // SetErrorMode doesn't exist on CE. SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); @@ -3870,7 +4214,6 @@ int UnitTest::Run() { 0x0, // Clear the following flags: _WRITE_ABORT_MSG | _CALL_REPORTFAULT); // pop-up window, core dump. # endif - } #endif // GTEST_HAS_SEH @@ -3888,16 +4231,16 @@ const char* UnitTest::original_working_dir() const { // Returns the TestCase object for the test that's currently running, // or NULL if no test is running. -// L < mutex_ -const TestCase* UnitTest::current_test_case() const { +const TestCase* UnitTest::current_test_case() const + GTEST_LOCK_EXCLUDED_(mutex_) { internal::MutexLock lock(&mutex_); return impl_->current_test_case(); } // Returns the TestInfo object for the test that's currently running, // or NULL if no test is running. -// L < mutex_ -const TestInfo* UnitTest::current_test_info() const { +const TestInfo* UnitTest::current_test_info() const + GTEST_LOCK_EXCLUDED_(mutex_) { internal::MutexLock lock(&mutex_); return impl_->current_test_info(); } @@ -3908,9 +4251,9 @@ int UnitTest::random_seed() const { return impl_->random_seed(); } #if GTEST_HAS_PARAM_TEST // Returns ParameterizedTestCaseRegistry object used to keep track of // value-parameterized tests and instantiate and register them. -// L < mutex_ internal::ParameterizedTestCaseRegistry& - UnitTest::parameterized_test_registry() { + UnitTest::parameterized_test_registry() + GTEST_LOCK_EXCLUDED_(mutex_) { return impl_->parameterized_test_registry(); } #endif // GTEST_HAS_PARAM_TEST @@ -3927,15 +4270,15 @@ UnitTest::~UnitTest() { // Pushes a trace defined by SCOPED_TRACE() on to the per-thread // Google Test trace stack. -// L < mutex_ -void UnitTest::PushGTestTrace(const internal::TraceInfo& trace) { +void UnitTest::PushGTestTrace(const internal::TraceInfo& trace) + GTEST_LOCK_EXCLUDED_(mutex_) { internal::MutexLock lock(&mutex_); impl_->gtest_trace_stack().push_back(trace); } // Pops a trace from the per-thread Google Test trace stack. -// L < mutex_ -void UnitTest::PopGTestTrace() { +void UnitTest::PopGTestTrace() + GTEST_LOCK_EXCLUDED_(mutex_) { internal::MutexLock lock(&mutex_); impl_->gtest_trace_stack().pop_back(); } @@ -3944,17 +4287,10 @@ namespace internal { UnitTestImpl::UnitTestImpl(UnitTest* parent) : parent_(parent), -#ifdef _MSC_VER -# pragma warning(push) // Saves the current warning state. -# pragma warning(disable:4355) // Temporarily disables warning 4355 - // (using this in initializer). + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4355 /* using this in initializer */) default_global_test_part_result_reporter_(this), default_per_thread_test_part_result_reporter_(this), -# pragma warning(pop) // Restores the warning state again. -#else - default_global_test_part_result_reporter_(this), - default_per_thread_test_part_result_reporter_(this), -#endif // _MSC_VER + GTEST_DISABLE_MSC_WARNINGS_POP_() global_test_part_result_repoter_( &default_global_test_part_result_reporter_), per_thread_test_part_result_reporter_( @@ -3971,9 +4307,9 @@ UnitTestImpl::UnitTestImpl(UnitTest* parent) post_flag_parse_init_performed_(false), random_seed_(0), // Will be overridden by the flag before first use. random_(0), // Will be reseeded before first use. + start_timestamp_(0), elapsed_time_(0), #if GTEST_HAS_DEATH_TEST - internal_run_death_test_flag_(NULL), death_test_factory_(new DefaultDeathTestFactory), #endif // Will be overridden by the flag before first use. @@ -3991,6 +4327,28 @@ UnitTestImpl::~UnitTestImpl() { delete os_stack_trace_getter_; } +// Adds a TestProperty to the current TestResult object when invoked in a +// context of a test, to current test case's ad_hoc_test_result when invoke +// from SetUpTestCase/TearDownTestCase, or to the global property set +// otherwise. If the result already contains a property with the same key, +// the value will be updated. +void UnitTestImpl::RecordProperty(const TestProperty& test_property) { + std::string xml_element; + TestResult* test_result; // TestResult appropriate for property recording. + + if (current_test_info_ != NULL) { + xml_element = "testcase"; + test_result = &(current_test_info_->result_); + } else if (current_test_case_ != NULL) { + xml_element = "testsuite"; + test_result = &(current_test_case_->ad_hoc_test_result_); + } else { + xml_element = "testsuites"; + test_result = &ad_hoc_test_result_; + } + test_result->RecordProperty(xml_element, test_property); +} + #if GTEST_HAS_DEATH_TEST // Disables event forwarding if the control is currently in a death test // subprocess. Must not be called before InitGoogleTest. @@ -4003,7 +4361,7 @@ void UnitTestImpl::SuppressTestEventsIfInSubprocess() { // Initializes event listeners performing XML output as specified by // UnitTestOptions. Must not be called before InitGoogleTest. void UnitTestImpl::ConfigureXmlOutput() { - const String& output_format = UnitTestOptions::GetOutputFormat(); + const std::string& output_format = UnitTestOptions::GetOutputFormat(); if (output_format == "xml") { listeners()->SetDefaultXmlGenerator(new XmlUnitTestResultPrinter( UnitTestOptions::GetAbsolutePathToOutputFile().c_str())); @@ -4015,13 +4373,13 @@ void UnitTestImpl::ConfigureXmlOutput() { } #if GTEST_CAN_STREAM_RESULTS_ -// Initializes event listeners for streaming test results in String form. +// Initializes event listeners for streaming test results in string form. // Must not be called before InitGoogleTest. void UnitTestImpl::ConfigureStreamingOutput() { - const string& target = GTEST_FLAG(stream_result_to); + const std::string& target = GTEST_FLAG(stream_result_to); if (!target.empty()) { const size_t pos = target.find(':'); - if (pos != string::npos) { + if (pos != std::string::npos) { listeners()->Append(new StreamingListener(target.substr(0, pos), target.substr(pos+1))); } else { @@ -4075,7 +4433,7 @@ void UnitTestImpl::PostFlagParsingInit() { class TestCaseNameIs { public: // Constructor. - explicit TestCaseNameIs(const String& name) + explicit TestCaseNameIs(const std::string& name) : name_(name) {} // Returns true iff the name of test_case matches name_. @@ -4084,7 +4442,7 @@ class TestCaseNameIs { } private: - String name_; + std::string name_; }; // Finds and returns a TestCase with the given name. If one doesn't @@ -4116,7 +4474,7 @@ TestCase* UnitTestImpl::GetTestCase(const char* test_case_name, new TestCase(test_case_name, type_param, set_up_tc, tear_down_tc); // Is this a death test case? - if (internal::UnitTestOptions::MatchesFilter(String(test_case_name), + if (internal::UnitTestOptions::MatchesFilter(test_case_name, kDeathTestCaseFilter)) { // Yes. Inserts the test case after the last death test case // defined so far. This only works when the test cases haven't @@ -4202,6 +4560,7 @@ bool UnitTestImpl::RunAllTests() { TestEventListener* repeater = listeners()->repeater(); + start_timestamp_ = GetTimeInMillis(); repeater->OnTestProgramStart(*parent_); // How many times to repeat the tests? We don't want to repeat them @@ -4394,14 +4753,12 @@ int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) { int num_selected_tests = 0; for (size_t i = 0; i < test_cases_.size(); i++) { TestCase* const test_case = test_cases_[i]; - const String &test_case_name = test_case->name(); + const std::string &test_case_name = test_case->name(); test_case->set_should_run(false); - bool any_matched_filter = false; - for (size_t j = 0; j < test_case->test_info_list().size(); j++) { TestInfo* const test_info = test_case->test_info_list()[j]; - const String test_name(test_info->name()); + const std::string test_name(test_info->name()); // A test is disabled if test case name or test name matches // kDisableTestFilter. const bool is_disabled = @@ -4415,7 +4772,6 @@ int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) { internal::UnitTestOptions::FilterMatchesTest(test_case_name, test_name); test_info->matches_filter_ = matches_filter; - any_matched_filter |= matches_filter; const bool is_runnable = (GTEST_FLAG(also_run_disabled_tests) || !is_disabled) && @@ -4432,14 +4788,37 @@ int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) { test_info->should_run_ = is_selected; test_case->set_should_run(test_case->should_run() || is_selected); } - - test_case->set_should_skip_report(!any_matched_filter); } return num_selected_tests; } +// Prints the given C-string on a single line by replacing all '\n' +// characters with string "\\n". If the output takes more than +// max_length characters, only prints the first max_length characters +// and "...". +static void PrintOnOneLine(const char* str, int max_length) { + if (str != NULL) { + for (int i = 0; *str != '\0'; ++str) { + if (i >= max_length) { + printf("..."); + break; + } + if (*str == '\n') { + printf("\\n"); + i += 2; + } else { + printf("%c", *str); + ++i; + } + } + } +} + // Prints the names of the tests matching the user-specified filter flag. void UnitTestImpl::ListTestsMatchingFilter() { + // Print at most this many characters for each type/value parameter. + const int kMaxParamLength = 250; + for (size_t i = 0; i < test_cases_.size(); i++) { const TestCase* const test_case = test_cases_[i]; bool printed_test_case_name = false; @@ -4450,9 +4829,23 @@ void UnitTestImpl::ListTestsMatchingFilter() { if (test_info->matches_filter_) { if (!printed_test_case_name) { printed_test_case_name = true; - printf("%s.\n", test_case->name()); + printf("%s.", test_case->name()); + if (test_case->type_param() != NULL) { + printf(" # %s = ", kTypeParamLabel); + // We print the type parameter on a single line to make + // the output easy to parse by a program. + PrintOnOneLine(test_case->type_param(), kMaxParamLength); + } + printf("\n"); + } + printf(" %s", test_info->name()); + if (test_info->value_param() != NULL) { + printf(" # %s = ", kValueParamLabel); + // We print the value parameter on a single line to make the + // output easy to parse by a program. + PrintOnOneLine(test_info->value_param(), kMaxParamLength); } - printf(" %s\n", test_info->name()); + printf("\n"); } } } @@ -4516,7 +4909,7 @@ void UnitTestImpl::UnshuffleTests() { } } -// Returns the current OS stack trace as a String. +// Returns the current OS stack trace as an std::string. // // The maximum number of stack frames to be included is specified by // the gtest_stack_trace_depth flag. The skip_count parameter @@ -4526,8 +4919,8 @@ void UnitTestImpl::UnshuffleTests() { // For example, if Foo() calls Bar(), which in turn calls // GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in // the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't. -String GetCurrentOsStackTraceExceptTop(UnitTest* /*unit_test*/, - int skip_count) { +std::string GetCurrentOsStackTraceExceptTop(UnitTest* /*unit_test*/, + int skip_count) { // We pass skip_count + 1 to skip this wrapper function in addition // to what the user really wants to skip. return GetUnitTestImpl()->CurrentOsStackTraceExceptTop(skip_count + 1); @@ -4575,7 +4968,7 @@ const char* ParseFlagValue(const char* str, if (str == NULL || flag == NULL) return NULL; // The flag must start with "--" followed by GTEST_FLAG_PREFIX_. - const String flag_str = String::Format("--%s%s", GTEST_FLAG_PREFIX_, flag); + const std::string flag_str = std::string("--") + GTEST_FLAG_PREFIX_ + flag; const size_t flag_len = flag_str.length(); if (strncmp(str, flag_str.c_str(), flag_len) != 0) return NULL; @@ -4640,7 +5033,7 @@ bool ParseInt32Flag(const char* str, const char* flag, Int32* value) { // // On success, stores the value of the flag in *value, and returns // true. On failure, returns false without changing *value. -bool ParseStringFlag(const char* str, const char* flag, String* value) { +bool ParseStringFlag(const char* str, const char* flag, std::string* value) { // Gets the value of the flag as a string. const char* const value_str = ParseFlagValue(str, flag, false); @@ -4692,7 +5085,7 @@ static void PrintColorEncoded(const char* str) { return; } - ColoredPrintf(color, "%s", String(str, p - str).c_str()); + ColoredPrintf(color, "%s", std::string(str, p).c_str()); const char ch = p[1]; str = p + 2; @@ -4782,7 +5175,7 @@ static const char kColorEncodedHelpMessage[] = template <typename CharType> void ParseGoogleTestFlagsOnlyImpl(int* argc, CharType** argv) { for (int i = 1; i < *argc; i++) { - const String arg_string = StreamableToString(argv[i]); + const std::string arg_string = StreamableToString(argv[i]); const char* const arg = arg_string.c_str(); using internal::ParseBoolFlag; |