diff options
author | binji <binji@chromium.org> | 2015-03-19 10:25:43 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-03-19 17:26:34 +0000 |
commit | fea846358e7c2efe04bdeb4b01ecccee6efe7759 (patch) | |
tree | 1aa2f9f8be62b5c885d23afa4324108edc6c329f /native_client_sdk/src | |
parent | 821dbcff61cfb0ab1d602ef505b9545599a86410 (diff) | |
download | chromium_src-fea846358e7c2efe04bdeb4b01ecccee6efe7759.zip chromium_src-fea846358e7c2efe04bdeb4b01ecccee6efe7759.tar.gz chromium_src-fea846358e7c2efe04bdeb4b01ecccee6efe7759.tar.bz2 |
[NaCl SDK] Rewrite nacl_io::Path to avoid STL.
Path used to be a std::vector<std::string>. Now it is just a C array of size
PATH_MAX.
The code has a lot of nasty edge cases because it is trying to match the
previous behavior. Really, all this code should be axed -- it is incorrect to
handle paths the way nacl_io does currently. AFAICT, the only place that should
have to handle paths is the path lookup routine.
Fixing that is a much larger change.
BUG=none
R=sbc@chromium.org
Review URL: https://codereview.chromium.org/1016873004
Cr-Commit-Position: refs/heads/master@{#321386}
Diffstat (limited to 'native_client_sdk/src')
-rw-r--r-- | native_client_sdk/src/libraries/nacl_io/path.cc | 286 | ||||
-rw-r--r-- | native_client_sdk/src/libraries/nacl_io/path.h | 34 | ||||
-rw-r--r-- | native_client_sdk/src/tests/nacl_io_test/path_test.cc | 402 |
3 files changed, 369 insertions, 353 deletions
diff --git a/native_client_sdk/src/libraries/nacl_io/path.cc b/native_client_sdk/src/libraries/nacl_io/path.cc index c524bfa..2196560 100644 --- a/native_client_sdk/src/libraries/nacl_io/path.cc +++ b/native_client_sdk/src/libraries/nacl_io/path.cc @@ -4,6 +4,7 @@ #include "nacl_io/path.h" +#include <assert.h> #include <stdio.h> #include <string.h> #include <string> @@ -12,8 +13,13 @@ namespace nacl_io { +Path::Path() : len_(0) { + path_[0] = 0; +} + Path::Path(const Path& path) { - paths_ = path.paths_; + len_ = path.len_; + strcpy(path_, path.path_); } Path::Path(const std::string& path) { @@ -21,24 +27,54 @@ Path::Path(const std::string& path) { } bool Path::IsAbsolute() const { - return !paths_.empty() && paths_[0] == "/"; + return path_[0] == '/'; } -const std::string& Path::Part(size_t index) const { - return paths_[index]; +std::string Path::Part(size_t index) const { + if (IsAbsolute() && index == 0) { + return std::string("/"); + } + + const char* start = &path_[0]; + size_t slashes = 0; + const char* p; + for (p = &path_[0]; *p; p++) { + if (*p == '/') { + if (++slashes == index + 1) + break; + + start = p + 1; + } + } + + return std::string(start, p - start); } size_t Path::Size() const { - return paths_.size(); + if (len_ == 0) + return 0; + + const char* p = &path_[0]; + if (len_ == 1 && *p == '/') { + return 1; + } + + size_t count = 1; + for (; *p; p++) { + if (*p == '/') + count++; + } + return count; } bool Path::IsRoot() const { - return paths_.empty() || (paths_.size() == 1 && paths_[0] == "/"); + return strcmp(path_, "/") == 0; } Path& Path::MakeRelative() { if (IsAbsolute()) { - paths_.erase(paths_.begin()); + memmove(&path_[0], &path_[1], PATH_MAX - 1); + len_--; } return *this; } @@ -47,14 +83,19 @@ Path& Path::Append(const Path& path) { // Appending an absolute path effectivly sets the path, ignoring // the current contents. if (path.IsAbsolute()) { - paths_ = path.paths_; + strcpy(path_, path.path_); } else { - for (size_t index = 0; index < path.paths_.size(); index++) { - paths_.push_back(path.paths_[index]); + strncat(path_, "/", PATH_MAX - len_ - 1); + len_++; + strncat(path_, path.path_, PATH_MAX - len_ - 1); + len_ += path.len_; + + if (len_ >= PATH_MAX - 1) { + len_ = PATH_MAX - 1; } } - paths_ = Normalize(paths_); + Normalize(); return *this; } @@ -62,142 +103,157 @@ Path& Path::Append(const std::string& path) { return Append(Path(path)); } -Path& Path::Set(const StringArray_t path) { - paths_ = Normalize(path); - return *this; -} - Path& Path::Set(const std::string& path) { - return Set(Split(path)); + strncpy(path_, path.c_str(), PATH_MAX - 1); + path_[PATH_MAX - 1] = 0; + len_ = path.length(); + if (len_ > PATH_MAX - 1) + len_ = PATH_MAX - 1; + Normalize(); + return *this; } Path Path::Parent() const { - Path out; - out.paths_ = paths_; - if (out.paths_.size()) - out.paths_.pop_back(); - return out; + const char* last_slash = strrchr(path_, '/'); + if (last_slash) { + Path out; + if (last_slash == &path_[0]) { + out.len_ = 1; + strcpy(out.path_, "/"); + } else { + out.len_ = last_slash - &path_[0]; + strncpy(out.path_, path_, out.len_); + out.path_[out.len_] = 0; + } + + return out; + } + + return Path(*this); } std::string Path::Basename() const { - if (paths_.size()) - return paths_.back(); - return std::string(); -} + if (IsRoot()) + return std::string(path_); -std::string Path::Join() const { - return Range(paths_, 0, paths_.size()); -} + const char* last_slash = strrchr(path_, '/'); + if (last_slash) + return std::string(last_slash + 1, path_ + len_ - (last_slash + 1)); -std::string Path::Range(size_t start, size_t end) const { - return Range(paths_, start, end); + return std::string(path_); } -StringArray_t Path::Split() const { - return paths_; +std::string Path::Join() const { + return std::string(path_); } -// static -StringArray_t Path::Normalize(const StringArray_t& paths) { - StringArray_t path_out; - - for (size_t index = 0; index < paths.size(); index++) { - const std::string& curr = paths[index]; - - // Check if '/' was used excessively in the path. - // For example, in cd Desktop///// - if (curr == "/" && index != 0) - continue; +std::string Path::Range(size_t start, size_t end) const { + assert(start <= end); - // Check for '.' in the path and remove it - if (curr == ".") - continue; + const char* pstart = &path_[0]; + const char* pend = &path_[len_]; - // Check for '..' - if (curr == "..") { - // If the path is empty, or "..", then add ".." - if (path_out.empty() || path_out.back() == "..") { - path_out.push_back(curr); - continue; - } + if (IsAbsolute() && start == 0 && end == 1) + return std::string("/"); - // If the path is at root, "/.." = "/" - if (path_out.back() == "/") { - continue; - } + size_t slashes = 0; + for (const char* p = &path_[0]; *p; p++) { + if (*p == '/') { + ++slashes; + if (slashes == start) + pstart = p + 1; - // if we are already at root, then stay there (root/.. -> root) - if (path_out.back() == "/") { - continue; + if (slashes == end) { + pend = p; + break; } - - // otherwise, pop off the top path component - path_out.pop_back(); - continue; } - - // By now, we should have handled end cases so just append. - path_out.push_back(curr); } - // If the path was valid, but now it's empty, return self - if (path_out.empty()) - path_out.push_back("."); - - return path_out; -} + if (pstart > pend) + return std::string(); -// static -std::string Path::Join(const StringArray_t& paths) { - return Range(paths, 0, paths.size()); + return std::string(pstart, pend - pstart); } -// static -std::string Path::Range(const StringArray_t& paths, size_t start, size_t end) { - std::string out_path; - size_t index = start; - - if (end > paths.size()) - end = paths.size(); +void Path::Normalize() { + char* outp = &path_[0]; + const char* start = outp; + const char* part_start = start; + const char* next_slash; + bool is_absolute = false; - // If this is an absolute path, paths[0] == "/". In this case, we don't want - // to add an additional / separator. - if (start == 0 && end > 0 && paths[0] == "/") { - out_path += "/"; - index++; + if (IsAbsolute()) { + // Absolute path. Append the slash, then continue the algorithm as if the + // path were relative. + start++; + outp++; + part_start++; + is_absolute = true; } - for (; index < end; index++) { - out_path += paths[index]; - if (index < end - 1) - out_path += "/"; - } + do { + next_slash = strchr(part_start, '/'); + const char* part_end = next_slash; + if (!part_end) + part_end = part_start + strlen(part_start); + + size_t part_len = part_end - part_start; + + bool should_append = true; + if (part_len == 0) { + // Don't append if the part is empty. + should_append = false; + } else if (part_len == 1 && part_start[0] == '.') { + // Don't append "." + should_append = false; + } else if (part_len == 2 && part_start[0] == '.' && part_start[1] == '.') { + // If part is "..", only append if the output is empty or already has + // ".." at the end. + if (outp == start || + (outp - start >= 2 && outp[-1] == '.' && outp[-2] == '.')) { + should_append = !is_absolute; + } else { + should_append = false; + // Move outp backward to the one past the previous slash, or to the + // beginning of the string. Unless outp == start, outp[-1] is a '/'. + if (outp > start) + --outp; + while (outp > start && outp[0] != '/') + --outp; + } + } - return out_path; -} + if (should_append) { + // Append [part_start, part_end) to outp. + if (outp != start) { + // Append slash to separate from previous path. + *outp++ = '/'; + } -// static -StringArray_t Path::Split(const std::string& path) { - StringArray_t path_split; - StringArray_t components; + // Only need to copy bytes when the pointers are different. + if (outp != part_start) { + memmove(outp, part_start, part_len); + } - sdk_util::SplitString(path, '/', &path_split); + outp += part_len; + } - if (path[0] == '/') - components.push_back("/"); + part_start = next_slash + 1; + } while (next_slash); - // Copy path_split to components, removing empty path segments. - for (StringArray_t::const_iterator it = path_split.begin(); - it != path_split.end(); - ++it) { - if (!it->empty()) - components.push_back(*it); + // Return '.' instead of an empty path. + if (outp == start && !is_absolute) { + *outp++ = '.'; } - return components; + + *outp = 0; + len_ = outp - &path_[0]; } Path& Path::operator=(const Path& p) { - paths_ = p.paths_; + len_ = p.len_; + strcpy(path_, p.path_); return *this; } @@ -205,4 +261,12 @@ Path& Path::operator=(const std::string& p) { return Set(p); } +bool Path::operator==(const Path& other) { + return len_ == other.len_ && strncmp(path_, other.path_, len_) == 0; +} + +bool Path::operator!=(const Path& other) { + return !operator==(other); +} + } // namespace nacl_io diff --git a/native_client_sdk/src/libraries/nacl_io/path.h b/native_client_sdk/src/libraries/nacl_io/path.h index ff90113..737e8f1 100644 --- a/native_client_sdk/src/libraries/nacl_io/path.h +++ b/native_client_sdk/src/libraries/nacl_io/path.h @@ -5,6 +5,8 @@ #ifndef LIBRARIES_NACL_IO_PATH_H_ #define LIBRARIES_NACL_IO_PATH_H_ +#include <limits.h> + #include <string> #include <vector> @@ -12,16 +14,10 @@ namespace nacl_io { -typedef std::vector<std::string> StringArray_t; - class Path { public: - Path() {} + Path(); Path(const Path& path); - - // This constructor splits path by '/' as a starting point for this Path. - // If the path begins with the character '/', the path is considered - // to be absolute. explicit Path(const std::string& path); // Return true of the first path item is '/'. @@ -31,7 +27,7 @@ class Path { bool IsRoot() const; // Return a part of the path - const std::string& Part(size_t index) const; + std::string Part(size_t index) const; // Return the number of path parts size_t Size() const; @@ -41,7 +37,6 @@ class Path { Path& Append(const std::string& path); Path& Set(const std::string& path); Path& MakeRelative(); - Path& Set(const StringArray_t path); // Return the parent path. Path Parent() const; @@ -49,26 +44,19 @@ class Path { std::string Join() const; std::string Range(size_t start, size_t end) const; - StringArray_t Split() const; - // Collapse the string list removing extraneous '.', '..' path components - static StringArray_t Normalize(const StringArray_t& paths); - static std::string Join(const StringArray_t& paths); - static std::string Range(const StringArray_t& paths, - size_t start, - size_t end); - static StringArray_t Split(const std::string& paths); // Operator versions Path& operator=(const Path& p); Path& operator=(const std::string& str); - bool operator==(const Path& other) { return Split() == other.Split(); } - bool operator!=(const Path& other) { return !operator==(other); } + bool operator==(const Path& other); + bool operator!=(const Path& other); private: - // Internal representation of the path stored an array of string representing - // the directory traversal. The first string is a "/" if this is an absolute - // path. - StringArray_t paths_; + // Collapse the string list removing extraneous '.', '..' path components + void Normalize(); + + size_t len_; + char path_[PATH_MAX]; }; } // namespace nacl_io diff --git a/native_client_sdk/src/tests/nacl_io_test/path_test.cc b/native_client_sdk/src/tests/nacl_io_test/path_test.cc index 9cdaa682..b7b82cc 100644 --- a/native_client_sdk/src/tests/nacl_io_test/path_test.cc +++ b/native_client_sdk/src/tests/nacl_io_test/path_test.cc @@ -11,253 +11,217 @@ using namespace nacl_io; -TEST(PathTest, SanityChecks) { - // can we construct and delete? - Path ph1("."); - Path *ph2 = new Path("."); - delete ph2; - - Path p1("."); - EXPECT_FALSE(p1.IsAbsolute()); - EXPECT_EQ(".", p1.Join()); - Path p2("/"); - EXPECT_TRUE(p2.IsAbsolute()); - EXPECT_EQ("/", p2.Join()); +TEST(PathTest, Empty) { + Path p; + EXPECT_FALSE(p.IsAbsolute()); + EXPECT_FALSE(p.IsRoot()); + EXPECT_EQ(0, p.Size()); + EXPECT_EQ("", p.Basename()); + EXPECT_EQ("", p.Join()); + EXPECT_EQ("", p.Range(0, 0)); + EXPECT_EQ("", p.Parent().Join()); } -TEST(PathTest, Assignment) { - Path empty; - Path dot("."); - Path root("/"); - Path abs_str("/abs/from/string"); - Path rel_str("rel/from/string"); - Path self_str("./rel/from/string"); - - EXPECT_EQ(0, empty.Size()); - EXPECT_FALSE(empty.IsAbsolute()); - EXPECT_EQ("", empty.Join()); - - EXPECT_EQ(1, dot.Size()); - EXPECT_FALSE(dot.IsAbsolute()); - EXPECT_EQ(".", dot.Join()); - - EXPECT_EQ(1, root.Size()); - EXPECT_TRUE(root.IsAbsolute()); - EXPECT_EQ("/", root.Join()); - - EXPECT_EQ(4, abs_str.Size()); - EXPECT_TRUE(abs_str.IsAbsolute()); - EXPECT_EQ("/abs/from/string", abs_str.Join()); - - EXPECT_EQ(3, rel_str.Size()); - EXPECT_FALSE(rel_str.IsAbsolute()); - EXPECT_EQ("rel/from/string", rel_str.Join()); - - EXPECT_EQ(3, self_str.Size()); - EXPECT_FALSE(self_str.IsAbsolute()); - EXPECT_EQ("rel/from/string", self_str.Join()); - - empty = ""; - dot = "."; - root = "/"; - abs_str = "/abs/from/assign"; - rel_str = "rel/from/assign"; - self_str = "./rel/from/assign"; - - EXPECT_EQ(1, empty.Size()); - EXPECT_FALSE(empty.IsAbsolute()); - EXPECT_EQ(".", empty.Join()); - - EXPECT_EQ(1, dot.Size()); - EXPECT_FALSE(dot.IsAbsolute()); - EXPECT_EQ(".", dot.Join()); - - EXPECT_EQ(1, root.Size()); - EXPECT_TRUE(root.IsAbsolute()); - EXPECT_EQ("/", root.Join()); - - EXPECT_EQ(4, abs_str.Size()); - EXPECT_TRUE(abs_str.IsAbsolute()); - EXPECT_EQ("/abs/from/assign", abs_str.Join()); - - EXPECT_EQ(3, rel_str.Size()); - EXPECT_FALSE(rel_str.IsAbsolute()); - EXPECT_EQ("rel/from/assign", rel_str.Join()); - - EXPECT_EQ(3, self_str.Size()); - EXPECT_FALSE(self_str.IsAbsolute()); - EXPECT_EQ("rel/from/assign", self_str.Join()); - - Path cpy_str; - cpy_str = empty; - EXPECT_EQ(1, cpy_str.Size()); - EXPECT_FALSE(cpy_str.IsAbsolute()); - EXPECT_EQ(".", cpy_str.Join()); - - cpy_str = dot; - EXPECT_EQ(1, cpy_str.Size()); - EXPECT_FALSE(cpy_str.IsAbsolute()); - EXPECT_EQ(".", cpy_str.Join()); - - cpy_str = root; - EXPECT_EQ(1, cpy_str.Size()); - EXPECT_TRUE(cpy_str.IsAbsolute()); - EXPECT_EQ("/", cpy_str.Join()); - - cpy_str = abs_str; - EXPECT_EQ(4, cpy_str.Size()); - EXPECT_TRUE(cpy_str.IsAbsolute()); - EXPECT_EQ("/abs/from/assign", cpy_str.Join()); - - cpy_str = rel_str; - EXPECT_EQ(3, cpy_str.Size()); - EXPECT_FALSE(cpy_str.IsAbsolute()); - EXPECT_EQ("rel/from/assign", cpy_str.Join()); - - cpy_str = self_str; - EXPECT_EQ(3, cpy_str.Size()); - EXPECT_FALSE(cpy_str.IsAbsolute()); - EXPECT_EQ("rel/from/assign", cpy_str.Join()); +TEST(PathTest, Dot) { + Path p("."); + EXPECT_FALSE(p.IsAbsolute()); + EXPECT_FALSE(p.IsRoot()); + EXPECT_EQ(1, p.Size()); + EXPECT_EQ(".", p.Part(0)); + EXPECT_EQ(".", p.Basename()); + EXPECT_EQ(".", p.Join()); + EXPECT_EQ(".", p.Range(0, 1)); + EXPECT_EQ(".", p.Parent().Join()); // TODO(binji): this is unexpected. } +TEST(PathTest, Root) { + Path p("/"); + EXPECT_TRUE(p.IsAbsolute()); + EXPECT_TRUE(p.IsRoot()); + EXPECT_EQ(1, p.Size()); + EXPECT_EQ("/", p.Part(0)); + EXPECT_EQ("/", p.Basename()); + EXPECT_EQ("/", p.Join()); + EXPECT_EQ("/", p.Range(0, 1)); + EXPECT_EQ("/", p.Parent().Join()); +} -TEST(PathTest, Collapse) { - StringArray_t path_components; - - Path p1("/simple/splitter/test"); - path_components = p1.Split(); - EXPECT_EQ("/", path_components[0]); - EXPECT_EQ("/", p1.Part(0)); - - EXPECT_EQ("simple", path_components[1]); - EXPECT_EQ("simple", p1.Part(1)); - - EXPECT_EQ("splitter",path_components[2]); - EXPECT_EQ("splitter",p1.Part(2)); - - EXPECT_EQ("test", path_components[3]); - EXPECT_EQ("test", p1.Part(3)); - - Path p2("///simple//splitter///test/"); - path_components = p2.Split(); - EXPECT_EQ(4, static_cast<int>(path_components.size())); - EXPECT_EQ(4, static_cast<int>(p2.Size())); - EXPECT_EQ("/", path_components[0]); - EXPECT_EQ("simple", path_components[1]); - EXPECT_EQ("splitter", path_components[2]); - EXPECT_EQ("test", path_components[3]); - - Path p3("sim/ple//spli/tter/te/st/"); - path_components = p3.Split(); - EXPECT_EQ(6, static_cast<int>(path_components.size())); - EXPECT_FALSE(p3.IsAbsolute()); - EXPECT_EQ("sim", path_components[0]); - EXPECT_EQ("ple", path_components[1]); - EXPECT_EQ("spli", path_components[2]); - EXPECT_EQ("tter", path_components[3]); - EXPECT_EQ("te", path_components[4]); - EXPECT_EQ("st", path_components[5]); - - Path p4(""); - path_components = p4.Split(); - EXPECT_EQ(1, static_cast<int>(path_components.size())); - - Path p5("/"); - path_components = p5.Split(); - EXPECT_EQ(1, static_cast<int>(path_components.size())); +TEST(PathTest, OnePart_Relative) { + Path p("foo"); + EXPECT_FALSE(p.IsAbsolute()); + EXPECT_FALSE(p.IsRoot()); + EXPECT_EQ(1, p.Size()); + EXPECT_EQ("foo", p.Part(0)); + EXPECT_EQ("foo", p.Basename()); + EXPECT_EQ("foo", p.Join()); + EXPECT_EQ("foo", p.Range(0, 1)); + EXPECT_EQ("foo", p.Parent().Join()); } -TEST(PathTest, AppendAndJoin) { - Path ph1("/usr/local/hi/there"); - - EXPECT_EQ("/usr/local/hi/there", ph1.Join()); - ph1 = ph1.Append(".."); - EXPECT_EQ("/usr/local/hi", ph1.Join()); - ph1 = ph1.Append(".././././hi/there/../.././././"); - EXPECT_EQ("/usr/local", ph1.Join()); - ph1 = ph1.Append("../../../../../../../../././../"); - EXPECT_EQ("/", ph1.Join()); - ph1 = ph1.Append("usr/lib/../bin/.././etc/../local/../share"); - EXPECT_EQ("/usr/share", ph1.Join()); - - Path ph2("./"); - EXPECT_EQ(".", ph2.Join()); - - Path ph3("/"); - EXPECT_EQ("/", ph3.Join()); - ph3 = ph3.Append(""); - EXPECT_EQ("/", ph3.Join()); - ph3 = ph3.Append("USR/local/SHARE"); - EXPECT_EQ("/USR/local/SHARE", ph3.Join()); - - Path ph4(".."); - EXPECT_EQ("..", ph4.Join()); - ph4 = ph4.Append("node1/node3/../../node1/./"); - EXPECT_EQ("../node1", ph4.Join()); - ph4 = ph4.Append("node4/../../node1/./node5"); - EXPECT_EQ("../node1/node5", ph4.Join()); +TEST(PathTest, OnePart_Absolute) { + Path p("/foo"); + EXPECT_TRUE(p.IsAbsolute()); + EXPECT_FALSE(p.IsRoot()); + EXPECT_EQ(2, p.Size()); + EXPECT_EQ("/", p.Part(0)); + EXPECT_EQ("foo", p.Part(1)); + EXPECT_EQ("foo", p.Basename()); + EXPECT_EQ("/foo", p.Join()); + EXPECT_EQ("/", p.Range(0, 1)); + EXPECT_EQ("foo", p.Range(1, 2)); + EXPECT_EQ("/foo", p.Range(0, 2)); + EXPECT_EQ("/", p.Parent().Join()); } -TEST(PathTest, AppendAbsolute) { - Path path("/usr/local"); - path.Append("/foo"); - EXPECT_EQ("/foo", path.Join()); +TEST(PathTest, TwoPart_Relative) { + Path p("foo/bar"); + EXPECT_FALSE(p.IsAbsolute()); + EXPECT_FALSE(p.IsRoot()); + EXPECT_EQ(2, p.Size()); + EXPECT_EQ("foo", p.Part(0)); + EXPECT_EQ("bar", p.Part(1)); + EXPECT_EQ("bar", p.Basename()); + EXPECT_EQ("foo/bar", p.Join()); + EXPECT_EQ("foo", p.Range(0, 1)); + EXPECT_EQ("bar", p.Range(1, 2)); + EXPECT_EQ("foo/bar", p.Range(0, 2)); + EXPECT_EQ("foo", p.Parent().Join()); } -TEST(PathTest, Invalid) { - Path absolute("/usr/local"); - Path current("./usr/local"); - Path relative("usr/local"); +TEST(PathTest, MakeRelative) { + EXPECT_EQ("", Path("/").MakeRelative().Join()); + EXPECT_EQ("foo/bar/baz", Path("/foo/bar/baz").MakeRelative().Join()); + EXPECT_EQ("foo/bar/baz", Path("foo/bar/baz").MakeRelative().Join()); +} - Path test; +TEST(PathTest, Normalize_EmptyComponent) { + EXPECT_EQ("foo/bar", Path("foo//bar").Join()); + EXPECT_EQ("/blah", Path("//blah").Join()); + EXPECT_EQ("/a/b/c", Path("//a//b//c").Join()); + EXPECT_EQ("path/to/dir", Path("path/to/dir/").Join()); +} - test = absolute; - test.Append("../.."); - EXPECT_EQ("/", test.Join()); +TEST(PathTest, Normalize_Dot) { + EXPECT_EQ(".", Path(".").Join()); + EXPECT_EQ("foo", Path("foo/.").Join()); + EXPECT_EQ("foo/bar", Path("foo/./bar").Join()); + EXPECT_EQ("blah", Path("./blah").Join()); + EXPECT_EQ("stuff", Path("stuff/./.").Join()); + EXPECT_EQ("/", Path("/.").Join()); +} - test = absolute; - test.Append("../../.."); - EXPECT_EQ("/", test.Join()); +TEST(PathTest, Normalize_DotDot_Relative) { + EXPECT_EQ("..", Path("..").Join()); + EXPECT_EQ("../..", Path("../..").Join()); + EXPECT_EQ(".", Path("foo/..").Join()); + EXPECT_EQ("foo", Path("foo/bar/..").Join()); + EXPECT_EQ("bar", Path("foo/../bar").Join()); + EXPECT_EQ("foo/baz", Path("foo/bar/../baz").Join()); +} - test = absolute; - test.Append("../../../foo"); - EXPECT_EQ("/foo", test.Join()); +TEST(PathTest, Normalize_DotDot_Absolute) { + EXPECT_EQ("/", Path("/..").Join()); + EXPECT_EQ("/", Path("/../..").Join()); + EXPECT_EQ("/", Path("/foo/..").Join()); + EXPECT_EQ("/foo", Path("/foo/bar/..").Join()); + EXPECT_EQ("/bar", Path("/foo/../bar").Join()); + EXPECT_EQ("/foo/baz", Path("/foo/bar/../baz").Join()); +} + +TEST(PathTest, Append) { + EXPECT_EQ(".", Path("").Append(Path("")).Join()); + EXPECT_EQ("foo", Path("").Append(Path("foo")).Join()); + EXPECT_EQ(".", Path(".").Append(Path("")).Join()); + EXPECT_EQ("foo", Path(".").Append(Path("foo")).Join()); + EXPECT_EQ("foo/bar", Path(".").Append(Path("foo/bar")).Join()); + EXPECT_EQ("foo", Path("foo").Append(Path("")).Join()); + EXPECT_EQ("foo/bar", Path("foo").Append(Path("bar")).Join()); + EXPECT_EQ("foo/bar/quux", Path("foo").Append(Path("bar/quux")).Join()); + EXPECT_EQ("foo/and", Path("foo/and/more").Append(Path("..")).Join()); +} - test = current; - test.Append("../.."); - EXPECT_EQ(".", test.Join()); +TEST(PathTest, Append_Absolute) { + EXPECT_EQ("/", Path("").Append(Path("/")).Join()); + EXPECT_EQ("/hello/world", Path("").Append(Path("/hello/world")).Join()); + EXPECT_EQ("/", Path(".").Append(Path("/")).Join()); + EXPECT_EQ("/goodbye", Path(".").Append(Path("/goodbye")).Join()); + EXPECT_EQ("/foo/bar/baz", Path("/a/b").Append(Path("/foo/bar/baz")).Join()); +} - test = current; - test.Append("../../.."); - EXPECT_EQ("..", test.Join()); +TEST(PathTest, Append_Overflow) { + std::string big(PATH_MAX - 5, 'A'); + Path p(big.c_str()); + p.Append(Path("0123456789")); + + std::string part(p.Join()); + EXPECT_EQ(PATH_MAX - 1, part.size()); +} + +TEST(PathTest, Set) { + Path p("/random/path"); + EXPECT_EQ("something/else", p.Set("something/else").Join()); + // Set should change p, not just return a copy. + EXPECT_EQ("something/else", p.Join()); +} + +TEST(PathTest, Set_Overflow) { + std::string big(PATH_MAX * 2, 'A'); + Path p(big.c_str()); + EXPECT_EQ(PATH_MAX - 1, p.Part(0).size()); +} + +TEST(PathTest, Range_Empty) { + EXPECT_EQ("", Path("/").Range(1, 1)); +} - test = current; - test.Append("../../../foo"); - EXPECT_EQ("../foo", test.Join()); +TEST(PathTest, Range_Relative) { + Path p("a/relative/path"); - test = relative; - test.Append("../.."); - EXPECT_EQ(".", test.Join()); + EXPECT_EQ("a", p.Range(0, 1)); + EXPECT_EQ("a/relative", p.Range(0, 2)); + EXPECT_EQ("a/relative/path", p.Range(0, 3)); - test = relative; - test.Append("../../.."); - EXPECT_EQ("..", test.Join()); + EXPECT_EQ("relative", p.Range(1, 2)); + EXPECT_EQ("relative/path", p.Range(1, 3)); - test = relative; - test.Append("../../../foo"); - EXPECT_EQ("../foo", test.Join()); + EXPECT_EQ("path", p.Range(2, 3)); } -TEST(PathTest, Range) { +TEST(PathTest, Range_Absolute) { Path p("/an/absolute/path"); - // p's parts should be ["/", "an", "absolute", "path"]. + EXPECT_EQ("/", p.Range(0, 1)); + EXPECT_EQ("/an", p.Range(0, 2)); + EXPECT_EQ("/an/absolute", p.Range(0, 3)); EXPECT_EQ("/an/absolute/path", p.Range(0, 4)); + + EXPECT_EQ("an", p.Range(1, 2)); + EXPECT_EQ("an/absolute", p.Range(1, 3)); EXPECT_EQ("an/absolute/path", p.Range(1, 4)); + + EXPECT_EQ("absolute", p.Range(2, 3)); EXPECT_EQ("absolute/path", p.Range(2, 4)); + EXPECT_EQ("path", p.Range(3, 4)); +} - EXPECT_EQ("/an/absolute", p.Range(0, 3)); - EXPECT_EQ("an/absolute", p.Range(1, 3)); - EXPECT_EQ("absolute", p.Range(2, 3)); +TEST(PathTest, Assign) { + Path p; + + p = "foo/bar"; + EXPECT_EQ("foo/bar", p.Join()); + + // Should normalize. + p = "/foo/../bar"; + EXPECT_EQ("/bar", p.Join()); + + p = Path("hi/planet"); + EXPECT_EQ("hi/planet", p.Join()); +} + +TEST(PathTest, Equals) { + EXPECT_TRUE(Path("/foo") == Path("/foo")); + EXPECT_TRUE(Path("foo/../bar") == Path("bar")); + EXPECT_TRUE(Path("one/path") != Path("another/path")); } + |