mirror of
https://github.com/google/googletest.git
synced 2025-01-14 16:37:56 +08:00
Adds Win32 UNC path support to FilePath::IsAbsolutePath() and FilePath::IsRootDirectory() in GoogleTest
Fixes: #3025 PiperOrigin-RevId: 481932601 Change-Id: I90fcb5b3d189aea79a0fd18735bad038b3511270
This commit is contained in:
parent
26d3ab5442
commit
f372c76026
@ -199,6 +199,16 @@ class GTEST_API_ FilePath {
|
|||||||
// separators. Returns NULL if no path separator was found.
|
// separators. Returns NULL if no path separator was found.
|
||||||
const char* FindLastPathSeparator() const;
|
const char* FindLastPathSeparator() const;
|
||||||
|
|
||||||
|
// Returns the length of the path root, including the directory separator at
|
||||||
|
// the end of the prefix. Returns zero by definition if the path is relative.
|
||||||
|
// Examples:
|
||||||
|
// - [Windows] "..\Sibling" => 0
|
||||||
|
// - [Windows] "\Windows" => 1
|
||||||
|
// - [Windows] "C:/Windows\Notepad.exe" => 3
|
||||||
|
// - [Windows] "\\Host\Share\C$/Windows" => 13
|
||||||
|
// - [UNIX] "/bin" => 1
|
||||||
|
size_t CalculateRootLength() const;
|
||||||
|
|
||||||
std::string pathname_;
|
std::string pathname_;
|
||||||
}; // class FilePath
|
}; // class FilePath
|
||||||
|
|
||||||
|
@ -145,6 +145,45 @@ const char* FilePath::FindLastPathSeparator() const {
|
|||||||
return last_sep;
|
return last_sep;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t FilePath::CalculateRootLength() const {
|
||||||
|
const auto &path = pathname_;
|
||||||
|
auto s = path.begin();
|
||||||
|
auto end = path.end();
|
||||||
|
#if GTEST_OS_WINDOWS
|
||||||
|
if (end - s >= 2 && s[1] == ':' &&
|
||||||
|
(end - s == 2 || IsPathSeparator(s[2])) &&
|
||||||
|
(('A' <= s[0] && s[0] <= 'Z') || ('a' <= s[0] && s[0] <= 'z'))) {
|
||||||
|
// A typical absolute path like "C:\Windows" or "D:"
|
||||||
|
s += 2;
|
||||||
|
if (s != end) {
|
||||||
|
++s;
|
||||||
|
}
|
||||||
|
} else if (end - s >= 3 && IsPathSeparator(*s) && IsPathSeparator(*(s + 1))
|
||||||
|
&& !IsPathSeparator(*(s + 2))) {
|
||||||
|
// Move past the "\\" prefix in a UNC path like "\\Server\Share\Folder"
|
||||||
|
s += 2;
|
||||||
|
// Skip 2 components and their following separators ("Server\" and "Share\")
|
||||||
|
for (int i = 0; i < 2; ++i) {
|
||||||
|
while (s != end) {
|
||||||
|
bool stop = IsPathSeparator(*s);
|
||||||
|
++s;
|
||||||
|
if (stop) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (s != end && IsPathSeparator(*s)) {
|
||||||
|
// A drive-rooted path like "\Windows"
|
||||||
|
++s;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (s != end && IsPathSeparator(*s)) {
|
||||||
|
++s;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return static_cast<size_t>(s - path.begin());
|
||||||
|
}
|
||||||
|
|
||||||
// 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
|
||||||
@ -246,26 +285,16 @@ bool FilePath::DirectoryExists() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if pathname describes a root directory. (Windows has one
|
// Returns true if pathname describes a root directory. (Windows has one
|
||||||
// root directory per disk drive.)
|
// root directory per disk drive. UNC share roots are also included.)
|
||||||
bool FilePath::IsRootDirectory() const {
|
bool FilePath::IsRootDirectory() const {
|
||||||
#if GTEST_OS_WINDOWS
|
size_t root_length = CalculateRootLength();
|
||||||
return pathname_.length() == 3 && IsAbsolutePath();
|
return root_length > 0 && root_length == pathname_.size() &&
|
||||||
#else
|
IsPathSeparator(pathname_[root_length - 1]);
|
||||||
return pathname_.length() == 1 && IsPathSeparator(pathname_.c_str()[0]);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if pathname describes an absolute path.
|
// Returns true if pathname describes an absolute path.
|
||||||
bool FilePath::IsAbsolutePath() const {
|
bool FilePath::IsAbsolutePath() const {
|
||||||
const char* const name = pathname_.c_str();
|
return CalculateRootLength() > 0;
|
||||||
#if GTEST_OS_WINDOWS
|
|
||||||
return pathname_.length() >= 3 &&
|
|
||||||
((name[0] >= 'a' && name[0] <= 'z') ||
|
|
||||||
(name[0] >= 'A' && name[0] <= 'Z')) &&
|
|
||||||
name[1] == ':' && IsPathSeparator(name[2]);
|
|
||||||
#else
|
|
||||||
return IsPathSeparator(name[0]);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a pathname for a file that does not currently exist. The pathname
|
// Returns a pathname for a file that does not currently exist. The pathname
|
||||||
@ -347,17 +376,27 @@ FilePath FilePath::RemoveTrailingPathSeparator() const {
|
|||||||
// Removes any redundant separators that might be in the pathname.
|
// Removes any redundant separators that might be in the pathname.
|
||||||
// For example, "bar///foo" becomes "bar/foo". Does not eliminate other
|
// For example, "bar///foo" becomes "bar/foo". Does not eliminate other
|
||||||
// redundancies that might be in a pathname involving "." or "..".
|
// redundancies that might be in a pathname involving "." or "..".
|
||||||
|
// Note that "\\Host\Share" does not contain a redundancy on Windows!
|
||||||
void FilePath::Normalize() {
|
void FilePath::Normalize() {
|
||||||
auto out = pathname_.begin();
|
auto out = pathname_.begin();
|
||||||
|
|
||||||
for (const char character : pathname_) {
|
auto i = pathname_.cbegin();
|
||||||
|
#if GTEST_OS_WINDOWS
|
||||||
|
// UNC paths are treated specially
|
||||||
|
if (pathname_.end() - i >= 3 && IsPathSeparator(*i) &&
|
||||||
|
IsPathSeparator(*(i + 1)) && !IsPathSeparator(*(i + 2))) {
|
||||||
|
*(out++) = kPathSeparator;
|
||||||
|
*(out++) = kPathSeparator;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
while (i != pathname_.end()) {
|
||||||
|
const char character = *i;
|
||||||
if (!IsPathSeparator(character)) {
|
if (!IsPathSeparator(character)) {
|
||||||
*(out++) = character;
|
*(out++) = character;
|
||||||
} else if (out == pathname_.begin() || *std::prev(out) != kPathSeparator) {
|
} else if (out == pathname_.begin() || *std::prev(out) != kPathSeparator) {
|
||||||
*(out++) = kPathSeparator;
|
*(out++) = kPathSeparator;
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
++i;
|
||||||
}
|
}
|
||||||
|
|
||||||
pathname_.erase(out, pathname_.end());
|
pathname_.erase(out, pathname_.end());
|
||||||
|
@ -421,8 +421,13 @@ TEST(NormalizeTest, MultipleConsecutiveSeparatorsInMidstring) {
|
|||||||
// "/bar" == //bar" == "///bar"
|
// "/bar" == //bar" == "///bar"
|
||||||
TEST(NormalizeTest, MultipleConsecutiveSeparatorsAtStringStart) {
|
TEST(NormalizeTest, MultipleConsecutiveSeparatorsAtStringStart) {
|
||||||
EXPECT_EQ(GTEST_PATH_SEP_ "bar", FilePath(GTEST_PATH_SEP_ "bar").string());
|
EXPECT_EQ(GTEST_PATH_SEP_ "bar", FilePath(GTEST_PATH_SEP_ "bar").string());
|
||||||
|
#if GTEST_OS_WINDOWS
|
||||||
|
EXPECT_EQ(GTEST_PATH_SEP_ GTEST_PATH_SEP_ "bar",
|
||||||
|
FilePath(GTEST_PATH_SEP_ GTEST_PATH_SEP_ "bar").string());
|
||||||
|
#else
|
||||||
EXPECT_EQ(GTEST_PATH_SEP_ "bar",
|
EXPECT_EQ(GTEST_PATH_SEP_ "bar",
|
||||||
FilePath(GTEST_PATH_SEP_ GTEST_PATH_SEP_ "bar").string());
|
FilePath(GTEST_PATH_SEP_ GTEST_PATH_SEP_ "bar").string());
|
||||||
|
#endif
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
GTEST_PATH_SEP_ "bar",
|
GTEST_PATH_SEP_ "bar",
|
||||||
FilePath(GTEST_PATH_SEP_ GTEST_PATH_SEP_ GTEST_PATH_SEP_ "bar").string());
|
FilePath(GTEST_PATH_SEP_ GTEST_PATH_SEP_ GTEST_PATH_SEP_ "bar").string());
|
||||||
@ -621,6 +626,9 @@ TEST(FilePathTest, IsAbsolutePath) {
|
|||||||
EXPECT_TRUE(
|
EXPECT_TRUE(
|
||||||
FilePath("c:/" GTEST_PATH_SEP_ "is_not" GTEST_PATH_SEP_ "relative")
|
FilePath("c:/" GTEST_PATH_SEP_ "is_not" GTEST_PATH_SEP_ "relative")
|
||||||
.IsAbsolutePath());
|
.IsAbsolutePath());
|
||||||
|
EXPECT_TRUE(FilePath("d:/Windows").IsAbsolutePath());
|
||||||
|
EXPECT_TRUE(FilePath("\\\\Host\\Share").IsAbsolutePath());
|
||||||
|
EXPECT_TRUE(FilePath("\\\\Host\\Share\\Folder").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());
|
||||||
@ -637,6 +645,16 @@ TEST(FilePathTest, IsRootDirectory) {
|
|||||||
EXPECT_FALSE(FilePath("b:a").IsRootDirectory());
|
EXPECT_FALSE(FilePath("b:a").IsRootDirectory());
|
||||||
EXPECT_FALSE(FilePath("8:/").IsRootDirectory());
|
EXPECT_FALSE(FilePath("8:/").IsRootDirectory());
|
||||||
EXPECT_FALSE(FilePath("c|/").IsRootDirectory());
|
EXPECT_FALSE(FilePath("c|/").IsRootDirectory());
|
||||||
|
EXPECT_TRUE(FilePath("c:/").IsRootDirectory());
|
||||||
|
EXPECT_FALSE(FilePath("d:/Windows").IsRootDirectory());
|
||||||
|
|
||||||
|
// This is for backward compatibility, since callers (even in this library)
|
||||||
|
// have assumed IsRootDirectory() implies a trailing directory separator.
|
||||||
|
EXPECT_FALSE(FilePath("\\\\Host\\Share").IsRootDirectory());
|
||||||
|
|
||||||
|
EXPECT_TRUE(FilePath("\\\\Host\\Share\\").IsRootDirectory());
|
||||||
|
EXPECT_FALSE(FilePath("\\\\Host\\Share\\.").IsRootDirectory());
|
||||||
|
EXPECT_FALSE(FilePath("\\\\Host\\Share\\C$\\").IsRootDirectory());
|
||||||
#else
|
#else
|
||||||
EXPECT_TRUE(FilePath("/").IsRootDirectory());
|
EXPECT_TRUE(FilePath("/").IsRootDirectory());
|
||||||
EXPECT_TRUE(FilePath("//").IsRootDirectory());
|
EXPECT_TRUE(FilePath("//").IsRootDirectory());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user