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.
|
||||
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_;
|
||||
}; // class FilePath
|
||||
|
||||
|
@ -145,6 +145,45 @@ const char* FilePath::FindLastPathSeparator() const {
|
||||
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.
|
||||
// Example: FilePath("path/to/file").RemoveDirectoryName() 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
|
||||
// root directory per disk drive.)
|
||||
// root directory per disk drive. UNC share roots are also included.)
|
||||
bool FilePath::IsRootDirectory() const {
|
||||
#if GTEST_OS_WINDOWS
|
||||
return pathname_.length() == 3 && IsAbsolutePath();
|
||||
#else
|
||||
return pathname_.length() == 1 && IsPathSeparator(pathname_.c_str()[0]);
|
||||
#endif
|
||||
size_t root_length = CalculateRootLength();
|
||||
return root_length > 0 && root_length == pathname_.size() &&
|
||||
IsPathSeparator(pathname_[root_length - 1]);
|
||||
}
|
||||
|
||||
// Returns true if pathname describes an absolute path.
|
||||
bool FilePath::IsAbsolutePath() const {
|
||||
const char* const name = pathname_.c_str();
|
||||
#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
|
||||
return CalculateRootLength() > 0;
|
||||
}
|
||||
|
||||
// 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.
|
||||
// For example, "bar///foo" becomes "bar/foo". Does not eliminate other
|
||||
// redundancies that might be in a pathname involving "." or "..".
|
||||
// Note that "\\Host\Share" does not contain a redundancy on Windows!
|
||||
void FilePath::Normalize() {
|
||||
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)) {
|
||||
*(out++) = character;
|
||||
} else if (out == pathname_.begin() || *std::prev(out) != kPathSeparator) {
|
||||
*(out++) = kPathSeparator;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
pathname_.erase(out, pathname_.end());
|
||||
|
@ -421,8 +421,13 @@ TEST(NormalizeTest, MultipleConsecutiveSeparatorsInMidstring) {
|
||||
// "/bar" == //bar" == "///bar"
|
||||
TEST(NormalizeTest, MultipleConsecutiveSeparatorsAtStringStart) {
|
||||
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",
|
||||
FilePath(GTEST_PATH_SEP_ GTEST_PATH_SEP_ "bar").string());
|
||||
#endif
|
||||
EXPECT_EQ(
|
||||
GTEST_PATH_SEP_ "bar",
|
||||
FilePath(GTEST_PATH_SEP_ GTEST_PATH_SEP_ GTEST_PATH_SEP_ "bar").string());
|
||||
@ -621,6 +626,9 @@ TEST(FilePathTest, IsAbsolutePath) {
|
||||
EXPECT_TRUE(
|
||||
FilePath("c:/" GTEST_PATH_SEP_ "is_not" GTEST_PATH_SEP_ "relative")
|
||||
.IsAbsolutePath());
|
||||
EXPECT_TRUE(FilePath("d:/Windows").IsAbsolutePath());
|
||||
EXPECT_TRUE(FilePath("\\\\Host\\Share").IsAbsolutePath());
|
||||
EXPECT_TRUE(FilePath("\\\\Host\\Share\\Folder").IsAbsolutePath());
|
||||
#else
|
||||
EXPECT_TRUE(FilePath(GTEST_PATH_SEP_ "is_not" GTEST_PATH_SEP_ "relative")
|
||||
.IsAbsolutePath());
|
||||
@ -637,6 +645,16 @@ TEST(FilePathTest, IsRootDirectory) {
|
||||
EXPECT_FALSE(FilePath("b:a").IsRootDirectory());
|
||||
EXPECT_FALSE(FilePath("8:/").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
|
||||
EXPECT_TRUE(FilePath("/").IsRootDirectory());
|
||||
EXPECT_TRUE(FilePath("//").IsRootDirectory());
|
||||
|
Loading…
Reference in New Issue
Block a user