diff options
author | noelallen@google.com <noelallen@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-05-04 21:04:22 +0000 |
---|---|---|
committer | noelallen@google.com <noelallen@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-05-04 21:04:22 +0000 |
commit | e9e9c6d4044854227199be9fe0d6816846f4c343 (patch) | |
tree | 7b857840d4d12cf40ed5136976e4679290f593a0 /native_client_sdk/src/libraries | |
parent | 811f189fc18bc5cdf545a8af0faf422db6fc1a92 (diff) | |
download | chromium_src-e9e9c6d4044854227199be9fe0d6816846f4c343.zip chromium_src-e9e9c6d4044854227199be9fe0d6816846f4c343.tar.gz chromium_src-e9e9c6d4044854227199be9fe0d6816846f4c343.tar.bz2 |
Add mount sources for a memory mounts
BUG=122229
R=binji@chromium.org
Review URL: https://chromiumcodereview.appspot.com/10352019
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@135428 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'native_client_sdk/src/libraries')
8 files changed, 816 insertions, 9 deletions
diff --git a/native_client_sdk/src/libraries/nacl_mounts/mount.cc b/native_client_sdk/src/libraries/nacl_mounts/mount.cc new file mode 100644 index 0000000..ad8eeae --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_mounts/mount.cc @@ -0,0 +1,54 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <string> + +#include "auto_lock.h" +#include "ref_object.h" + +#include "nacl_mounts/mount.h" +#include "nacl_mounts/mount_node.h" +#include "nacl_mounts/mount_node_dir.h" +#include "nacl_mounts/mount_node_mem.h" +#include "nacl_mounts/path.h" + + +Mount::Mount() + : dev_(0) { +} + +Mount::~Mount() {} + +bool Mount::Init(int dev, StringMap_t& args) { + dev_ = dev; + return true; +} + +void Mount::Destroy() {} + +void Mount::AcquireNode(MountNode* node) { + AutoLock lock(&lock_); + node->Acquire(); +} + +void Mount::ReleaseNode(MountNode* node) { + AutoLock lock(&lock_); + node->Release(); +} + +int Mount::OpenModeToPermission(int mode) { + int out; + switch (mode & 3) { + case O_RDONLY: out = S_IREAD; + case O_WRONLY: out = S_IWRITE; + case O_RDWR: out = S_IREAD | S_IWRITE; + } + return out; +} + + diff --git a/native_client_sdk/src/libraries/nacl_mounts/mount.h b/native_client_sdk/src/libraries/nacl_mounts/mount.h new file mode 100644 index 0000000..866a829 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_mounts/mount.h @@ -0,0 +1,105 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef LIBRARIES_NACL_MOUNTS_MOUNT_H_ +#define LIBRARIES_NACL_MOUNTS_MOUNT_H_ + +#include <stdint.h> +#include <map> +#include <string> + +#include "macros.h" +#include "ref_object.h" + +#include "nacl_mounts/mount_node.h" +#include "nacl_mounts/path.h" + +struct dirent; +struct stat; + +class MountNode; +class MountManager; + +typedef std::map<std::string, std::string> StringMap_t; + +template<class C, class P> class MountFactory : public P { + protected: + MountFactory() + : P() {} + + static Mount* Create(int dev, StringMap_t& args) { + Mount* mnt = new C(); + if (mnt->Init(dev, args) == false) { + delete mnt; + return NULL; + } + return mnt; + } + + friend class KernelProxy; +}; + + + +// Mount serves as the base mounting class that will be used by +// the mount manager (class MountManager). The mount manager +// relies heavily on the GetNode method as a way of directing +// system calls that take a path as an argument. The methods +// of this class are pure virtual. BaseMount class contains +// stub implementations for these methods. Feel free to use +// BaseMount if your mount does not implement all of these +// operations. +class Mount : public RefObject { + protected: + // The protected functions are only used internally and will not + // acquire or release the mount's lock. + Mount(); + virtual ~Mount(); + + // Init must be called by the factory before the mount is used. + // This function must assign a root node, or replace FindNode. + virtual bool Init(int dev, StringMap_t& args); + + // Destroy is called when the reference count reaches zero, + // just before the destructor is called. + virtual void Destroy(); + + public: + // All paths are expected to containing a leading "/" + virtual void AcquireNode(MountNode* node); + virtual void ReleaseNode(MountNode* node); + + // Open and Close will affect the RefCount on the node, so + // there must be a call to close for each call to open. + virtual MountNode *Open(const Path& path, int mode) = 0; + virtual int Close(MountNode* node) = 0; + + // Unlink, Mkdir, Rmdir will affect the both the RefCount + // and the nlink number in the stat object. + virtual int Unlink(const Path& path) = 0; + virtual int Mkdir(const Path& path, int permissions) = 0; + virtual int Rmdir(const Path& path) = 0; + + // Convert from R,W,R/W open flags to STAT permission flags + static int OpenModeToPermission(int mode); + + protected: + // Device number for the mount. + int dev_; + + private: + // May only be called by the KernelProxy when the Kernel's + // lock is held, so we make it private. + friend class KernelObject; + friend class KernelProxy; + void Acquire() { RefObject::Acquire(); } + bool Release() { return RefObject::Release(); } + + template <class M, class P> friend class MountFactory; + DISALLOW_COPY_AND_ASSIGN(Mount); +}; + + +#endif // LIBRARIES_NACL_MOUNTS_MOUNT_H_ + diff --git a/native_client_sdk/src/libraries/nacl_mounts/mount_node_mem.cc b/native_client_sdk/src/libraries/nacl_mounts/mount_node_mem.cc index 4a59119..4d92692 100644 --- a/native_client_sdk/src/libraries/nacl_mounts/mount_node_mem.cc +++ b/native_client_sdk/src/libraries/nacl_mounts/mount_node_mem.cc @@ -12,10 +12,10 @@ #define BLOCK_SIZE (1 << 16) #define BLOCK_MASK (BLOCK_SIZE - 1) -MountNodeMem::MountNodeMem(Mount *mount, int ino, int dev) : - MountNode(mount, ino, dev), - data_(NULL), - capacity_(0) { +MountNodeMem::MountNodeMem(Mount *mount, int ino, int dev) + : MountNode(mount, ino, dev), + data_(NULL), + capacity_(0) { } MountNodeMem::~MountNodeMem() { @@ -35,7 +35,7 @@ int MountNodeMem::Read(size_t offs, void *buf, size_t count) { count = GetSize() - offs; } - memcpy(buf, data_, count); + memcpy(buf, &data_[offs], count); return static_cast<int>(count); } @@ -49,7 +49,7 @@ int MountNodeMem::Write(size_t offs, const void *buf, size_t count) { count = GetSize() - offs; } - memcpy(data_, buf, count); + memcpy(&data_[offs], buf, count); return static_cast<int>(count); } diff --git a/native_client_sdk/src/libraries/nacl_mounts/path.cc b/native_client_sdk/src/libraries/nacl_mounts/path.cc new file mode 100644 index 0000000..363a4e3 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_mounts/path.cc @@ -0,0 +1,199 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <stdio.h> +#include <string.h> +#include <string> + +#include "nacl_mounts/path.h" + +Path::Path() {} + +Path::Path(const Path& path) { + paths_ = path.paths_; +} + +Path::Path(const std::string& path) { + Set(path); +} + +Path::~Path() {} + +bool Path::IsAbsolute() const { + return !paths_.empty() && paths_[0] == "/"; +} + +const std::string& Path::Part(size_t index) const { + return paths_[index]; +} + +size_t Path::Size() const { + return paths_.size(); +} + +Path& Path::Append(const std::string& path) { + StringArray_t paths = Split(path); + + for (size_t index = 0; index < paths.size(); index++) { + // Skip ROOT + if (paths_.size() && index == 0 && paths[0] == "/") continue; + paths_.push_back(paths[index]); + } + + paths_ = Normalize(paths_); + return *this; +} + +Path& Path::Prepend(const std::string& path) { + StringArray_t paths = Split(path); + + for (size_t index = 0; index < paths_.size(); index++) { + // Skip ROOT + if (index == 0 && paths_[0] == "/") continue; + paths.push_back(paths[index]); + } + paths_ = Normalize(paths); + return *this; +} + +Path& Path::Set(const std::string& path) { + StringArray_t paths = Split(path); + paths_ = Normalize(paths); + return *this; +} + +Path Path::Parent() const { + Path out; + out.paths_ = paths_; + if (out.paths_.size()) out.paths_.pop_back(); + return out; +} + +std::string Path::Basename() const { + if (paths_.size()) return paths_.back(); + return std::string(); +} + +std::string Path::Join() const { + return Range(paths_, 0, paths_.size()); +} + +std::string Path::Range(size_t start, size_t end) const { + return Range(paths_, start, end); +} + +StringArray_t Path::Split() const { + return paths_; +} + +// static +StringArray_t Path::Normalize(const StringArray_t& paths) { + StringArray_t path_out; + int back = 0; + + 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; + + // Check for '.' in the path and remove it + if (curr == ".") continue; + + // 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 the path is at root, "/.." = "/" + if (path_out.back() == "/") { + continue; + } + + // if we are already at root, then stay there (root/.. -> root) + if (path_out.back() == "/") { + continue; + } + + // 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.size() == 0) path_out.push_back("."); + + return path_out; +} + +// static +std::string Path::Join(const StringArray_t& paths) { + return Range(paths, 0, paths.size()); +} + +// 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(); + + if (start == 0 && end > 0 && paths[0] == "/") { + if (end == 1) return std::string("/"); + index++; + } + + for (; index < end; index++) { + if (index) out_path += "/"; + out_path += paths[index]; + } + + return out_path; +} + +// static +StringArray_t Path::Split(const std::string& path) { + StringArray_t components; + size_t offs = 0; + size_t next = 0; + ssize_t cnt = 0; + + if (path[0] == '/') { + offs = 1; + components.push_back("/"); + } + + while (next != std::string::npos) { + next = path.find('/', offs); + + // Remove extra seperators + if (next == offs) { + ++offs; + continue; + } + + std::string part = path.substr(offs, next - offs); + if (!part.empty()) components.push_back(part); + offs = next + 1; + } + return components; +} + +Path& Path::operator =(const Path& p) { + paths_ = p.paths_; + return *this; +} + +Path& Path::operator =(const std::string& p) { + return Set(p); +}
\ No newline at end of file diff --git a/native_client_sdk/src/libraries/nacl_mounts/path.h b/native_client_sdk/src/libraries/nacl_mounts/path.h new file mode 100644 index 0000000..ed3d0ee --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_mounts/path.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2011 The Native Client Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef PACKAGES_LIBRARIES_NACL_MOUNTS_PATH_H_ +#define PACKAGES_LIBRARIES_NACL_MOUNTS_PATH_H_ + +#include <stdint.h> +#include <string> +#include <vector> + +#include "macros.h" + +typedef std::vector<std::string> StringArray_t; + +class Path { + public: + 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); + ~Path(); + + + // Return true of the first path item is '/'. + bool IsAbsolute() const; + + // Return a part of the path + const std::string& Part(size_t index) const; + + // Return the number of path parts/ + size_t Size() const; + + // Update the path. + Path& Append(const std::string& path); + Path& Prepend(const std::string& path); + Path& Set(const std::string& path); + + // Return the parent path. + Path Parent() const; + std::string Basename() const; + + 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); + + private: + // Internal representation of the path stored an array of string representing + // the directory traversal. The first string is a "/" if this is an abolute + // path. + StringArray_t paths_; +}; + +#endif // PACKAGES_LIBRARIES_NACL_MOUNTS_PATH_H_ + diff --git a/native_client_sdk/src/libraries/nacl_mounts_test/mount_node_test.cc b/native_client_sdk/src/libraries/nacl_mounts_test/mount_node_test.cc index 2f213d2..e38b1fe 100644 --- a/native_client_sdk/src/libraries/nacl_mounts_test/mount_node_test.cc +++ b/native_client_sdk/src/libraries/nacl_mounts_test/mount_node_test.cc @@ -4,11 +4,15 @@ */ #include <errno.h> +#include <fcntl.h> +#include <unistd.h> -#include "../nacl_mounts/mount_node.h" -#include "../nacl_mounts/mount_node_dir.h" -#include "../nacl_mounts/mount_node_mem.h" +#include "nacl_mounts/kernel_proxy.h" +#include "nacl_mounts/mount_node.h" +#include "nacl_mounts/mount_node_dir.h" +#include "nacl_mounts/mount_node_mem.h" +#define __STDC__ 1 #include "gtest/gtest.h" #define NULL_NODE ((MountNode *) NULL) diff --git a/native_client_sdk/src/libraries/nacl_mounts_test/mount_test.cc b/native_client_sdk/src/libraries/nacl_mounts_test/mount_test.cc new file mode 100644 index 0000000..c5f2537 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_mounts_test/mount_test.cc @@ -0,0 +1,129 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/stat.h> + +#include <string> + +#include "nacl_mounts/mount.h" +#include "nacl_mounts/mount_mem.h" + +#define __STDC__ 1 +#include "gtest/gtest.h" + +class MountMock : public MountMem { + public: + MountMock() + : MountMem(), + nodes_(0) { + StringMap_t map; + Init(1, map); + }; + + MountNode *AllocateData(int mode) { + nodes_++; + return MountMem::AllocateData(mode); + } + + void ReleaseNode(MountNode* node) { + if (!node->Release()) { + nodes_--; + } + } + + int nodes_; +}; + +#define NULL_NODE ((MountNode *) NULL) + +TEST(MountTest, Sanity) { + MountMock* mnt = new MountMock(); + MountNode* file; + MountNode* root; + + char buf1[1024]; + + EXPECT_EQ(0, mnt->nodes_); + + // Fail to open non existant file + EXPECT_EQ(NULL_NODE, mnt->Open(Path("/foo"), O_RDWR)); + EXPECT_EQ(errno, ENOENT); + + // Create a file + file = mnt->Open(Path("/foo"), O_RDWR | O_CREAT); + EXPECT_NE(NULL_NODE, file); + if (file == NULL) return; + EXPECT_EQ(2, file->RefCount()); + EXPECT_EQ(1, mnt->nodes_); + + // Open the root directory + root = mnt->Open(Path("/"), O_RDWR); + EXPECT_NE(NULL_NODE, root); + if (NULL != root) { + struct dirent dirs[2]; + int len = root->GetDents(0, dirs, sizeof(dirs)); + EXPECT_EQ(sizeof(struct dirent), len); + } + + // Fail to re-create the same file + EXPECT_EQ(NULL_NODE, mnt->Open(Path("/foo"), O_RDWR | O_CREAT | O_EXCL)); + EXPECT_EQ(errno, EEXIST); + EXPECT_EQ(1, mnt->nodes_); + + // Fail to create a directory with the same name + EXPECT_EQ(-1, mnt->Mkdir(Path("/foo"), O_RDWR)); + EXPECT_EQ(errno, EEXIST); + + // Attempt to READ/WRITE + EXPECT_EQ(0, file->GetSize()); + EXPECT_EQ(sizeof(buf1), file->Write(0, buf1, sizeof(buf1))); + EXPECT_EQ(sizeof(buf1), file->GetSize()); + EXPECT_EQ(sizeof(buf1), file->Read(0, buf1, sizeof(buf1))); + + // Attempt to open the same file + EXPECT_EQ(file, mnt->Open(Path("/foo"), O_RDWR | O_CREAT)); + EXPECT_EQ(sizeof(buf1), file->GetSize()); + EXPECT_EQ(3, file->RefCount()); + EXPECT_EQ(1, mnt->nodes_); + + // Attempt to close and delete the file + EXPECT_EQ(0, mnt->Close(file)); + EXPECT_EQ(1, mnt->nodes_); + EXPECT_EQ(0, mnt->Unlink(Path("/foo"))); + EXPECT_EQ(1, mnt->nodes_); + EXPECT_EQ(-1, mnt->Unlink(Path("/foo"))); + EXPECT_EQ(1, mnt->nodes_); + EXPECT_EQ(errno, ENOENT); + EXPECT_EQ(0, mnt->Close(file)); + EXPECT_EQ(0, mnt->nodes_); + + // Recreate foo as a directory + EXPECT_EQ(0, mnt->Mkdir(Path("/foo"), O_RDWR)); + + // Create a file (exclusively) + file = mnt->Open(Path("/foo/bar"), O_RDWR | O_CREAT | O_EXCL); + EXPECT_NE(NULL_NODE, file); + if (NULL == file) return; + + // Attempt to delete the directory + EXPECT_EQ(-1, mnt->Rmdir(Path("/foo"))); + EXPECT_EQ(errno, ENOTEMPTY); + + // Unlink the file, then delete the directory + EXPECT_EQ(0, mnt->Unlink(Path("/foo/bar"))); + EXPECT_EQ(0, mnt->Rmdir(Path("/foo"))); + EXPECT_EQ(1, mnt->nodes_); + EXPECT_EQ(0, mnt->Close(file)); + EXPECT_EQ(0, mnt->nodes_); + + // Verify the directory is gone + file = mnt->Open(Path("/foo"), O_RDWR); + EXPECT_EQ(NULL_NODE, file); + EXPECT_EQ(errno, ENOENT); +} diff --git a/native_client_sdk/src/libraries/nacl_mounts_test/path_test.cc b/native_client_sdk/src/libraries/nacl_mounts_test/path_test.cc new file mode 100644 index 0000000..4e3caaa --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_mounts_test/path_test.cc @@ -0,0 +1,246 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <fcntl.h> +#include <unistd.h> +#include "nacl_mounts/kernel_proxy.h" +#include "nacl_mounts/path.h" + +#define __STDC__ 1 +#include "gtest/gtest.h" + +TEST(PathTest, SanityChecks) { + // can we construct and delete? + Path ph1("."); + Path *ph2 = new Path("."); + delete ph2; + + Path p1("."); + EXPECT_EQ(false, p1.IsAbsolute()); + EXPECT_EQ(".", p1.Join()); + Path p2("/"); + EXPECT_EQ(true, p2.IsAbsolute()); + EXPECT_EQ("/", p2.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_EQ(false, empty.IsAbsolute()); + EXPECT_EQ(std::string(""), empty.Join()); + + EXPECT_EQ(1, dot.Size()); + EXPECT_EQ(false, dot.IsAbsolute()); + EXPECT_EQ(std::string("."), dot.Join()); + + EXPECT_EQ(1, root.Size()); + EXPECT_EQ(true, root.IsAbsolute()); + EXPECT_EQ(std::string("/"), root.Join()); + + EXPECT_EQ(4, abs_str.Size()); + EXPECT_EQ(true, abs_str.IsAbsolute()); + EXPECT_EQ(std::string("/abs/from/string"), abs_str.Join()); + + EXPECT_EQ(3, rel_str.Size()); + EXPECT_EQ(false, rel_str.IsAbsolute()); + EXPECT_EQ(std::string("rel/from/string"), rel_str.Join()); + + EXPECT_EQ(3, self_str.Size()); + EXPECT_EQ(false, self_str.IsAbsolute()); + EXPECT_EQ(std::string("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_EQ(false, empty.IsAbsolute()); + EXPECT_EQ(std::string("."), empty.Join()); + + EXPECT_EQ(1, dot.Size()); + EXPECT_EQ(false, dot.IsAbsolute()); + EXPECT_EQ(std::string("."), dot.Join()); + + EXPECT_EQ(1, root.Size()); + EXPECT_EQ(true, root.IsAbsolute()); + EXPECT_EQ(std::string("/"), root.Join()); + + EXPECT_EQ(4, abs_str.Size()); + EXPECT_EQ(true, abs_str.IsAbsolute()); + EXPECT_EQ(std::string("/abs/from/assign"), abs_str.Join()); + + EXPECT_EQ(3, rel_str.Size()); + EXPECT_EQ(false, rel_str.IsAbsolute()); + EXPECT_EQ(std::string("rel/from/assign"), rel_str.Join()); + + EXPECT_EQ(3, self_str.Size()); + EXPECT_EQ(false, self_str.IsAbsolute()); + EXPECT_EQ(std::string("rel/from/assign"), self_str.Join()); + + Path cpy_str; + cpy_str = empty; + EXPECT_EQ(1, cpy_str.Size()); + EXPECT_EQ(false, cpy_str.IsAbsolute()); + EXPECT_EQ(std::string("."), cpy_str.Join()); + + cpy_str = dot; + EXPECT_EQ(1, cpy_str.Size()); + EXPECT_EQ(false, cpy_str.IsAbsolute()); + EXPECT_EQ(std::string("."), cpy_str.Join()); + + cpy_str = root; + EXPECT_EQ(1, cpy_str.Size()); + EXPECT_EQ(true, cpy_str.IsAbsolute()); + EXPECT_EQ(std::string("/"), cpy_str.Join()); + + cpy_str = abs_str; + EXPECT_EQ(4, cpy_str.Size()); + EXPECT_EQ(true, cpy_str.IsAbsolute()); + EXPECT_EQ(std::string("/abs/from/assign"), cpy_str.Join()); + + cpy_str = rel_str; + EXPECT_EQ(3, cpy_str.Size()); + EXPECT_EQ(false, cpy_str.IsAbsolute()); + EXPECT_EQ(std::string("rel/from/assign"), cpy_str.Join()); + + cpy_str = self_str; + EXPECT_EQ(3, cpy_str.Size()); + EXPECT_EQ(false, cpy_str.IsAbsolute()); + EXPECT_EQ(std::string("rel/from/assign"), cpy_str.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_EQ(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, 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()); + ph3 = ph3.Append("///////////////////////////////"); + 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, Invalid) { + Path rooted("/usr/local"); + Path current("./usr/local"); + Path relative("usr/local"); + + Path test; + + test = rooted; + test.Append("../.."); + EXPECT_EQ("/", test.Join()); + + test = rooted; + test.Append("../../.."); + EXPECT_EQ("/", test.Join()); + + test = rooted; + test.Append("../../../foo"); + EXPECT_EQ("/foo", test.Join()); + + test = current; + test.Append("../.."); + EXPECT_EQ(".", test.Join()); + + test = current; + test.Append("../../.."); + EXPECT_EQ("..", test.Join()); + + test = current; + test.Append("../../../foo"); + EXPECT_EQ("../foo", test.Join()); + + test = relative; + test.Append("../.."); + EXPECT_EQ(".", test.Join()); + + test = relative; + test.Append("../../.."); + EXPECT_EQ("..", test.Join()); + + test = relative; + test.Append("../../../foo"); + EXPECT_EQ("../foo", test.Join()); +} |