Commit 8d373310 by zhanyong.wan

Adds support for alternate path separator on Windows, and make all tests pass…

Adds support for alternate path separator on Windows, and make all tests pass with CMake and VC++ 9 (by Manuel Klimek).
parent 81e1cc73
...@@ -189,9 +189,18 @@ class FilePath { ...@@ -189,9 +189,18 @@ class FilePath {
// particular, RemoveTrailingPathSeparator() only removes one separator, and // particular, RemoveTrailingPathSeparator() only removes one separator, and
// it is called in CreateDirectoriesRecursively() assuming that it will change // it is called in CreateDirectoriesRecursively() assuming that it will change
// a pathname from directory syntax (trailing separator) to filename syntax. // a pathname from directory syntax (trailing separator) to filename syntax.
//
// On Windows this method also replaces the alternate path separator '/' with
// the primary path separator '\\', so that for example "bar\\/\\foo" becomes
// "bar\\foo".
void Normalize(); void Normalize();
// Returns a pointer to the last occurence of a valid path separator in
// the FilePath. On Windows, for example, both '/' and '\' are valid path
// separators. Returns NULL if no path separator was found.
const char* FindLastPathSeparator() const;
String pathname_; String pathname_;
}; // class FilePath }; // class FilePath
......
...@@ -810,10 +810,12 @@ struct is_pointer<T*> : public true_type {}; ...@@ -810,10 +810,12 @@ struct is_pointer<T*> : public true_type {};
#if GTEST_OS_WINDOWS #if GTEST_OS_WINDOWS
#define GTEST_PATH_SEP_ "\\" #define GTEST_PATH_SEP_ "\\"
#define GTEST_HAS_ALT_PATH_SEP_ 1
// The biggest signed integer type the compiler supports. // The biggest signed integer type the compiler supports.
typedef __int64 BiggestInt; typedef __int64 BiggestInt;
#else #else
#define GTEST_PATH_SEP_ "/" #define GTEST_PATH_SEP_ "/"
#define GTEST_HAS_ALT_PATH_SEP_ 0
typedef long long BiggestInt; // NOLINT typedef long long BiggestInt; // NOLINT
#endif // GTEST_OS_WINDOWS #endif // GTEST_OS_WINDOWS
......
...@@ -63,8 +63,14 @@ namespace testing { ...@@ -63,8 +63,14 @@ namespace testing {
namespace internal { namespace internal {
#if GTEST_OS_WINDOWS #if GTEST_OS_WINDOWS
// On Windows, '\\' is the standard path separator, but many tools and the
// Windows API also accept '/' as an alternate path separator. Unless otherwise
// noted, a file path can contain either kind of path separators, or a mixture
// of them.
const char kPathSeparator = '\\'; const char kPathSeparator = '\\';
const char kAlternatePathSeparator = '/';
const char kPathSeparatorString[] = "\\"; const char kPathSeparatorString[] = "\\";
const char kAlternatePathSeparatorString[] = "/";
#if GTEST_OS_WINDOWS_MOBILE #if GTEST_OS_WINDOWS_MOBILE
// Windows CE doesn't have a current directory. You should not use // Windows CE doesn't have a current directory. You should not use
// the current directory in tests on Windows CE, but this at least // the current directory in tests on Windows CE, but this at least
...@@ -81,6 +87,15 @@ const char kPathSeparatorString[] = "/"; ...@@ -81,6 +87,15 @@ const char kPathSeparatorString[] = "/";
const char kCurrentDirectoryString[] = "./"; const char kCurrentDirectoryString[] = "./";
#endif // GTEST_OS_WINDOWS #endif // GTEST_OS_WINDOWS
// Returns whether the given character is a valid path separator.
static bool IsPathSeparator(char c) {
#if GTEST_HAS_ALT_PATH_SEP_
return (c == kPathSeparator) || (c == kAlternatePathSeparator);
#else
return c == kPathSeparator;
#endif
}
// Returns the current working directory, or "" if unsuccessful. // Returns the current working directory, or "" if unsuccessful.
FilePath FilePath::GetCurrentDir() { FilePath FilePath::GetCurrentDir() {
#if GTEST_OS_WINDOWS_MOBILE #if GTEST_OS_WINDOWS_MOBILE
...@@ -108,6 +123,22 @@ FilePath FilePath::RemoveExtension(const char* extension) const { ...@@ -108,6 +123,22 @@ FilePath FilePath::RemoveExtension(const char* extension) const {
return *this; return *this;
} }
// Returns a pointer to the last occurence of a valid path separator in
// the FilePath. On Windows, for example, both '/' and '\' are valid path
// separators. Returns NULL if no path separator was found.
const char* FilePath::FindLastPathSeparator() const {
const char* const last_sep = strrchr(c_str(), kPathSeparator);
#if GTEST_HAS_ALT_PATH_SEP_
const char* const last_alt_sep = strrchr(c_str(), kAlternatePathSeparator);
// Comparing two pointers of which only one is NULL is undefined.
if (last_alt_sep != NULL &&
(last_sep == NULL || last_alt_sep > last_sep)) {
return last_alt_sep;
}
#endif
return last_sep;
}
// Returns a copy of the FilePath with the directory part removed. // Returns a copy of the FilePath with the directory part removed.
// Example: FilePath("path/to/file").RemoveDirectoryName() returns // Example: FilePath("path/to/file").RemoveDirectoryName() returns
// FilePath("file"). If there is no directory part ("just_a_file"), it returns // FilePath("file"). If there is no directory part ("just_a_file"), it returns
...@@ -115,7 +146,7 @@ FilePath FilePath::RemoveExtension(const char* extension) const { ...@@ -115,7 +146,7 @@ FilePath FilePath::RemoveExtension(const char* extension) const {
// returns an empty FilePath (""). // returns an empty FilePath ("").
// On Windows platform, '\' is the path separator, otherwise it is '/'. // On Windows platform, '\' is the path separator, otherwise it is '/'.
FilePath FilePath::RemoveDirectoryName() const { FilePath FilePath::RemoveDirectoryName() const {
const char* const last_sep = strrchr(c_str(), kPathSeparator); const char* const last_sep = FindLastPathSeparator();
return last_sep ? FilePath(String(last_sep + 1)) : *this; return last_sep ? FilePath(String(last_sep + 1)) : *this;
} }
...@@ -126,7 +157,7 @@ FilePath FilePath::RemoveDirectoryName() const { ...@@ -126,7 +157,7 @@ FilePath FilePath::RemoveDirectoryName() const {
// not have a file, like "just/a/dir/", it returns the FilePath unmodified. // not have a file, like "just/a/dir/", it returns the FilePath unmodified.
// On Windows platform, '\' is the path separator, otherwise it is '/'. // On Windows platform, '\' is the path separator, otherwise it is '/'.
FilePath FilePath::RemoveFileName() const { FilePath FilePath::RemoveFileName() const {
const char* const last_sep = strrchr(c_str(), kPathSeparator); const char* const last_sep = FindLastPathSeparator();
String dir; String dir;
if (last_sep) { if (last_sep) {
dir = String(c_str(), last_sep + 1 - c_str()); dir = String(c_str(), last_sep + 1 - c_str());
...@@ -219,7 +250,7 @@ bool FilePath::IsRootDirectory() const { ...@@ -219,7 +250,7 @@ bool FilePath::IsRootDirectory() const {
// current directory. Handle this properly. // current directory. Handle this properly.
return pathname_.length() == 3 && IsAbsolutePath(); return pathname_.length() == 3 && IsAbsolutePath();
#else #else
return pathname_ == kPathSeparatorString; return pathname_.length() == 1 && IsPathSeparator(pathname_.c_str()[0]);
#endif #endif
} }
...@@ -231,9 +262,9 @@ bool FilePath::IsAbsolutePath() const { ...@@ -231,9 +262,9 @@ bool FilePath::IsAbsolutePath() const {
((name[0] >= 'a' && name[0] <= 'z') || ((name[0] >= 'a' && name[0] <= 'z') ||
(name[0] >= 'A' && name[0] <= 'Z')) && (name[0] >= 'A' && name[0] <= 'Z')) &&
name[1] == ':' && name[1] == ':' &&
name[2] == kPathSeparator; IsPathSeparator(name[2]);
#else #else
return name[0] == kPathSeparator; return IsPathSeparator(name[0]);
#endif #endif
} }
...@@ -260,7 +291,8 @@ FilePath FilePath::GenerateUniqueFileName(const FilePath& directory, ...@@ -260,7 +291,8 @@ FilePath FilePath::GenerateUniqueFileName(const FilePath& directory,
// it is intended to represent a directory. Returns false otherwise. // it is intended to represent a directory. Returns false otherwise.
// This does NOT check that a directory (or file) actually exists. // This does NOT check that a directory (or file) actually exists.
bool FilePath::IsDirectory() const { bool FilePath::IsDirectory() const {
return pathname_.EndsWith(kPathSeparatorString); return !pathname_.empty() &&
IsPathSeparator(pathname_.c_str()[pathname_.length() - 1]);
} }
// Create directories so that path exists. Returns true if successful or if // Create directories so that path exists. Returns true if successful or if
...@@ -305,7 +337,7 @@ bool FilePath::CreateFolder() const { ...@@ -305,7 +337,7 @@ bool FilePath::CreateFolder() const {
// name, otherwise return the name string unmodified. // name, otherwise return the name string unmodified.
// On Windows platform, uses \ as the separator, other platforms use /. // On Windows platform, uses \ as the separator, other platforms use /.
FilePath FilePath::RemoveTrailingPathSeparator() const { FilePath FilePath::RemoveTrailingPathSeparator() const {
return pathname_.EndsWith(kPathSeparatorString) return IsDirectory()
? FilePath(String(pathname_.c_str(), pathname_.length() - 1)) ? FilePath(String(pathname_.c_str(), pathname_.length() - 1))
: *this; : *this;
} }
...@@ -324,12 +356,19 @@ void FilePath::Normalize() { ...@@ -324,12 +356,19 @@ void FilePath::Normalize() {
memset(dest_ptr, 0, pathname_.length() + 1); memset(dest_ptr, 0, pathname_.length() + 1);
while (*src != '\0') { while (*src != '\0') {
*dest_ptr++ = *src; *dest_ptr = *src;
if (*src != kPathSeparator) if (!IsPathSeparator(*src)) {
src++; src++;
else } else {
while (*src == kPathSeparator) #if GTEST_HAS_ALT_PATH_SEP_
if (*dest_ptr == kAlternatePathSeparator) {
*dest_ptr = kPathSeparator;
}
#endif
while (IsPathSeparator(*src))
src++; src++;
}
dest_ptr++;
} }
*dest_ptr = '\0'; *dest_ptr = '\0';
pathname_ = dest; pathname_ = dest;
......
...@@ -657,24 +657,6 @@ static void TestExitMacros() { ...@@ -657,24 +657,6 @@ static void TestExitMacros() {
EXPECT_EXIT(_exit(1), testing::ExitedWithCode(1), ""); EXPECT_EXIT(_exit(1), testing::ExitedWithCode(1), "");
ASSERT_EXIT(_exit(42), testing::ExitedWithCode(42), ""); ASSERT_EXIT(_exit(42), testing::ExitedWithCode(42), "");
#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW
// MinGW (as of MinGW 5.1.6 and MSYS 1.0.11) does not tag crashed
// processes with non-zero exit code and does not honor calls to
// SetErrorMode(SEM_NOGPFAULTERRORBOX) that are supposed to suppress
// error pop-ups.
EXPECT_EXIT({
testing::GTEST_FLAG(catch_exceptions) = false;
*static_cast<int*>(NULL) = 1;
}, testing::ExitedWithCode(0xC0000005), "") << "foo";
EXPECT_NONFATAL_FAILURE({ // NOLINT
EXPECT_EXIT({
testing::GTEST_FLAG(catch_exceptions) = false;
*static_cast<int*>(NULL) = 1;
}, testing::ExitedWithCode(0), "") << "This failure is expected.";
}, "This failure is expected.");
#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW
#if GTEST_OS_WINDOWS #if GTEST_OS_WINDOWS
// Of all signals effects on the process exit code, only those of SIGABRT // Of all signals effects on the process exit code, only those of SIGABRT
// are documented on Windows. // are documented on Windows.
......
...@@ -151,6 +151,36 @@ TEST(RemoveDirectoryNameTest, ShouldAlsoGiveFileName) { ...@@ -151,6 +151,36 @@ TEST(RemoveDirectoryNameTest, ShouldAlsoGiveFileName) {
.RemoveDirectoryName().c_str()); .RemoveDirectoryName().c_str());
} }
#if GTEST_HAS_ALT_PATH_SEP_
// Test RemoveDirectory* functions with "/".
// RemoveDirectoryName "/afile" -> "afile"
TEST(RemoveDirectoryNameTest, RootFileShouldGiveFileNameForAlternateSeparator) {
EXPECT_STREQ("afile",
FilePath("/afile").RemoveDirectoryName().c_str());
}
// RemoveDirectoryName "adir/" -> ""
TEST(RemoveDirectoryNameTest, WhereThereIsNoFileNameForAlternateSeparator) {
EXPECT_STREQ("",
FilePath("adir/").RemoveDirectoryName().c_str());
}
// RemoveDirectoryName "adir/afile" -> "afile"
TEST(RemoveDirectoryNameTest, ShouldGiveFileNameForAlternateSeparator) {
EXPECT_STREQ("afile",
FilePath("adir/afile").RemoveDirectoryName().c_str());
}
// RemoveDirectoryName "adir/subdir/afile" -> "afile"
TEST(RemoveDirectoryNameTest, ShouldAlsoGiveFileNameForAlternateSeparator) {
EXPECT_STREQ("afile",
FilePath("adir/subdir/afile")
.RemoveDirectoryName().c_str());
}
#endif
// RemoveFileName "" -> "./" // RemoveFileName "" -> "./"
TEST(RemoveFileNameTest, EmptyName) { TEST(RemoveFileNameTest, EmptyName) {
...@@ -190,6 +220,37 @@ TEST(RemoveFileNameTest, GivesRootDir) { ...@@ -190,6 +220,37 @@ TEST(RemoveFileNameTest, GivesRootDir) {
FilePath(GTEST_PATH_SEP_ "afile").RemoveFileName().c_str()); FilePath(GTEST_PATH_SEP_ "afile").RemoveFileName().c_str());
} }
#if GTEST_HAS_ALT_PATH_SEP_
// Test RemoveFile* functions with "/".
// RemoveFileName "adir/" -> "adir/"
TEST(RemoveFileNameTest, ButNoFileForAlternateSeparator) {
EXPECT_STREQ("adir" GTEST_PATH_SEP_,
FilePath("adir/").RemoveFileName().c_str());
}
// RemoveFileName "adir/afile" -> "adir/"
TEST(RemoveFileNameTest, GivesDirNameForAlternateSeparator) {
EXPECT_STREQ("adir" GTEST_PATH_SEP_,
FilePath("adir/afile")
.RemoveFileName().c_str());
}
// RemoveFileName "adir/subdir/afile" -> "adir/subdir/"
TEST(RemoveFileNameTest, GivesDirAndSubDirNameForAlternateSeparator) {
EXPECT_STREQ("adir" GTEST_PATH_SEP_ "subdir" GTEST_PATH_SEP_,
FilePath("adir/subdir/afile")
.RemoveFileName().c_str());
}
// RemoveFileName "/afile" -> "\"
TEST(RemoveFileNameTest, GivesRootDirForAlternateSeparator) {
EXPECT_STREQ(GTEST_PATH_SEP_,
FilePath("/afile").RemoveFileName().c_str());
}
#endif
TEST(MakeFileNameTest, GenerateWhenNumberIsZero) { TEST(MakeFileNameTest, GenerateWhenNumberIsZero) {
FilePath actual = FilePath::MakeFileName(FilePath("foo"), FilePath("bar"), FilePath actual = FilePath::MakeFileName(FilePath("foo"), FilePath("bar"),
...@@ -295,6 +356,11 @@ TEST(RemoveTrailingPathSeparatorTest, ShouldRemoveTrailingSeparator) { ...@@ -295,6 +356,11 @@ TEST(RemoveTrailingPathSeparatorTest, ShouldRemoveTrailingSeparator) {
EXPECT_STREQ( EXPECT_STREQ(
"foo", "foo",
FilePath("foo" GTEST_PATH_SEP_).RemoveTrailingPathSeparator().c_str()); FilePath("foo" GTEST_PATH_SEP_).RemoveTrailingPathSeparator().c_str());
#if GTEST_HAS_ALT_PATH_SEP_
EXPECT_STREQ(
"foo",
FilePath("foo/").RemoveTrailingPathSeparator().c_str());
#endif
} }
// RemoveTrailingPathSeparator "foo/bar/" -> "foo/bar/" // RemoveTrailingPathSeparator "foo/bar/" -> "foo/bar/"
...@@ -397,6 +463,20 @@ TEST(NormalizeTest, MultipleConsecutiveSepaparatorsAtStringEnd) { ...@@ -397,6 +463,20 @@ TEST(NormalizeTest, MultipleConsecutiveSepaparatorsAtStringEnd) {
FilePath("foo" GTEST_PATH_SEP_ GTEST_PATH_SEP_ GTEST_PATH_SEP_).c_str()); FilePath("foo" GTEST_PATH_SEP_ GTEST_PATH_SEP_ GTEST_PATH_SEP_).c_str());
} }
#if GTEST_HAS_ALT_PATH_SEP_
// "foo\" =="foo/\" == "foo\\/"
TEST(NormalizeTest, MixAlternateSeparatorAtStringEnd) {
EXPECT_STREQ("foo" GTEST_PATH_SEP_,
FilePath("foo/").c_str());
EXPECT_STREQ("foo" GTEST_PATH_SEP_,
FilePath("foo" GTEST_PATH_SEP_ "/").c_str());
EXPECT_STREQ("foo" GTEST_PATH_SEP_,
FilePath("foo//" GTEST_PATH_SEP_).c_str());
}
#endif
TEST(AssignmentOperatorTest, DefaultAssignedToNonDefault) { TEST(AssignmentOperatorTest, DefaultAssignedToNonDefault) {
FilePath default_path; FilePath default_path;
FilePath non_default_path("path"); FilePath non_default_path("path");
...@@ -566,6 +646,9 @@ TEST(FilePathTest, RemoveExtensionWhenThereIsNoExtension) { ...@@ -566,6 +646,9 @@ TEST(FilePathTest, RemoveExtensionWhenThereIsNoExtension) {
TEST(FilePathTest, IsDirectory) { TEST(FilePathTest, IsDirectory) {
EXPECT_FALSE(FilePath("cola").IsDirectory()); EXPECT_FALSE(FilePath("cola").IsDirectory());
EXPECT_TRUE(FilePath("koala" GTEST_PATH_SEP_).IsDirectory()); EXPECT_TRUE(FilePath("koala" GTEST_PATH_SEP_).IsDirectory());
#if GTEST_HAS_ALT_PATH_SEP_
EXPECT_TRUE(FilePath("koala/").IsDirectory());
#endif
} }
TEST(FilePathTest, IsAbsolutePath) { TEST(FilePathTest, IsAbsolutePath) {
...@@ -575,12 +658,32 @@ TEST(FilePathTest, IsAbsolutePath) { ...@@ -575,12 +658,32 @@ TEST(FilePathTest, IsAbsolutePath) {
EXPECT_TRUE(FilePath("c:\\" GTEST_PATH_SEP_ "is_not" EXPECT_TRUE(FilePath("c:\\" GTEST_PATH_SEP_ "is_not"
GTEST_PATH_SEP_ "relative").IsAbsolutePath()); GTEST_PATH_SEP_ "relative").IsAbsolutePath());
EXPECT_FALSE(FilePath("c:foo" GTEST_PATH_SEP_ "bar").IsAbsolutePath()); EXPECT_FALSE(FilePath("c:foo" GTEST_PATH_SEP_ "bar").IsAbsolutePath());
EXPECT_TRUE(FilePath("c:/" GTEST_PATH_SEP_ "is_not"
GTEST_PATH_SEP_ "relative").IsAbsolutePath());
#else #else
EXPECT_TRUE(FilePath(GTEST_PATH_SEP_ "is_not" GTEST_PATH_SEP_ "relative") EXPECT_TRUE(FilePath(GTEST_PATH_SEP_ "is_not" GTEST_PATH_SEP_ "relative")
.IsAbsolutePath()); .IsAbsolutePath());
#endif // GTEST_OS_WINDOWS #endif // GTEST_OS_WINDOWS
} }
TEST(FilePathTest, IsRootDirectory) {
#if GTEST_OS_WINDOWS
EXPECT_TRUE(FilePath("a:\\").IsRootDirectory());
EXPECT_TRUE(FilePath("Z:/").IsRootDirectory());
EXPECT_TRUE(FilePath("e://").IsRootDirectory());
EXPECT_FALSE(FilePath("").IsRootDirectory());
EXPECT_FALSE(FilePath("b:").IsRootDirectory());
EXPECT_FALSE(FilePath("b:a").IsRootDirectory());
EXPECT_FALSE(FilePath("8:/").IsRootDirectory());
EXPECT_FALSE(FilePath("c|/").IsRootDirectory());
#else
EXPECT_TRUE(FilePath("/").IsRootDirectory());
EXPECT_FALSE(FilePath("").IsRootDirectory());
EXPECT_FALSE(FilePath("\\").IsRootDirectory());
EXPECT_FALSE(FilePath("/x").IsRootDirectory());
#endif
}
} // namespace } // namespace
} // namespace internal } // namespace internal
} // namespace testing } // namespace testing
......
...@@ -43,6 +43,7 @@ ...@@ -43,6 +43,7 @@
#if GTEST_OS_WINDOWS #if GTEST_OS_WINDOWS
#include <windows.h> #include <windows.h>
#include <stdlib.h>
#endif #endif
namespace { namespace {
...@@ -52,6 +53,14 @@ TEST(Foo, Bar) { ...@@ -52,6 +53,14 @@ TEST(Foo, Bar) {
EXPECT_EQ(2, 3); EXPECT_EQ(2, 3);
} }
#if GTEST_HAS_SEH && !GTEST_OS_WINDOWS_MOBILE
// On Windows Mobile global exception handlers are not supported.
LONG WINAPI ExitWithExceptionCode(
struct _EXCEPTION_POINTERS* exception_pointers) {
exit(exception_pointers->ExceptionRecord->ExceptionCode);
}
#endif
} // namespace } // namespace
int main(int argc, char **argv) { int main(int argc, char **argv) {
...@@ -59,7 +68,18 @@ int main(int argc, char **argv) { ...@@ -59,7 +68,18 @@ int main(int argc, char **argv) {
// Suppresses display of the Windows error dialog upon encountering // Suppresses display of the Windows error dialog upon encountering
// a general protection fault (segment violation). // a general protection fault (segment violation).
SetErrorMode(SEM_NOGPFAULTERRORBOX | SEM_FAILCRITICALERRORS); SetErrorMode(SEM_NOGPFAULTERRORBOX | SEM_FAILCRITICALERRORS);
#if !GTEST_OS_WINDOWS_MOBILE
// The default unhandled exception filter does not always exit
// with the exception code as exit code - for example it exits with
// 0 for EXCEPTION_ACCESS_VIOLATION and 1 for EXCEPTION_BREAKPOINT
// if the application is compiled in debug mode. Thus we use our own
// filter which always exits with the exception code for unhandled
// exceptions.
SetUnhandledExceptionFilter(ExitWithExceptionCode);
#endif
#endif #endif
testing::InitGoogleTest(&argc, argv); testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS(); return RUN_ALL_TESTS();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment