summaryrefslogtreecommitdiffstats
path: root/native_client_sdk
diff options
context:
space:
mode:
authornoelallen@google.com <noelallen@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2012-05-04 21:04:22 +0000
committernoelallen@google.com <noelallen@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2012-05-04 21:04:22 +0000
commite9e9c6d4044854227199be9fe0d6816846f4c343 (patch)
tree7b857840d4d12cf40ed5136976e4679290f593a0 /native_client_sdk
parent811f189fc18bc5cdf545a8af0faf422db6fc1a92 (diff)
downloadchromium_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')
-rw-r--r--native_client_sdk/src/libraries/nacl_mounts/mount.cc54
-rw-r--r--native_client_sdk/src/libraries/nacl_mounts/mount.h105
-rw-r--r--native_client_sdk/src/libraries/nacl_mounts/mount_node_mem.cc12
-rw-r--r--native_client_sdk/src/libraries/nacl_mounts/path.cc199
-rw-r--r--native_client_sdk/src/libraries/nacl_mounts/path.h70
-rw-r--r--native_client_sdk/src/libraries/nacl_mounts_test/mount_node_test.cc10
-rw-r--r--native_client_sdk/src/libraries/nacl_mounts_test/mount_test.cc129
-rw-r--r--native_client_sdk/src/libraries/nacl_mounts_test/path_test.cc246
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());
+}