summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorshiqian <shiqian@861a406c-534a-0410-8894-cb66d6ee9925>2008-09-26 16:08:30 +0000
committershiqian <shiqian@861a406c-534a-0410-8894-cb66d6ee9925>2008-09-26 16:08:30 +0000
commitf0e809a3c9946e99595d4faeb0a16bdc2ca9ffd5 (patch)
tree68f49941b72ce8f866c4f762cb3043504804800f
parentdc8c9fa9f38a6a8fc2b78d8096e8d3f1c969cb7c (diff)
downloadgoogletest-f0e809a3c9946e99595d4faeb0a16bdc2ca9ffd5.tar.gz
Lots of changes:
* changes the XML report format to match JUnit/Ant's. * improves file path handling. * allows the user to disable RTTI using the GTEST_HAS_RTTI macro. * makes the code compile with -Wswitch-enum. git-svn-id: http://googletest.googlecode.com/svn/trunk@98 861a406c-534a-0410-8894-cb66d6ee9925
-rw-r--r--include/gtest/gtest-spi.h9
-rw-r--r--include/gtest/internal/gtest-filepath.h40
-rw-r--r--include/gtest/internal/gtest-port.h52
-rw-r--r--include/gtest/internal/gtest-type-util.h6
-rw-r--r--include/gtest/internal/gtest-type-util.h.pump6
-rw-r--r--src/gtest-death-test.cc1
-rw-r--r--src/gtest-filepath.cc54
-rw-r--r--src/gtest.cc46
-rw-r--r--test/gtest-filepath_test.cc106
-rw-r--r--test/gtest_unittest.cc24
-rwxr-xr-xtest/gtest_xml_outfiles_test.py6
-rwxr-xr-xtest/gtest_xml_output_unittest.py20
-rwxr-xr-xtest/gtest_xml_test_utils.py100
13 files changed, 395 insertions, 75 deletions
diff --git a/include/gtest/gtest-spi.h b/include/gtest/gtest-spi.h
index 75d0dcf..5315b97 100644
--- a/include/gtest/gtest-spi.h
+++ b/include/gtest/gtest-spi.h
@@ -55,6 +55,7 @@ class TestPartResult {
: type_(type),
file_name_(file_name),
line_number_(line_number),
+ summary_(ExtractSummary(message)),
message_(message) {
}
@@ -69,6 +70,9 @@ class TestPartResult {
// or -1 if it's unknown.
int line_number() const { return line_number_; }
+ // Gets the summary of the failure message.
+ const char* summary() const { return summary_.c_str(); }
+
// Gets the message associated with the test part.
const char* message() const { return message_.c_str(); }
@@ -86,12 +90,17 @@ class TestPartResult {
private:
TestPartResultType type_;
+ // Gets the summary of the failure message by omitting the stack
+ // trace in it.
+ static internal::String ExtractSummary(const char* message);
+
// The name of the source file where the test part took place, or
// NULL if the source file is unknown.
internal::String file_name_;
// The line in the source file where the test part took place, or -1
// if the line number is unknown.
int line_number_;
+ internal::String summary_; // The test failure summary.
internal::String message_; // The test failure message.
};
diff --git a/include/gtest/internal/gtest-filepath.h b/include/gtest/internal/gtest-filepath.h
index fecdddc..9a0682a 100644
--- a/include/gtest/internal/gtest-filepath.h
+++ b/include/gtest/internal/gtest-filepath.h
@@ -60,8 +60,19 @@ class FilePath {
public:
FilePath() : pathname_("") { }
FilePath(const FilePath& rhs) : pathname_(rhs.pathname_) { }
- explicit FilePath(const char* pathname) : pathname_(pathname) { }
- explicit FilePath(const String& pathname) : pathname_(pathname) { }
+
+ explicit FilePath(const char* pathname) : pathname_(pathname) {
+ Normalize();
+ }
+
+ explicit FilePath(const String& pathname) : pathname_(pathname) {
+ Normalize();
+ }
+
+ FilePath& operator=(const FilePath& rhs) {
+ Set(rhs);
+ return *this;
+ }
void Set(const FilePath& rhs) {
pathname_ = rhs.pathname_;
@@ -149,11 +160,30 @@ class FilePath {
// This does NOT check that a directory (or file) actually exists.
bool IsDirectory() const;
+ // Returns true if pathname describes a root directory. (Windows has one
+ // root directory per disk drive.)
+ bool IsRootDirectory() const;
+
private:
- String pathname_;
+ // Replaces multiple consecutive separators with a single separator.
+ // For example, "bar///foo" becomes "bar/foo". Does not eliminate other
+ // redundancies that might be in a pathname involving "." or "..".
+ //
+ // A pathname with multiple consecutive separators may occur either through
+ // user error or as a result of some scripts or APIs that generate a pathname
+ // with a trailing separator. On other platforms the same API or script
+ // may NOT generate a pathname with a trailing "/". Then elsewhere that
+ // pathname may have another "/" and pathname components added to it,
+ // without checking for the separator already being there.
+ // The script language and operating system may allow paths like "foo//bar"
+ // but some of the functions in FilePath will not handle that correctly. In
+ // particular, RemoveTrailingPathSeparator() only removes one separator, and
+ // it is called in CreateDirectoriesRecursively() assuming that it will change
+ // a pathname from directory syntax (trailing separator) to filename syntax.
+
+ void Normalize();
- // Don't implement operator= because it is banned by the style guide.
- FilePath& operator=(const FilePath& rhs);
+ String pathname_;
}; // class FilePath
} // namespace internal
diff --git a/include/gtest/internal/gtest-port.h b/include/gtest/internal/gtest-port.h
index 75429b2..022e670 100644
--- a/include/gtest/internal/gtest-port.h
+++ b/include/gtest/internal/gtest-port.h
@@ -55,6 +55,9 @@
// is/isn't available (some systems define
// ::wstring, which is different to std::wstring).
// Leave it undefined to let Google Test define it.
+// GTEST_HAS_RTTI - Define it to 1/0 to indicate that RTTI is/isn't
+// enabled. Leave it undefined to let Google
+// Test define it.
// This header defines the following utilities:
//
@@ -135,6 +138,13 @@
#define GTEST_FLAG_PREFIX "gtest_"
#define GTEST_FLAG_PREFIX_UPPER "GTEST_"
+// Determines the version of gcc that is used to compile this.
+#ifdef __GNUC__
+// 40302 means version 4.3.2.
+#define GTEST_GCC_VER_ \
+ (__GNUC__*10000 + __GNUC_MINOR__*100 + __GNUC_PATCHLEVEL__)
+#endif // __GNUC__
+
// Determines the platform on which Google Test is compiled.
#ifdef __CYGWIN__
#define GTEST_OS_CYGWIN
@@ -215,6 +225,42 @@
#include <strstream> // NOLINT
#endif // GTEST_HAS_STD_STRING
+// Determines whether RTTI is available.
+#ifndef GTEST_HAS_RTTI
+// The user didn't tell us whether RTTI is enabled, so we need to
+// figure it out.
+
+#ifdef _MSC_VER
+
+#ifdef _CPPRTTI // MSVC defines this macro iff RTTI is enabled.
+#define GTEST_HAS_RTTI 1
+#else
+#define GTEST_HAS_RTTI 0
+#endif // _CPPRTTI
+
+#elif defined(__GNUC__)
+
+// Starting with version 4.3.2, gcc defines __GXX_RTTI iff RTTI is enabled.
+#if GTEST_GCC_VER_ >= 40302
+#ifdef __GXX_RTTI
+#define GTEST_HAS_RTTI 1
+#else
+#define GTEST_HAS_RTTI 0
+#endif // __GXX_RTTI
+#else
+// For gcc versions smaller than 4.3.2, we assume RTTI is enabled.
+#define GTEST_HAS_RTTI 1
+#endif // GTEST_GCC_VER >= 40302
+
+#else
+
+// Unknown compiler - assume RTTI is enabled.
+#define GTEST_HAS_RTTI 1
+
+#endif // _MSC_VER
+
+#endif // GTEST_HAS_RTTI
+
// Determines whether to support death tests.
#if GTEST_HAS_STD_STRING && defined(GTEST_OS_LINUX)
#define GTEST_HAS_DEATH_TEST
@@ -284,13 +330,11 @@
// following the argument list:
//
// Sprocket* AllocateSprocket() GTEST_MUST_USE_RESULT;
-#if defined(__GNUC__) \
- && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) \
- && !defined(COMPILER_ICC)
+#if defined(__GNUC__) && (GTEST_GCC_VER_ >= 30400) && !defined(COMPILER_ICC)
#define GTEST_MUST_USE_RESULT __attribute__ ((warn_unused_result))
#else
#define GTEST_MUST_USE_RESULT
-#endif // (__GNUC__ > 3 || __GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+#endif // __GNUC__ && (GTEST_GCC_VER_ >= 30400) && !COMPILER_ICC
namespace testing {
diff --git a/include/gtest/internal/gtest-type-util.h b/include/gtest/internal/gtest-type-util.h
index 8b56082..815da4b 100644
--- a/include/gtest/internal/gtest-type-util.h
+++ b/include/gtest/internal/gtest-type-util.h
@@ -71,6 +71,8 @@ struct AssertTypeEq<T, T> {
// GetTypeName<T>() returns a human-readable name of type T.
template <typename T>
String GetTypeName() {
+#if GTEST_HAS_RTTI
+
const char* const name = typeid(T).name();
#ifdef __GNUC__
int status = 0;
@@ -83,6 +85,10 @@ String GetTypeName() {
#else
return name;
#endif // __GNUC__
+
+#else
+ return "<type>";
+#endif // GTEST_HAS_RTTI
}
// A unique type used as the default value for the arguments of class
diff --git a/include/gtest/internal/gtest-type-util.h.pump b/include/gtest/internal/gtest-type-util.h.pump
index f43be5e..4858c7d 100644
--- a/include/gtest/internal/gtest-type-util.h.pump
+++ b/include/gtest/internal/gtest-type-util.h.pump
@@ -71,6 +71,8 @@ struct AssertTypeEq<T, T> {
// GetTypeName<T>() returns a human-readable name of type T.
template <typename T>
String GetTypeName() {
+#if GTEST_HAS_RTTI
+
const char* const name = typeid(T).name();
#ifdef __GNUC__
int status = 0;
@@ -83,6 +85,10 @@ String GetTypeName() {
#else
return name;
#endif // __GNUC__
+
+#else
+ return "<type>";
+#endif // GTEST_HAS_RTTI
}
// A unique type used as the default value for the arguments of class
diff --git a/src/gtest-death-test.cc b/src/gtest-death-test.cc
index 971c300..fa80087 100644
--- a/src/gtest-death-test.cc
+++ b/src/gtest-death-test.cc
@@ -416,6 +416,7 @@ bool ForkingDeathTest::Passed(bool status_ok) {
<< " " << ExitSummary(status_) << "\n";
}
break;
+ case IN_PROGRESS:
default:
GTEST_LOG(FATAL,
"DeathTest::Passed somehow called before conclusion of test");
diff --git a/src/gtest-filepath.cc b/src/gtest-filepath.cc
index 2a5be8c..dc0d78f 100644
--- a/src/gtest-filepath.cc
+++ b/src/gtest-filepath.cc
@@ -161,10 +161,13 @@ bool FilePath::FileOrDirectoryExists() const {
// that exists.
bool FilePath::DirectoryExists() const {
bool result = false;
-#ifdef _WIN32
- FilePath removed_sep(this->RemoveTrailingPathSeparator());
+#ifdef GTEST_OS_WINDOWS
+ // Don't strip off trailing separator if path is a root directory on
+ // Windows (like "C:\\").
+ const FilePath& path(IsRootDirectory() ? *this :
+ RemoveTrailingPathSeparator());
#ifdef _WIN32_WCE
- LPCWSTR unicode = String::AnsiToUtf16(removed_sep.c_str());
+ LPCWSTR unicode = String::AnsiToUtf16(path.c_str());
const DWORD attributes = GetFileAttributes(unicode);
delete [] unicode;
if ((attributes != kInvalidFileAttributes) &&
@@ -173,17 +176,32 @@ bool FilePath::DirectoryExists() const {
}
#else
struct _stat file_stat = {};
- result = _stat(removed_sep.c_str(), &file_stat) == 0 &&
+ result = _stat(path.c_str(), &file_stat) == 0 &&
(_S_IFDIR & file_stat.st_mode) != 0;
#endif // _WIN32_WCE
#else
struct stat file_stat = {};
result = stat(pathname_.c_str(), &file_stat) == 0 &&
S_ISDIR(file_stat.st_mode);
-#endif // _WIN32
+#endif // GTEST_OS_WINDOWS
return result;
}
+// Returns true if pathname describes a root directory. (Windows has one
+// root directory per disk drive.)
+bool FilePath::IsRootDirectory() const {
+#ifdef GTEST_OS_WINDOWS
+ const char* const name = pathname_.c_str();
+ return pathname_.GetLength() == 3 &&
+ ((name[0] >= 'a' && name[0] <= 'z') ||
+ (name[0] >= 'A' && name[0] <= 'Z')) &&
+ name[1] == ':' &&
+ name[2] == kPathSeparator;
+#else
+ return pathname_ == kPathSeparatorString;
+#endif
+}
+
// Returns a pathname for a file that does not currently exist. The pathname
// will be directory/base_name.extension or
// directory/base_name_<number>.extension if directory/base_name.extension
@@ -258,5 +276,31 @@ FilePath FilePath::RemoveTrailingPathSeparator() const {
: *this;
}
+// Normalize removes any redundant separators that might be in the pathname.
+// For example, "bar///foo" becomes "bar/foo". Does not eliminate other
+// redundancies that might be in a pathname involving "." or "..".
+void FilePath::Normalize() {
+ if (pathname_.c_str() == NULL) {
+ pathname_ = "";
+ return;
+ }
+ const char* src = pathname_.c_str();
+ char* const dest = new char[pathname_.GetLength() + 1];
+ char* dest_ptr = dest;
+ memset(dest_ptr, 0, pathname_.GetLength() + 1);
+
+ while (*src != '\0') {
+ *dest_ptr++ = *src;
+ if (*src != kPathSeparator)
+ src++;
+ else
+ while (*src == kPathSeparator)
+ src++;
+ }
+ *dest_ptr = '\0';
+ pathname_ = dest;
+ delete[] dest;
+}
+
} // namespace internal
} // namespace testing
diff --git a/src/gtest.cc b/src/gtest.cc
index 8d2d2a2..f8c1199 100644
--- a/src/gtest.cc
+++ b/src/gtest.cc
@@ -134,6 +134,10 @@ static const char kUniversalFilter[] = "*";
// The default output file for XML output.
static const char kDefaultOutputFile[] = "test_detail.xml";
+// The text used in failure messages to indicate the start of the
+// stack trace.
+static const char kStackTraceMarker[] = "\nStack trace:\n";
+
GTEST_DEFINE_bool(
break_on_failure,
internal::BoolFromGTestEnv("break_on_failure", false),
@@ -200,6 +204,14 @@ GTEST_DEFINE_bool(
"True iff " GTEST_NAME " should include internal stack frames when "
"printing test failure stack traces.");
+// Gets the summary of the failure message by omitting the stack trace
+// in it.
+internal::String TestPartResult::ExtractSummary(const char* message) {
+ const char* const stack_trace = strstr(message, kStackTraceMarker);
+ return stack_trace == NULL ? internal::String(message) :
+ internal::String(message, stack_trace - message);
+}
+
namespace internal {
// GTestIsInitialized() returns true iff the user has initialized
@@ -2923,13 +2935,28 @@ internal::String XmlUnitTestResultPrinter::EscapeXml(const char* str,
// <testsuite name="AllTests"> <-- corresponds to a UnitTest object
// <testsuite name="testcase-name"> <-- corresponds to a TestCase object
// <testcase name="test-name"> <-- corresponds to a TestInfo object
-// <failure message="..." />
-// <failure message="..." /> <-- individual assertion failures
-// <failure message="..." />
+// <failure message="...">...</failure>
+// <failure message="...">...</failure>
+// <failure message="...">...</failure>
+// <-- individual assertion failures
// </testcase>
// </testsuite>
// </testsuite>
+namespace internal {
+
+// Formats the given time in milliseconds as seconds. The returned
+// C-string is owned by this function and cannot be released by the
+// caller. Calling the function again invalidates the previous
+// result.
+const char* FormatTimeInMillisAsSeconds(TimeInMillis ms) {
+ static String str;
+ str = (Message() << (ms/1000.0)).GetString();
+ return str.c_str();
+}
+
+} // namespace internal
+
// Prints an XML representation of a TestInfo object.
// TODO(wan): There is also value in printing properties with the plain printer.
void XmlUnitTestResultPrinter::PrintXmlTestInfo(FILE* out,
@@ -2942,7 +2969,7 @@ void XmlUnitTestResultPrinter::PrintXmlTestInfo(FILE* out,
"classname=\"%s\"%s",
EscapeXmlAttribute(test_info->name()).c_str(),
test_info->should_run() ? "run" : "notrun",
- internal::StreamableToString(result->elapsed_time()).c_str(),
+ internal::FormatTimeInMillisAsSeconds(result->elapsed_time()),
EscapeXmlAttribute(test_case_name).c_str(),
TestPropertiesAsXmlAttributes(result).c_str());
@@ -2958,8 +2985,9 @@ void XmlUnitTestResultPrinter::PrintXmlTestInfo(FILE* out,
if (++failures == 1)
fprintf(out, ">\n");
fprintf(out,
- " <failure message=\"%s\" type=\"\"/>\n",
- EscapeXmlAttribute(message.c_str()).c_str());
+ " <failure message=\"%s\" type=\"\"><![CDATA[%s]]>"
+ "</failure>\n",
+ EscapeXmlAttribute(part.summary()).c_str(), message.c_str());
}
}
@@ -2981,7 +3009,7 @@ void XmlUnitTestResultPrinter::PrintXmlTestCase(FILE* out,
test_case->disabled_test_count());
fprintf(out,
"errors=\"0\" time=\"%s\">\n",
- internal::StreamableToString(test_case->elapsed_time()).c_str());
+ internal::FormatTimeInMillisAsSeconds(test_case->elapsed_time()));
for (const internal::ListNode<TestInfo*>* info_node =
test_case->test_info_list().Head();
info_node != NULL;
@@ -3002,7 +3030,7 @@ void XmlUnitTestResultPrinter::PrintXmlUnitTest(FILE* out,
impl->total_test_count(),
impl->failed_test_count(),
impl->disabled_test_count(),
- internal::StreamableToString(impl->elapsed_time()).c_str());
+ internal::FormatTimeInMillisAsSeconds(impl->elapsed_time()));
fprintf(out, "name=\"AllTests\">\n");
for (const internal::ListNode<TestCase*>* case_node =
impl->test_cases()->Head();
@@ -3153,7 +3181,7 @@ void UnitTest::AddTestPartResult(TestPartResultType result_type,
}
if (os_stack_trace.c_str() != NULL && !os_stack_trace.empty()) {
- msg << "\nStack trace:\n" << os_stack_trace;
+ msg << kStackTraceMarker << os_stack_trace;
}
const TestPartResult result =
diff --git a/test/gtest-filepath_test.cc b/test/gtest-filepath_test.cc
index 603427c..ae5e3fb 100644
--- a/test/gtest-filepath_test.cc
+++ b/test/gtest-filepath_test.cc
@@ -123,8 +123,6 @@ TEST(IsEmptyTest, ReturnsFalseForNonEmptyPath) {
EXPECT_FALSE(FilePath("a\\b\\").IsEmpty());
}
-// FilePath's functions used by UnitTestOptions::GetOutputFile.
-
// RemoveDirectoryName "" -> ""
TEST(RemoveDirectoryNameTest, WhenEmptyName) {
EXPECT_STREQ("", FilePath("").RemoveDirectoryName().c_str());
@@ -257,6 +255,110 @@ TEST(RemoveTrailingPathSeparatorTest, ShouldReturnUnmodified) {
FilePath("foo" PATH_SEP "bar").RemoveTrailingPathSeparator().c_str());
}
+TEST(DirectoryTest, RootDirectoryExists) {
+#ifdef GTEST_OS_WINDOWS // We are on Windows.
+ char current_drive[_MAX_PATH];
+ current_drive[0] = _getdrive() + 'A' - 1;
+ current_drive[1] = ':';
+ current_drive[2] = '\\';
+ current_drive[3] = '\0';
+ EXPECT_TRUE(FilePath(current_drive).DirectoryExists());
+#else
+ EXPECT_TRUE(FilePath("/").DirectoryExists());
+#endif // GTEST_OS_WINDOWS
+}
+
+#ifdef GTEST_OS_WINDOWS
+TEST(DirectoryTest, RootOfWrongDriveDoesNotExists) {
+ const int saved_drive_ = _getdrive();
+ // Find a drive that doesn't exist. Start with 'Z' to avoid common ones.
+ for (char drive = 'Z'; drive >= 'A'; drive--)
+ if (_chdrive(drive - 'A' + 1) == -1) {
+ char non_drive[_MAX_PATH];
+ non_drive[0] = drive;
+ non_drive[1] = ':';
+ non_drive[2] = '\\';
+ non_drive[3] = '\0';
+ EXPECT_FALSE(FilePath(non_drive).DirectoryExists());
+ break;
+ }
+ _chdrive(saved_drive_);
+}
+#endif // GTEST_OS_WINDOWS
+
+TEST(DirectoryTest, EmptyPathDirectoryDoesNotExist) {
+ EXPECT_FALSE(FilePath("").DirectoryExists());
+}
+
+TEST(DirectoryTest, CurrentDirectoryExists) {
+#ifdef GTEST_OS_WINDOWS // We are on Windows.
+#ifndef _WIN32_CE // Windows CE doesn't have a current directory.
+ EXPECT_TRUE(FilePath(".").DirectoryExists());
+ EXPECT_TRUE(FilePath(".\\").DirectoryExists());
+#endif // _WIN32_CE
+#else
+ EXPECT_TRUE(FilePath(".").DirectoryExists());
+ EXPECT_TRUE(FilePath("./").DirectoryExists());
+#endif // GTEST_OS_WINDOWS
+}
+
+TEST(NormalizeTest, NullStringsEqualEmptyDirectory) {
+ EXPECT_STREQ("", FilePath(NULL).c_str());
+ EXPECT_STREQ("", FilePath(String(NULL)).c_str());
+}
+
+// "foo/bar" == foo//bar" == "foo///bar"
+TEST(NormalizeTest, MultipleConsecutiveSepaparatorsInMidstring) {
+ EXPECT_STREQ("foo" PATH_SEP "bar",
+ FilePath("foo" PATH_SEP "bar").c_str());
+ EXPECT_STREQ("foo" PATH_SEP "bar",
+ FilePath("foo" PATH_SEP PATH_SEP "bar").c_str());
+ EXPECT_STREQ("foo" PATH_SEP "bar",
+ FilePath("foo" PATH_SEP PATH_SEP PATH_SEP "bar").c_str());
+}
+
+// "/bar" == //bar" == "///bar"
+TEST(NormalizeTest, MultipleConsecutiveSepaparatorsAtStringStart) {
+ EXPECT_STREQ(PATH_SEP "bar",
+ FilePath(PATH_SEP "bar").c_str());
+ EXPECT_STREQ(PATH_SEP "bar",
+ FilePath(PATH_SEP PATH_SEP "bar").c_str());
+ EXPECT_STREQ(PATH_SEP "bar",
+ FilePath(PATH_SEP PATH_SEP PATH_SEP "bar").c_str());
+}
+
+// "foo/" == foo//" == "foo///"
+TEST(NormalizeTest, MultipleConsecutiveSepaparatorsAtStringEnd) {
+ EXPECT_STREQ("foo" PATH_SEP,
+ FilePath("foo" PATH_SEP).c_str());
+ EXPECT_STREQ("foo" PATH_SEP,
+ FilePath("foo" PATH_SEP PATH_SEP).c_str());
+ EXPECT_STREQ("foo" PATH_SEP,
+ FilePath("foo" PATH_SEP PATH_SEP PATH_SEP).c_str());
+}
+
+TEST(AssignmentOperatorTest, DefaultAssignedToNonDefault) {
+ FilePath default_path;
+ FilePath non_default_path("path");
+ non_default_path = default_path;
+ EXPECT_STREQ("", non_default_path.c_str());
+ EXPECT_STREQ("", default_path.c_str()); // RHS var is unchanged.
+}
+
+TEST(AssignmentOperatorTest, NonDefaultAssignedToDefault) {
+ FilePath non_default_path("path");
+ FilePath default_path;
+ default_path = non_default_path;
+ EXPECT_STREQ("path", default_path.c_str());
+ EXPECT_STREQ("path", non_default_path.c_str()); // RHS var is unchanged.
+}
+
+TEST(AssignmentOperatorTest, ConstAssignedToNonConst) {
+ const FilePath const_default_path("const_path");
+ FilePath non_default_path("path");
+ non_default_path = const_default_path;
+ EXPECT_STREQ("const_path", non_default_path.c_str());
+}
class DirectoryCreationTest : public Test {
protected:
diff --git a/test/gtest_unittest.cc b/test/gtest_unittest.cc
index abc337f..a9281ce 100644
--- a/test/gtest_unittest.cc
+++ b/test/gtest_unittest.cc
@@ -58,10 +58,12 @@
namespace testing {
namespace internal {
+const char* FormatTimeInMillisAsSeconds(TimeInMillis ms);
bool ParseInt32Flag(const char* str, const char* flag, Int32* value);
} // namespace internal
} // namespace testing
+using testing::internal::FormatTimeInMillisAsSeconds;
using testing::internal::ParseInt32Flag;
namespace testing {
@@ -118,6 +120,28 @@ using testing::internal::WideStringToUtf8;
// This line tests that we can define tests in an unnamed namespace.
namespace {
+// Tests FormatTimeInMillisAsSeconds().
+
+TEST(FormatTimeInMillisAsSecondsTest, FormatsZero) {
+ EXPECT_STREQ("0", FormatTimeInMillisAsSeconds(0));
+}
+
+TEST(FormatTimeInMillisAsSecondsTest, FormatsPositiveNumber) {
+ EXPECT_STREQ("0.003", FormatTimeInMillisAsSeconds(3));
+ EXPECT_STREQ("0.01", FormatTimeInMillisAsSeconds(10));
+ EXPECT_STREQ("0.2", FormatTimeInMillisAsSeconds(200));
+ EXPECT_STREQ("1.2", FormatTimeInMillisAsSeconds(1200));
+ EXPECT_STREQ("3", FormatTimeInMillisAsSeconds(3000));
+}
+
+TEST(FormatTimeInMillisAsSecondsTest, FormatsNegativeNumber) {
+ EXPECT_STREQ("-0.003", FormatTimeInMillisAsSeconds(-3));
+ EXPECT_STREQ("-0.01", FormatTimeInMillisAsSeconds(-10));
+ EXPECT_STREQ("-0.2", FormatTimeInMillisAsSeconds(-200));
+ EXPECT_STREQ("-1.2", FormatTimeInMillisAsSeconds(-1200));
+ EXPECT_STREQ("-3", FormatTimeInMillisAsSeconds(-3000));
+}
+
#ifndef __SYMBIAN32__
// NULL testing does not work with Symbian compilers.
diff --git a/test/gtest_xml_outfiles_test.py b/test/gtest_xml_outfiles_test.py
index c76e1f7..b83df77 100755
--- a/test/gtest_xml_outfiles_test.py
+++ b/test/gtest_xml_outfiles_test.py
@@ -122,9 +122,9 @@ class GTestXMLOutFilesTest(gtest_xml_test_utils.GTestXMLTestCase):
actual = minidom.parse(output_file1)
else:
actual = minidom.parse(output_file2)
- self._NormalizeXml(actual.documentElement)
- self._AssertEquivalentElements(expected.documentElement,
- actual.documentElement)
+ self.NormalizeXml(actual.documentElement)
+ self.AssertEquivalentNodes(expected.documentElement,
+ actual.documentElement)
expected.unlink()
actual.unlink()
diff --git a/test/gtest_xml_output_unittest.py b/test/gtest_xml_output_unittest.py
index 013e739..7206006 100755
--- a/test/gtest_xml_output_unittest.py
+++ b/test/gtest_xml_output_unittest.py
@@ -54,14 +54,20 @@ EXPECTED_NON_EMPTY_XML = """<?xml version="1.0" encoding="UTF-8"?>
</testsuite>
<testsuite name="FailedTest" tests="1" failures="1" disabled="0" errors="0" time="*">
<testcase name="Fails" status="run" time="*" classname="FailedTest">
- <failure message="gtest_xml_output_unittest_.cc:*&#x0A;Value of: 2&#x0A;Expected: 1" type=""/>
+ <failure message="Value of: 2&#x0A;Expected: 1" type=""><![CDATA[gtest_xml_output_unittest_.cc:*
+Value of: 2
+Expected: 1]]></failure>
</testcase>
</testsuite>
<testsuite name="MixedResultTest" tests="3" failures="1" disabled="1" errors="0" time="*">
<testcase name="Succeeds" status="run" time="*" classname="MixedResultTest"/>
<testcase name="Fails" status="run" time="*" classname="MixedResultTest">
- <failure message="gtest_xml_output_unittest_.cc:*&#x0A;Value of: 2&#x0A;Expected: 1" type=""/>
- <failure message="gtest_xml_output_unittest_.cc:*&#x0A;Value of: 3&#x0A;Expected: 2" type=""/>
+ <failure message="Value of: 2&#x0A;Expected: 1" type=""><![CDATA[gtest_xml_output_unittest_.cc:*
+Value of: 2
+Expected: 1]]></failure>
+ <failure message="Value of: 3&#x0A;Expected: 2" type=""><![CDATA[gtest_xml_output_unittest_.cc:*
+Value of: 3
+Expected: 2]]></failure>
</testcase>
<testcase name="DISABLED_test" status="notrun" time="*" classname="MixedResultTest"/>
</testsuite>
@@ -160,14 +166,14 @@ class GTestXMLOutputUnitTest(gtest_xml_test_utils.GTestXMLTestCase):
expected = minidom.parseString(expected_xml)
actual = minidom.parse(xml_path)
- self._NormalizeXml(actual.documentElement)
- self._AssertEquivalentElements(expected.documentElement,
- actual.documentElement)
+ self.NormalizeXml(actual.documentElement)
+ self.AssertEquivalentNodes(expected.documentElement,
+ actual.documentElement)
expected.unlink()
actual .unlink()
if __name__ == '__main__':
- os.environ['GTEST_STACK_TRACE_DEPTH'] = '0'
+ os.environ['GTEST_STACK_TRACE_DEPTH'] = '1'
gtest_test_utils.Main()
diff --git a/test/gtest_xml_test_utils.py b/test/gtest_xml_test_utils.py
index c2ea9c1..5694dff 100755
--- a/test/gtest_xml_test_utils.py
+++ b/test/gtest_xml_test_utils.py
@@ -47,27 +47,35 @@ class GTestXMLTestCase(unittest.TestCase):
"""
- def _AssertEquivalentElements(self, expected_element, actual_element):
+ def AssertEquivalentNodes(self, expected_node, actual_node):
"""
- Asserts that actual_element (a DOM element object) is equivalent to
- expected_element (another DOM element object), in that it meets all
- of the following conditions:
- * It has the same tag name as expected_element.
- * It has the same set of attributes as expected_element, each with
- the same value as the corresponding attribute of expected_element.
- An exception is any attribute named "time", which need only be
- convertible to a long integer.
- * Each child element is equivalent to a child element of
- expected_element. Child elements are matched according to an
- attribute that varies depending on the element; "name" for
- <testsuite> and <testcase> elements, "message" for <failure>
- elements. This matching is necessary because child elements are
- not guaranteed to be ordered in any particular way.
+ Asserts that actual_node (a DOM node object) is equivalent to
+ expected_node (another DOM node object), in that either both of
+ them are CDATA nodes and have the same value, or both are DOM
+ elements and actual_node meets all of the following conditions:
+
+ * It has the same tag name as expected_node.
+ * It has the same set of attributes as expected_node, each with
+ the same value as the corresponding attribute of expected_node.
+ An exception is any attribute named "time", which needs only be
+ convertible to a floating-point number.
+ * It has an equivalent set of child nodes (including elements and
+ CDATA sections) as expected_node. Note that we ignore the
+ order of the children as they are not guaranteed to be in any
+ particular order.
"""
- self.assertEquals(expected_element.tagName, actual_element.tagName)
- expected_attributes = expected_element.attributes
- actual_attributes = actual_element .attributes
+ if expected_node.nodeType == Node.CDATA_SECTION_NODE:
+ self.assertEquals(Node.CDATA_SECTION_NODE, actual_node.nodeType)
+ self.assertEquals(expected_node.nodeValue, actual_node.nodeValue)
+ return
+
+ self.assertEquals(Node.ELEMENT_NODE, actual_node.nodeType)
+ self.assertEquals(Node.ELEMENT_NODE, expected_node.nodeType)
+ self.assertEquals(expected_node.tagName, actual_node.tagName)
+
+ expected_attributes = expected_node.attributes
+ actual_attributes = actual_node .attributes
self.assertEquals(expected_attributes.length, actual_attributes.length)
for i in range(expected_attributes.length):
expected_attr = expected_attributes.item(i)
@@ -75,13 +83,13 @@ class GTestXMLTestCase(unittest.TestCase):
self.assert_(actual_attr is not None)
self.assertEquals(expected_attr.value, actual_attr.value)
- expected_child = self._GetChildElements(expected_element)
- actual_child = self._GetChildElements(actual_element)
- self.assertEquals(len(expected_child), len(actual_child))
- for child_id, element in expected_child.iteritems():
- self.assert_(child_id in actual_child,
- '<%s> is not in <%s>' % (child_id, actual_child))
- self._AssertEquivalentElements(element, actual_child[child_id])
+ expected_children = self._GetChildren(expected_node)
+ actual_children = self._GetChildren(actual_node)
+ self.assertEquals(len(expected_children), len(actual_children))
+ for child_id, child in expected_children.iteritems():
+ self.assert_(child_id in actual_children,
+ '<%s> is not in <%s>' % (child_id, actual_children))
+ self.AssertEquivalentNodes(child, actual_children[child_id])
identifying_attribute = {
"testsuite": "name",
@@ -89,18 +97,20 @@ class GTestXMLTestCase(unittest.TestCase):
"failure": "message",
}
- def _GetChildElements(self, element):
+ def _GetChildren(self, element):
"""
- Fetches all of the Element type child nodes of element, a DOM
- Element object. Returns them as the values of a dictionary keyed by
- the value of one of the node's attributes. For <testsuite> and
- <testcase> elements, the identifying attribute is "name"; for
- <failure> elements, it is "message". An exception is raised if
- any element other than the above three is encountered, if two
- child elements with the same identifying attributes are encountered,
- or if any other type of node is encountered, other than Text nodes
- containing only whitespace.
+ Fetches all of the child nodes of element, a DOM Element object.
+ Returns them as the values of a dictionary keyed by the IDs of the
+ children. For <testsuite> and <testcase> elements, the ID is the
+ value of their "name" attribute; for <failure> elements, it is the
+ value of the "message" attribute; for CDATA section node, it is
+ "detail". An exception is raised if any element other than the
+ above four is encountered, if two child elements with the same
+ identifying attributes are encountered, or if any other type of
+ node is encountered, other than Text nodes containing only
+ whitespace.
"""
+
children = {}
for child in element.childNodes:
if child.nodeType == Node.ELEMENT_NODE:
@@ -111,11 +121,14 @@ class GTestXMLTestCase(unittest.TestCase):
children[childID] = child
elif child.nodeType == Node.TEXT_NODE:
self.assert_(child.nodeValue.isspace())
+ elif child.nodeType == Node.CDATA_SECTION_NODE:
+ self.assert_("detail" not in children)
+ children["detail"] = child
else:
self.fail("Encountered unexpected node type %d" % child.nodeType)
return children
- def _NormalizeXml(self, element):
+ def NormalizeXml(self, element):
"""
Normalizes Google Test's XML output to eliminate references to transient
information that may change from run to run.
@@ -126,13 +139,20 @@ class GTestXMLTestCase(unittest.TestCase):
* The line number reported in the first line of the "message"
attribute of <failure> elements is replaced with a single asterisk.
* The directory names in file paths are removed.
+ * The stack traces are removed.
"""
+
if element.tagName in ("testsuite", "testcase"):
time = element.getAttributeNode("time")
- time.value = re.sub(r"^\d+$", "*", time.value)
+ time.value = re.sub(r"^\d+(\.\d+)?$", "*", time.value)
elif element.tagName == "failure":
- message = element.getAttributeNode("message")
- message.value = re.sub(r"^.*/(.*:)\d+\n", "\\1*\n", message.value)
+ for child in element.childNodes:
+ if child.nodeType == Node.CDATA_SECTION_NODE:
+ # Removes the source line number.
+ cdata = re.sub(r"^.*/(.*:)\d+\n", "\\1*\n", child.nodeValue)
+ # Removes the actual stack trace.
+ child.nodeValue = re.sub(r"\nStack trace:\n(.|\n)*",
+ "", cdata)
for child in element.childNodes:
if child.nodeType == Node.ELEMENT_NODE:
- self._NormalizeXml(child)
+ self.NormalizeXml(child)