mesos-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ka...@apache.org
Subject [14/50] [abbrv] mesos git commit: Moved stout from libprocess/3rdparty/ to 3rdparty/.
Date Wed, 11 May 2016 22:24:36 GMT
http://git-wip-us.apache.org/repos/asf/mesos/blob/c235f7c1/3rdparty/stout/tests/os/process_tests.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/stout/tests/os/process_tests.cpp b/3rdparty/stout/tests/os/process_tests.cpp
new file mode 100644
index 0000000..c945923
--- /dev/null
+++ b/3rdparty/stout/tests/os/process_tests.cpp
@@ -0,0 +1,286 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+#ifdef __WINDOWS__
+#include <process.h>
+#endif // __WINDOWS__
+
+#include <set>
+
+#include <gtest/gtest.h>
+
+#include <stout/os.hpp>
+
+#ifndef __WINDOWS__
+#include <stout/os/fork.hpp>
+#endif // __WINDOWS__
+#include <stout/os/pstree.hpp>
+
+#include <stout/tests/utils.hpp>
+
+
+class ProcessTest : public TemporaryDirectoryTest {};
+
+#ifndef __WINDOWS__
+using os::Exec;
+using os::Fork;
+#endif // __WINDOWS__
+using os::Process;
+using os::ProcessTree;
+
+using std::list;
+using std::set;
+using std::string;
+
+
+const unsigned int init_pid =
+#ifdef __WINDOWS__
+    0;
+#else
+    1;
+#endif // __WINDOWS__
+
+
+#ifdef __WINDOWS__
+int getppid()
+{
+  const int pid = getpid();
+  HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+  std::shared_ptr<void> sh(h, CloseHandle);
+
+  PROCESSENTRY32 pe = { 0 };
+  pe.dwSize = sizeof(PROCESSENTRY32);
+
+  if (Process32First(h, &pe)) {
+    do {
+      if (pe.th32ProcessID == pid) {
+        return pe.th32ParentProcessID;
+      }
+    } while (Process32Next(h, &pe));
+  }
+}
+#endif // __WINDOWS__
+
+TEST_F(ProcessTest, Process)
+{
+  const Result<Process> process = os::process(getpid());
+
+  ASSERT_SOME(process);
+  EXPECT_EQ(getpid(), process.get().pid);
+  EXPECT_EQ(getppid(), process.get().parent);
+  ASSERT_SOME(process.get().session);
+
+#ifndef __WINDOWS__
+  // NOTE: `getsid` does not have a meaningful interpretation on Windows.
+  EXPECT_EQ(getsid(getpid()), process.get().session.get());
+#endif // __WINDOWS__
+
+  ASSERT_SOME(process.get().rss);
+  EXPECT_GT(process.get().rss.get(), 0);
+
+  // NOTE: On Linux /proc is a bit slow to update the CPU times,
+  // hence we allow 0 in this test.
+  ASSERT_SOME(process.get().utime);
+  EXPECT_GE(process.get().utime.get(), Nanoseconds(0));
+  ASSERT_SOME(process.get().stime);
+  EXPECT_GE(process.get().stime.get(), Nanoseconds(0));
+
+  EXPECT_FALSE(process.get().command.empty());
+
+  // Assert invalid PID returns `None`.
+  Result<Process> invalid_process = os::process(-1);
+  EXPECT_NONE(invalid_process);
+
+  // Assert init.
+  Result<Process> init_process = os::process(init_pid);
+#ifdef __WINDOWS__
+  // NOTE: On Windows, inspecting other processes usually requires privileges.
+  // So we expect it to error out instead of succeed, unlike the POSIX version.
+  EXPECT_ERROR(init_process);
+#else
+  EXPECT_SOME(init_process);
+#endif // __WINDOWS__
+}
+
+
+TEST_F(ProcessTest, Processes)
+{
+  const Try<list<Process>> processes = os::processes();
+
+  ASSERT_SOME(processes);
+  ASSERT_GT(processes.get().size(), 2);
+
+  // Look for ourselves in the table.
+  bool found = false;
+  foreach (const Process& process, processes.get()) {
+    if (process.pid == getpid()) {
+      found = true;
+      EXPECT_EQ(getpid(), process.pid);
+      EXPECT_EQ(getppid(), process.parent);
+      ASSERT_SOME(process.session);
+
+#ifndef __WINDOWS__
+      // NOTE: `getsid` does not have a meaningful interpretation on Windows.
+      EXPECT_EQ(getsid(getpid()), process.session.get());
+#endif // __WINDOWS__
+
+      ASSERT_SOME(process.rss);
+      EXPECT_GT(process.rss.get(), 0);
+
+      // NOTE: On linux /proc is a bit slow to update the cpu times,
+      // hence we allow 0 in this test.
+      ASSERT_SOME(process.utime);
+      EXPECT_GE(process.utime.get(), Nanoseconds(0));
+      ASSERT_SOME(process.stime);
+      EXPECT_GE(process.stime.get(), Nanoseconds(0));
+
+      EXPECT_FALSE(process.command.empty());
+
+      break;
+    }
+  }
+
+  EXPECT_TRUE(found);
+}
+
+
+TEST_F(ProcessTest, Pids)
+{
+  Try<set<pid_t> > pids = os::pids();
+  ASSERT_SOME(pids);
+  EXPECT_NE(0u, pids.get().size());
+  EXPECT_EQ(1u, pids.get().count(getpid()));
+
+  // In a FreeBSD jail, pid 1 may not exist.
+#ifdef __FreeBSD__
+  if (!isJailed()) {
+#endif
+    EXPECT_EQ(1u, pids.get().count(init_pid));
+#ifdef __FreeBSD__
+  }
+#endif
+
+#ifndef __WINDOWS__
+  // NOTE: `getpgid` does not have a meaningful interpretation on Windows.
+  pids = os::pids(getpgid(0), None());
+  EXPECT_SOME(pids);
+  EXPECT_GE(pids.get().size(), 1u);
+  EXPECT_EQ(1u, pids.get().count(getpid()));
+
+  // NOTE: This test is not meaningful on Windows because process IDs are
+  // expected to be non-negative.
+  EXPECT_ERROR(os::pids(-1, None()));
+
+  // NOTE: `getsid` does not have a meaningful interpretation on Windows.
+  pids = os::pids(None(), getsid(0));
+  EXPECT_SOME(pids);
+  EXPECT_GE(pids.get().size(), 1u);
+  EXPECT_EQ(1u, pids.get().count(getpid()));
+
+  // NOTE: This test is not meaningful on Windows because process IDs are
+  // expected to be non-negative.
+  EXPECT_ERROR(os::pids(None(), -1));
+#endif // __WINDOWS__
+}
+
+
+#ifdef __WINDOWS__
+TEST_F(ProcessTest, Pstree)
+{
+  Try<ProcessTree> tree = os::pstree(getpid());
+  ASSERT_SOME(tree);
+
+  // Windows spawns `conhost.exe` if we're running from VS, so the count of
+  // children could be 0 or 1.
+  const size_t total_children = tree.get().children.size();
+  EXPECT_TRUE(0u == total_children ||
+              1u == total_children) << stringify(tree.get());
+  const bool conhost_spawned = total_children == 1;
+
+  // Windows has no `sleep` command, so we fake it with `ping`.
+  const string command = "ping 127.0.0.1 -n 2";
+
+  STARTUPINFO si;
+  PROCESS_INFORMATION pi;
+  ZeroMemory(&si, sizeof(si));
+  si.cb = sizeof(si);
+  ZeroMemory(&pi, sizeof(pi));
+
+  // Create new process that "sleeps".
+  BOOL created = CreateProcess(
+      NULL,                   // No module name (use command line).
+      (LPSTR)command.c_str(),
+      NULL,                   // Process handle not inheritable.
+      NULL,                   // Thread handle not inheritable.
+      FALSE,                  // Set handle inheritance to FALSE.
+      0,                      // No creation flags.
+      NULL,                   // Use parent's environment block.
+      NULL,                   // Use parent's starting directory.
+      &si,
+      &pi);
+  ASSERT_TRUE(created);
+
+  Try<ProcessTree> tree_after_spawn = os::pstree(getpid());
+  ASSERT_SOME(tree_after_spawn);
+
+  // Windows spawns conhost.exe if we're running from VS, so the count of
+  // children could be 0 or 1.
+  const size_t children_after_span = tree_after_spawn.get().children.size();
+  EXPECT_TRUE((!conhost_spawned && 1u == children_after_span) ||
+              (conhost_spawned && 2u == children_after_span)
+              ) << stringify(tree_after_spawn.get());
+
+  WaitForSingleObject(pi.hProcess, INFINITE);
+}
+#else
+TEST_F(ProcessTest, Pstree)
+{
+  Try<ProcessTree> tree = os::pstree(getpid());
+
+  ASSERT_SOME(tree);
+  EXPECT_EQ(0u, tree.get().children.size()) << stringify(tree.get());
+
+  tree =
+    Fork(None(),                   // Child.
+      Fork(Exec("sleep 10")),   // Grandchild.
+      Exec("sleep 10"))();
+
+  ASSERT_SOME(tree);
+
+  // Depending on whether or not the shell has fork/exec'ed,
+  // we could have 1 or 2 direct children. That is, some shells
+  // might simply exec the command above (i.e., 'sleep 10') while
+  // others might fork/exec the command, keeping around a 'sh -c'
+  // process as well.
+  ASSERT_LE(1u, tree.get().children.size());
+  ASSERT_GE(2u, tree.get().children.size());
+
+  pid_t child = tree.get().process.pid;
+  pid_t grandchild = tree.get().children.front().process.pid;
+
+  // Now check pstree again.
+  tree = os::pstree(child);
+
+  ASSERT_SOME(tree);
+  EXPECT_EQ(child, tree.get().process.pid);
+
+  ASSERT_LE(1u, tree.get().children.size());
+  ASSERT_GE(2u, tree.get().children.size());
+
+  // Cleanup by killing the descendant processes.
+  EXPECT_EQ(0, kill(grandchild, SIGKILL));
+  EXPECT_EQ(0, kill(child, SIGKILL));
+
+  // We have to reap the child for running the tests in repetition.
+  ASSERT_EQ(child, waitpid(child, NULL, 0));
+}
+#endif // __WINDOWS__

http://git-wip-us.apache.org/repos/asf/mesos/blob/c235f7c1/3rdparty/stout/tests/os/rmdir_tests.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/stout/tests/os/rmdir_tests.cpp b/3rdparty/stout/tests/os/rmdir_tests.cpp
new file mode 100644
index 0000000..a11bfc9
--- /dev/null
+++ b/3rdparty/stout/tests/os/rmdir_tests.cpp
@@ -0,0 +1,303 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+#include <list>
+#include <map>
+#include <set>
+#include <string>
+
+#include <stout/fs.hpp>
+#include <stout/os.hpp>
+#include <stout/path.hpp>
+
+#include <stout/os/getcwd.hpp>
+#include <stout/os/ls.hpp>
+#include <stout/os/mkdir.hpp>
+#include <stout/os/stat.hpp>
+#include <stout/os/touch.hpp>
+
+#include <stout/tests/utils.hpp>
+
+using std::list;
+using std::set;
+using std::string;
+
+
+static hashset<string> listfiles(const string& directory)
+{
+  hashset<string> fileset;
+  Try<list<string>> entries = os::ls(directory);
+  if (entries.isSome()) {
+    foreach (const string& entry, entries.get()) {
+      fileset.insert(entry);
+    }
+  }
+  return fileset;
+}
+
+
+class RmdirTest : public TemporaryDirectoryTest {};
+
+
+// TODO(hausdorff): This test is almost copy-pasted from
+// `TrivialRemoveEmptyDirectoryRelativePath`; we should parameterize them to
+// reduce redundancy.
+TEST_F(RmdirTest, TrivialRemoveEmptyDirectoryAbsolutePath)
+{
+  const string tmpdir = os::getcwd();
+  hashset<string> expectedListing = hashset<string>::EMPTY;
+
+  // Directory is initially empty.
+  EXPECT_EQ(expectedListing, listfiles(tmpdir));
+
+  // Successfully make directory using absolute path.
+  const string newDirectoryName = "newDirectory";
+  const string newDirectoryAbsolutePath = path::join(tmpdir, newDirectoryName);
+  expectedListing.insert(newDirectoryName);
+  EXPECT_SOME(os::mkdir(newDirectoryAbsolutePath));
+  EXPECT_EQ(expectedListing, listfiles(tmpdir));
+  EXPECT_EQ(hashset<string>::EMPTY, listfiles(newDirectoryAbsolutePath));
+
+  // Successfully remove.
+  EXPECT_SOME(os::rmdir(newDirectoryAbsolutePath));
+  EXPECT_EQ(hashset<string>::EMPTY, listfiles(tmpdir));
+}
+
+
+TEST_F(RmdirTest, TrivialRemoveEmptyDirectoryRelativePath)
+{
+  const string tmpdir = os::getcwd();
+  hashset<string> expectedListing = hashset<string>::EMPTY;
+
+  // Directory is initially empty.
+  EXPECT_EQ(expectedListing, listfiles(tmpdir));
+
+  // Successfully make directory using relative path.
+  const string newDirectoryName = "newDirectory";
+  expectedListing.insert(newDirectoryName);
+  EXPECT_SOME(os::mkdir(newDirectoryName));
+  EXPECT_EQ(expectedListing, listfiles(tmpdir));
+  EXPECT_EQ(hashset<string>::EMPTY, listfiles(newDirectoryName));
+
+  // Successfully remove.
+  EXPECT_SOME(os::rmdir(newDirectoryName));
+  EXPECT_EQ(hashset<string>::EMPTY, listfiles(tmpdir));
+}
+
+
+TEST_F(RmdirTest, RemoveRecursiveByDefault)
+{
+  const string tmpdir = os::getcwd();
+  hashset<string> expectedRootListing = hashset<string>::EMPTY;
+  hashset<string> expectedSubListing = hashset<string>::EMPTY;
+
+  // Directory is initially empty.
+  EXPECT_EQ(expectedRootListing, listfiles(tmpdir));
+
+  // Successfully make directory using absolute path, and then `touch` a file
+  // in that folder.
+  const string newDirectoryName = "newDirectory";
+  const string newDirectoryAbsolutePath = path::join(tmpdir, newDirectoryName);
+  const string newFileName = "newFile";
+  const string newFileAbsolutePath = path::join(
+      newDirectoryAbsolutePath,
+      newFileName);
+
+  expectedRootListing.insert(newDirectoryName);
+  expectedSubListing.insert(newFileName);
+
+  EXPECT_SOME(os::mkdir(newDirectoryAbsolutePath));
+  EXPECT_SOME(os::touch(newFileAbsolutePath));
+  EXPECT_EQ(expectedRootListing, listfiles(tmpdir));
+  EXPECT_EQ(expectedSubListing, listfiles(newDirectoryAbsolutePath));
+
+  // Successfully remove.
+  EXPECT_SOME(os::rmdir(newDirectoryAbsolutePath));
+  EXPECT_EQ(hashset<string>::EMPTY, listfiles(tmpdir));
+  EXPECT_EQ(hashset<string>::EMPTY, listfiles(newDirectoryAbsolutePath));
+}
+
+
+TEST_F(RmdirTest, TrivialFailToRemoveInvalidPath)
+{
+  // Directory is initially empty.
+  EXPECT_EQ(hashset<string>::EMPTY, listfiles(os::getcwd()));
+
+  // Removing fake relative paths should error out.
+  EXPECT_ERROR(os::rmdir("fakeRelativePath", false));
+  EXPECT_ERROR(os::rmdir("fakeRelativePath", true));
+
+  // Directory still empty.
+  EXPECT_EQ(hashset<string>::EMPTY, listfiles(os::getcwd()));
+}
+
+
+TEST_F(RmdirTest, FailToRemoveNestedInvalidPath)
+{
+  const string tmpdir = os::getcwd();
+  hashset<string> expectedRootListing = hashset<string>::EMPTY;
+
+  // Directory is initially empty.
+  EXPECT_EQ(expectedRootListing, listfiles(tmpdir));
+
+  // Successfully make directory using absolute path.
+  const string newDirectoryName = "newDirectory";
+  const string newDirectoryAbsolutePath = path::join(tmpdir, newDirectoryName);
+
+  expectedRootListing.insert(newDirectoryName);
+
+  EXPECT_SOME(os::mkdir(newDirectoryAbsolutePath));
+  EXPECT_EQ(expectedRootListing, listfiles(tmpdir));
+  EXPECT_EQ(hashset<string>::EMPTY, listfiles(newDirectoryAbsolutePath));
+
+  // Fail to remove a path to an invalid folder inside the
+  // `newDirectoryAbsolutePath`.
+  const string fakeAbsolutePath = path::join(newDirectoryAbsolutePath, "fake");
+  EXPECT_ERROR(os::rmdir(fakeAbsolutePath, false));
+  EXPECT_EQ(expectedRootListing, listfiles(tmpdir));
+  EXPECT_EQ(hashset<string>::EMPTY, listfiles(newDirectoryAbsolutePath));
+
+  // Test the same thing, but using the `recursive` flag.
+  EXPECT_ERROR(os::rmdir(fakeAbsolutePath, true));
+  EXPECT_EQ(expectedRootListing, listfiles(tmpdir));
+  EXPECT_EQ(hashset<string>::EMPTY, listfiles(newDirectoryAbsolutePath));
+}
+
+
+#ifndef __WINDOWS__
+// This test verifies that `rmdir` can remove a directory with a
+// device file.
+// TODO(hausdorff): Port this test to Windows. It is not clear that `rdev` and
+// `mknod` will implement the functionality expressed in this test, and as the
+// need for these capabilities arise elsewhere in the codebase, we should
+// rethink abstractions we need here, and subsequently, what this test should
+// look like.
+TEST_F(RmdirTest, RemoveDirectoryWithDeviceFile)
+{
+  // mknod requires root permission.
+  Result<string> user = os::user();
+  ASSERT_SOME(user);
+
+  if (user.get() != "root") {
+    return;
+  }
+
+  // Create a 'char' device file with major number same as that of
+  // `/dev/null`.
+  const string deviceDirectory = path::join(os::getcwd(),
+      "deviceDirectory");
+  ASSERT_SOME(os::mkdir(deviceDirectory));
+
+  const string device = "null";
+
+  const string existing = path::join("/dev", device);
+  ASSERT_TRUE(os::exists(existing));
+
+  Try<mode_t> mode = os::stat::mode(existing);
+  ASSERT_SOME(mode);
+
+  Try<dev_t> rdev = os::stat::rdev(existing);
+  ASSERT_SOME(rdev);
+
+  const string another = path::join(deviceDirectory, device);
+  ASSERT_FALSE(os::exists(another));
+
+  EXPECT_SOME(os::mknod(another, mode.get(), rdev.get()));
+
+  EXPECT_SOME(os::rmdir(deviceDirectory));
+}
+#endif // __WINDOWS__
+
+
+// This test verifies that `rmdir` can remove a directory with a
+// symlink that has no target.
+TEST_F(RmdirTest, RemoveDirectoryWithNoTargetSymbolicLink)
+{
+  const string newDirectory = path::join(os::getcwd(), "newDirectory");
+  ASSERT_SOME(os::mkdir(newDirectory));
+
+  const string link = path::join(newDirectory, "link");
+
+  // Create a symlink to non-existent file 'tmp'.
+  ASSERT_SOME(fs::symlink("tmp", link));
+
+  EXPECT_SOME(os::rmdir(newDirectory));
+}
+
+
+// This test verifies that `rmdir` will only remove the symbolic link and not
+// the target directory.
+TEST_F(RmdirTest, RemoveDirectoryWithSymbolicLinkTargetDirectory)
+{
+  const string newDirectory = path::join(os::getcwd(), "newDirectory");
+  ASSERT_SOME(os::mkdir(newDirectory));
+
+  const string link = path::join(newDirectory, "link");
+
+  const string targetDirectory = path::join(os::getcwd(), "targetDirectory");
+
+  ASSERT_SOME(os::mkdir(targetDirectory));
+
+  // Create a symlink that targets a directory outside the 'newDirectory'.
+  ASSERT_SOME(fs::symlink(targetDirectory, link));
+
+  EXPECT_SOME(os::rmdir(newDirectory));
+
+  // Verify that the target directory is not removed.
+  ASSERT_TRUE(os::exists(targetDirectory));
+}
+
+
+// This test verifies that `rmdir` will only remove the symbolic link and not
+// the target file.
+TEST_F(RmdirTest, RemoveDirectoryWithSymbolicLinkTargetFile)
+{
+  const string newDirectory = path::join(os::getcwd(), "newDirectory");
+  ASSERT_SOME(os::mkdir(newDirectory));
+
+  const string link = path::join(newDirectory, "link");
+
+  const string targetFile = path::join(os::getcwd(), "targetFile");
+
+  ASSERT_SOME(os::touch(targetFile));
+
+  // Create a symlink that targets a file outside the 'newDirectory'.
+  ASSERT_SOME(fs::symlink(targetFile, link));
+
+  EXPECT_SOME(os::rmdir(newDirectory));
+
+  // Verify that the target file is not removed.
+  ASSERT_TRUE(os::exists(targetFile));
+}
+
+
+// This tests that when appropriately instructed, `rmdir` can remove
+// the files and subdirectories that appear in a directory but
+// preserve the directory itself.
+TEST_F(RmdirTest, RemoveDirectoryButPreserveRoot)
+{
+  const string newDirectory = path::join(os::getcwd(), "newDirectory");
+  ASSERT_SOME(os::mkdir(newDirectory));
+
+  const string subDirectory = path::join(newDirectory, "subDirectory");
+  ASSERT_SOME(os::mkdir(subDirectory));
+
+  const string file1 = path::join(newDirectory, "file1");
+  ASSERT_SOME(os::touch(file1));
+
+  const string file2 = path::join(subDirectory, "file2");
+  ASSERT_SOME(os::touch(file2));
+
+  EXPECT_SOME(os::rmdir(newDirectory, true, false));
+  EXPECT_TRUE(os::exists(newDirectory));
+  EXPECT_EQ(hashset<string>::EMPTY, listfiles(newDirectory));
+}

http://git-wip-us.apache.org/repos/asf/mesos/blob/c235f7c1/3rdparty/stout/tests/os/sendfile_tests.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/stout/tests/os/sendfile_tests.cpp b/3rdparty/stout/tests/os/sendfile_tests.cpp
new file mode 100644
index 0000000..e221689
--- /dev/null
+++ b/3rdparty/stout/tests/os/sendfile_tests.cpp
@@ -0,0 +1,92 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+#include <string>
+
+#include <gmock/gmock.h>
+
+#include <gtest/gtest.h>
+
+#include <stout/gtest.hpp>
+#include <stout/os.hpp>
+#include <stout/path.hpp>
+
+#include <stout/tests/utils.hpp>
+
+using std::string;
+
+class OsSendfileTest : public TemporaryDirectoryTest
+{
+public:
+  OsSendfileTest()
+    : LOREM_IPSUM(
+        "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
+        "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim "
+        "ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut "
+        "aliquip ex ea commodo consequat. Duis aute irure dolor in "
+        "reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla "
+        "pariatur. Excepteur sint occaecat cupidatat non proident, sunt in "
+        "culpa qui officia deserunt mollit anim id est laborum.") {}
+
+protected:
+  virtual void SetUp()
+  {
+    TemporaryDirectoryTest::SetUp();
+
+    filename = "lorem.txt";
+
+    ASSERT_SOME(os::write(filename, LOREM_IPSUM));
+  }
+
+  const string LOREM_IPSUM;
+  string filename;
+};
+
+
+TEST_F(OsSendfileTest, Sendfile)
+{
+  Try<int> fd = os::open(filename, O_RDONLY | O_CLOEXEC);
+  ASSERT_SOME(fd);
+
+  // Construct a socket pair and use sendfile to transmit the text.
+  int s[2];
+  ASSERT_NE(-1, socketpair(AF_UNIX, SOCK_STREAM, 0, s)) << os::strerror(errno);
+  Try<ssize_t, SocketError> length =
+    os::sendfile(s[0], fd.get(), 0, LOREM_IPSUM.size());
+  ASSERT_TRUE(length.isSome());
+  ASSERT_EQ(LOREM_IPSUM.size(), length.get());
+
+  char* buffer = new char[LOREM_IPSUM.size()];
+  ASSERT_EQ(LOREM_IPSUM.size(), read(s[1], buffer, LOREM_IPSUM.size()));
+  ASSERT_EQ(LOREM_IPSUM, string(buffer, LOREM_IPSUM.size()));
+  ASSERT_SOME(os::close(fd.get()));
+  delete[] buffer;
+
+  // Now test with a closed socket, the SIGPIPE should be suppressed!
+  fd = os::open(filename, O_RDONLY | O_CLOEXEC);
+  ASSERT_SOME(fd);
+  ASSERT_SOME(os::close(s[1]));
+
+  Try<ssize_t, SocketError> result =
+    os::sendfile(s[0], fd.get(), 0, LOREM_IPSUM.size());
+  int _errno = result.error().code;
+  ASSERT_ERROR(result);
+
+#ifdef __linux__
+  ASSERT_EQ(EPIPE, _errno) << result.error().message;
+#elif defined __APPLE__
+  ASSERT_EQ(ENOTCONN, _errno) << result.error().message;
+#endif
+
+  ASSERT_SOME(os::close(fd.get()));
+  ASSERT_SOME(os::close(s[0]));
+}

http://git-wip-us.apache.org/repos/asf/mesos/blob/c235f7c1/3rdparty/stout/tests/os/signals_tests.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/stout/tests/os/signals_tests.cpp b/3rdparty/stout/tests/os/signals_tests.cpp
new file mode 100644
index 0000000..8b78beb
--- /dev/null
+++ b/3rdparty/stout/tests/os/signals_tests.cpp
@@ -0,0 +1,52 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+#include <errno.h>
+
+#include <string>
+
+#include <gmock/gmock.h>
+
+#include <gtest/gtest.h>
+
+#include <stout/gtest.hpp>
+#include <stout/os.hpp>
+
+using std::string;
+
+// TODO(bmahler): Expose OsTest so this can use it.
+class OsSignalsTest : public ::testing::Test {};
+
+
+TEST_F(OsSignalsTest, Suppress)
+{
+  int pipes[2];
+
+  ASSERT_NE(-1, pipe(pipes));
+
+  ASSERT_SOME(os::close(pipes[0]));
+
+  const string data = "hello";
+
+  // Let's make sure we can suppress SIGPIPE. Note that this
+  // only works on OS X because we are single threaded. In
+  // multi-threaded applications, OS X delivers SIGPIPE to
+  // the process, not necessarily to the triggering thread.
+  SUPPRESS(SIGPIPE) {
+    // Writing to a pipe that has been closed generates SIGPIPE.
+    ASSERT_EQ(-1, write(pipes[1], data.c_str(), data.length()));
+
+    ASSERT_EQ(EPIPE, errno);
+  }
+
+  ASSERT_SOME(os::close(pipes[1]));
+}

http://git-wip-us.apache.org/repos/asf/mesos/blob/c235f7c1/3rdparty/stout/tests/os/socket_tests.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/stout/tests/os/socket_tests.cpp b/3rdparty/stout/tests/os/socket_tests.cpp
new file mode 100644
index 0000000..8ea0f12
--- /dev/null
+++ b/3rdparty/stout/tests/os/socket_tests.cpp
@@ -0,0 +1,34 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+#include <stout/os/socket.hpp>
+
+#include <stout/tests/utils.hpp>
+
+using std::string;
+
+
+class SocketTests : public TemporaryDirectoryTest {};
+
+
+#ifdef __WINDOWS__
+TEST_F(SocketTests, InitSocket)
+{
+  // `wsa_initialize` should always return `true`.
+  ASSERT_TRUE(net::wsa_initialize());
+  ASSERT_TRUE(net::wsa_initialize());
+
+  // `wsa_cleanup` should always return `true`.
+  ASSERT_TRUE(net::wsa_cleanup());
+  ASSERT_TRUE(net::wsa_cleanup());
+}
+#endif // __WINDOWS__

http://git-wip-us.apache.org/repos/asf/mesos/blob/c235f7c1/3rdparty/stout/tests/os/strerror_tests.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/stout/tests/os/strerror_tests.cpp b/3rdparty/stout/tests/os/strerror_tests.cpp
new file mode 100644
index 0000000..9846682
--- /dev/null
+++ b/3rdparty/stout/tests/os/strerror_tests.cpp
@@ -0,0 +1,86 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+#include <errno.h> // For errno defines.
+
+#include <limits>
+
+#include <gtest/gtest.h>
+
+#include <stout/os.hpp>
+
+#include <stout/os/strerror.hpp>
+
+using std::string;
+
+
+#ifdef __WINDOWS__
+const string errorMessage(int err)
+{
+  char buffer[1024];
+
+  ::FormatMessage(
+      FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+      NULL,
+      err,
+      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+      buffer,
+      sizeof(buffer),
+      NULL);
+
+  return buffer;
+}
+#endif // __WINDOWS__
+
+
+TEST(StrerrorTest, ValidErrno)
+{
+  EXPECT_EQ(::strerror(ENODEV), os::strerror(ENODEV));
+  EXPECT_EQ(::strerror(ERANGE), os::strerror(ERANGE));
+}
+
+
+// Test that we behave correctly for invalid errnos
+// where GLIBC does not use an internal buffer.
+TEST(StrerrorTest, InvalidErrno)
+{
+  EXPECT_EQ(::strerror(-1), os::strerror(-1));
+
+  // Check the longest possible "Unknown error N" error message.
+  EXPECT_EQ(::strerror(std::numeric_limits<int>::max()),
+            os::strerror(std::numeric_limits<int>::max()));
+}
+
+
+// The Linux documentation[1] on `herrno` indicates there are only 4 possible
+// values that `h_errno` can have. Here we test that the Windows implementation
+// will return "Unknown resolver error" for other error values.
+//
+// [1] http://linux.die.net/man/3/hstrerror
+TEST(StrerrorTest, ValidHerrno)
+{
+#ifdef __WINDOWS__
+  EXPECT_EQ("Unknown resolver error", os::hstrerror(ENODEV));
+  EXPECT_EQ("Unknown resolver error", os::hstrerror(EINTR));
+  EXPECT_EQ(::errorMessage(HOST_NOT_FOUND), os::hstrerror(HOST_NOT_FOUND));
+  EXPECT_EQ(::errorMessage(NO_DATA), os::hstrerror(NO_DATA));
+  EXPECT_EQ(::errorMessage(NO_RECOVERY), os::hstrerror(NO_RECOVERY));
+  EXPECT_EQ(::errorMessage(TRY_AGAIN), os::hstrerror(TRY_AGAIN));
+#else
+  EXPECT_EQ(::hstrerror(ENODEV), os::hstrerror(ENODEV));
+  EXPECT_EQ(::hstrerror(EINTR), os::hstrerror(EINTR));
+  EXPECT_EQ(::hstrerror(HOST_NOT_FOUND), os::hstrerror(HOST_NOT_FOUND));
+  EXPECT_EQ(::hstrerror(NO_DATA), os::hstrerror(NO_DATA));
+  EXPECT_EQ(::hstrerror(NO_RECOVERY), os::hstrerror(NO_RECOVERY));
+  EXPECT_EQ(::hstrerror(TRY_AGAIN), os::hstrerror(TRY_AGAIN));
+#endif // __WINDOWS__
+}

http://git-wip-us.apache.org/repos/asf/mesos/blob/c235f7c1/3rdparty/stout/tests/os/systems_tests.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/stout/tests/os/systems_tests.cpp b/3rdparty/stout/tests/os/systems_tests.cpp
new file mode 100644
index 0000000..110ba5b
--- /dev/null
+++ b/3rdparty/stout/tests/os/systems_tests.cpp
@@ -0,0 +1,78 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+#include <stout/os.hpp>
+
+#include <stout/tests/utils.hpp>
+
+using std::string;
+
+
+class SystemsTests : public TemporaryDirectoryTest {};
+
+
+TEST_F(SystemsTests, Uname)
+{
+  const Try<os::UTSInfo> info = os::uname();
+
+  ASSERT_SOME(info);
+#ifdef __linux__
+  EXPECT_EQ(info.get().sysname, "Linux");
+
+  // Machine arch must be non-empty.
+  EXPECT_FALSE(info.get().machine.empty());
+#elif defined(__APPLE__)
+  EXPECT_EQ(info.get().sysname, "Darwin");
+
+  // Machine arch must be non-empty.
+  EXPECT_FALSE(info.get().machine.empty());
+#elif defined(__WINDOWS__)
+  // On Windows, `sysname` is one of 2 options.
+  hashset<string> server_types{"Windows", "Windows Server"};
+  EXPECT_TRUE(server_types.contains(info.get().sysname));
+
+  // On Windows, we `machine` takes one of 5 values.
+  hashset<string> arch_types{"AMD64", "ARM", "IA64", "x86", "Unknown"};
+  EXPECT_TRUE(arch_types.contains(info.get().machine));
+#endif // __linux__
+
+  // The `release`, `version`, and `nodename` properties should all be
+  // populated with a string of at least 1 character.
+  EXPECT_GT(info.get().release.size(), 0);
+  EXPECT_GT(info.get().version.size(), 0);
+  EXPECT_GT(info.get().nodename.size(), 0);
+}
+
+
+TEST_F(SystemsTests, Sysname)
+{
+  const Try<string> name = os::sysname();
+
+  ASSERT_SOME(name);
+#ifdef __linux__
+  EXPECT_EQ(name.get(), "Linux");
+#elif defined(__APPLE__)
+  EXPECT_EQ(name.get(), "Darwin");
+#elif defined(__WINDOWS__)
+  // On Windows, `sysname` is one of 2 options.
+  hashset<string> server_types{ "Windows", "Windows Server" };
+  EXPECT_TRUE(server_types.contains(name.get()));
+#endif // __linux__
+}
+
+
+TEST_F(SystemsTests, Release)
+{
+  const Try<Version> info = os::release();
+
+  ASSERT_SOME(info);
+}

http://git-wip-us.apache.org/repos/asf/mesos/blob/c235f7c1/3rdparty/stout/tests/os_tests.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/stout/tests/os_tests.cpp b/3rdparty/stout/tests/os_tests.cpp
new file mode 100644
index 0000000..9271763
--- /dev/null
+++ b/3rdparty/stout/tests/os_tests.cpp
@@ -0,0 +1,809 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+#include <stdint.h>
+
+#ifndef __linux__
+#include <sys/time.h> // For gettimeofday.
+#endif
+#ifdef __FreeBSD__
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#endif
+
+#include <cstdlib> // For rand.
+#include <list>
+#include <map>
+#include <set>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <gmock/gmock.h>
+
+#include <gtest/gtest.h>
+
+#include <stout/duration.hpp>
+#include <stout/foreach.hpp>
+#include <stout/fs.hpp>
+#include <stout/gtest.hpp>
+#include <stout/hashset.hpp>
+#include <stout/numify.hpp>
+#include <stout/os.hpp>
+#include <stout/stopwatch.hpp>
+#include <stout/strings.hpp>
+#include <stout/try.hpp>
+#include <stout/uuid.hpp>
+
+#if defined(__APPLE__) || defined(__FreeBSD__)
+#include <stout/os/sysctl.hpp>
+#endif
+
+#include <stout/tests/utils.hpp>
+
+using os::Exec;
+using os::Fork;
+using os::Process;
+using os::ProcessTree;
+
+using std::list;
+using std::set;
+using std::string;
+using std::vector;
+
+
+#ifdef __FreeBSD__
+static bool isJailed() {
+  int mib[4];
+  size_t len = 4;
+  ::sysctlnametomib("security.jail.jailed", mib, &len);
+  Try<int> jailed = os::sysctl(mib[0], mib[1], mib[2]).integer();
+  if (jailed.isSome()) {
+      return jailed.get() == 1;
+  }
+
+  return false;
+}
+#endif
+
+
+class OsTest : public TemporaryDirectoryTest {};
+
+
+TEST_F(OsTest, Environment)
+{
+  // Make sure the environment has some entries with '=' in the value.
+  os::setenv("SOME_SPECIAL_FLAG", "--flag=foobar");
+
+  char** environ = os::raw::environment();
+
+  hashmap<string, string> environment = os::environment();
+
+  for (size_t index = 0; environ[index] != NULL; index++) {
+    string entry(environ[index]);
+    size_t position = entry.find_first_of('=');
+    if (position == string::npos) {
+      continue; // Skip malformed environment entries.
+    }
+    const string key = entry.substr(0, position);
+    const string value = entry.substr(position + 1);
+    EXPECT_TRUE(environment.contains(key));
+    EXPECT_EQ(value, environment[key]);
+  }
+}
+
+
+TEST_F(OsTest, System)
+{
+  EXPECT_EQ(0, os::system("exit 0"));
+  EXPECT_EQ(0, os::system("sleep 0"));
+  EXPECT_NE(0, os::system("exit 1"));
+  EXPECT_NE(0, os::system("invalid.command"));
+
+  // Note that ::system returns 0 for the following two cases as well.
+  EXPECT_EQ(0, os::system(""));
+  EXPECT_EQ(0, os::system(" "));
+}
+
+
+TEST_F(OsTest, Cloexec)
+{
+  Try<int> fd = os::open(
+      "cloexec",
+      O_CREAT | O_WRONLY | O_APPEND | O_CLOEXEC,
+      S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+  ASSERT_SOME(fd);
+  EXPECT_SOME_TRUE(os::isCloexec(fd.get()));
+
+  close(fd.get());
+
+  fd = os::open(
+      "non-cloexec",
+      O_CREAT | O_WRONLY | O_APPEND,
+      S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+  ASSERT_SOME(fd);
+  EXPECT_SOME_FALSE(os::isCloexec(fd.get()));
+
+  close(fd.get());
+}
+
+
+TEST_F(OsTest, Nonblock)
+{
+  int pipes[2];
+  ASSERT_NE(-1, pipe(pipes));
+
+  Try<bool> isNonBlock = os::isNonblock(pipes[0]);
+  EXPECT_SOME_FALSE(isNonBlock);
+
+  ASSERT_SOME(os::nonblock(pipes[0]));
+
+  isNonBlock = os::isNonblock(pipes[0]);
+  EXPECT_SOME_TRUE(isNonBlock);
+
+  close(pipes[0]);
+  close(pipes[1]);
+
+  EXPECT_ERROR(os::nonblock(pipes[0]));
+  EXPECT_ERROR(os::nonblock(pipes[0]));
+}
+
+
+// Tests whether a file's size is reported by os::stat::size as expected.
+// Tests all four combinations of following a link or not and of a file
+// or a link as argument. Also tests that an error is returned for a
+// non-existing file.
+TEST_F(OsTest, Size)
+{
+  const string file = path::join(os::getcwd(), UUID::random().toString());
+
+  const Bytes size = 1053;
+
+  ASSERT_SOME(os::write(file, string(size.bytes(), 'X')));
+
+  // The reported file size should be the same whether following links
+  // or not, given that the input parameter is not a link.
+  EXPECT_SOME_EQ(size, os::stat::size(file, os::stat::FOLLOW_SYMLINK));
+  EXPECT_SOME_EQ(size, os::stat::size(file, os::stat::DO_NOT_FOLLOW_SYMLINK));
+
+  EXPECT_ERROR(os::stat::size("aFileThatDoesNotExist"));
+
+  const string link = path::join(os::getcwd(), UUID::random().toString());
+
+  ASSERT_SOME(fs::symlink(file, link));
+
+  // Following links we expect the file's size, not the link's.
+  EXPECT_SOME_EQ(size, os::stat::size(link, os::stat::FOLLOW_SYMLINK));
+
+  // Not following links, we expect the string length of the linked path.
+  EXPECT_SOME_EQ(Bytes(file.size()),
+                 os::stat::size(link, os::stat::DO_NOT_FOLLOW_SYMLINK));
+}
+
+
+TEST_F(OsTest, BootId)
+{
+  Try<string> bootId = os::bootId();
+  ASSERT_SOME(bootId);
+  EXPECT_NE("", bootId.get());
+
+#ifdef __linux__
+  Try<string> read = os::read("/proc/sys/kernel/random/boot_id");
+  ASSERT_SOME(read);
+  EXPECT_EQ(bootId.get(), strings::trim(read.get()));
+#elif defined(__APPLE__) || defined(__FreeBSD__)
+  // For OS X and FreeBSD systems, the boot id is the system boot time in
+  // seconds, so assert it can be numified and is a reasonable value.
+  Try<uint64_t> numified = numify<uint64_t>(bootId.get());
+  ASSERT_SOME(numified);
+
+  timeval time;
+  gettimeofday(&time, NULL);
+  EXPECT_GT(Seconds(numified.get()), Seconds(0));
+  EXPECT_LT(Seconds(numified.get()), Seconds(time.tv_sec));
+#endif
+}
+
+
+TEST_F(OsTest, Sleep)
+{
+  Duration duration = Milliseconds(10);
+  Stopwatch stopwatch;
+  stopwatch.start();
+  ASSERT_SOME(os::sleep(duration));
+  ASSERT_LE(duration, stopwatch.elapsed());
+
+  ASSERT_ERROR(os::sleep(Milliseconds(-10)));
+}
+
+
+#if defined(__APPLE__) || defined(__FreeBSD__)
+TEST_F(OsTest, Sysctl)
+{
+  // String test.
+  Try<os::UTSInfo> uname = os::uname();
+
+  ASSERT_SOME(uname);
+
+  Try<string> release = os::sysctl(CTL_KERN, KERN_OSRELEASE).string();
+
+  EXPECT_SOME_EQ(uname.get().release, release);
+
+  Try<string> type = os::sysctl(CTL_KERN, KERN_OSTYPE).string();
+
+  EXPECT_SOME_EQ(uname.get().sysname, type);
+
+  // Integer test.
+  Try<int> maxproc = os::sysctl(CTL_KERN, KERN_MAXPROC).integer();
+
+  ASSERT_SOME(maxproc);
+
+  // Table test.
+  Try<vector<kinfo_proc> > processes =
+    os::sysctl(CTL_KERN, KERN_PROC, KERN_PROC_ALL).table(maxproc.get());
+
+  ASSERT_SOME(processes);
+
+  std::set<pid_t> pids;
+
+  foreach (const kinfo_proc& process, processes.get()) {
+#ifdef __APPLE__
+    pids.insert(process.kp_proc.p_pid);
+#else
+    pids.insert(process.ki_pid);
+#endif // __APPLE__
+  }
+
+  EXPECT_EQ(1, pids.count(getpid()));
+
+  // Timeval test.
+  Try<timeval> bootTime = os::sysctl(CTL_KERN, KERN_BOOTTIME).time();
+  ASSERT_SOME(bootTime);
+
+  timeval time;
+  gettimeofday(&time, NULL);
+
+  EXPECT_GT(Seconds(bootTime.get().tv_sec), Seconds(0));
+  EXPECT_LT(Seconds(bootTime.get().tv_sec), Seconds(time.tv_sec));
+}
+#endif // __APPLE__ || __FreeBSD__
+
+
+TEST_F(OsTest, Children)
+{
+  Try<set<pid_t> > children = os::children(getpid());
+
+  ASSERT_SOME(children);
+  EXPECT_EQ(0u, children.get().size());
+
+  Try<ProcessTree> tree =
+    Fork(None(),                   // Child.
+         Fork(Exec("sleep 10")),   // Grandchild.
+         Exec("sleep 10"))();
+
+  ASSERT_SOME(tree);
+  ASSERT_EQ(1u, tree.get().children.size());
+
+  pid_t child = tree.get().process.pid;
+  pid_t grandchild = tree.get().children.front().process.pid;
+
+  // Ensure the non-recursive children does not include the
+  // grandchild.
+  children = os::children(getpid(), false);
+
+  ASSERT_SOME(children);
+  EXPECT_EQ(1u, children.get().size());
+  EXPECT_EQ(1u, children.get().count(child));
+
+  children = os::children(getpid());
+
+  ASSERT_SOME(children);
+
+  // Depending on whether or not the shell has fork/exec'ed in each
+  // above 'Exec', we could have 2 or 4 children. That is, some shells
+  // might simply for exec the command above (i.e., 'sleep 10') while
+  // others might fork/exec the command, keeping around a 'sh -c'
+  // process as well.
+  EXPECT_LE(2u, children.get().size());
+  EXPECT_GE(4u, children.get().size());
+
+  EXPECT_EQ(1u, children.get().count(child));
+  EXPECT_EQ(1u, children.get().count(grandchild));
+
+  // Cleanup by killing the descendant processes.
+  EXPECT_EQ(0, kill(grandchild, SIGKILL));
+  EXPECT_EQ(0, kill(child, SIGKILL));
+
+  // We have to reap the child for running the tests in repetition.
+  ASSERT_EQ(child, waitpid(child, NULL, 0));
+}
+
+
+void dosetsid()
+{
+  if (::setsid() == -1) {
+    ABORT(string("Failed to setsid: ") + os::strerror(errno));
+  }
+}
+
+
+TEST_F(OsTest, Killtree)
+{
+  Try<ProcessTree> tree =
+    Fork(&dosetsid,                        // Child.
+         Fork(None(),                      // Grandchild.
+              Fork(None(),                 // Great-grandchild.
+                   Fork(&dosetsid,         // Great-great-granchild.
+                        Exec("sleep 10")),
+                   Exec("sleep 10")),
+              Exec("exit 0")),
+         Exec("sleep 10"))();
+
+  ASSERT_SOME(tree);
+
+  // The process tree we instantiate initially looks like this:
+  //
+  //  -+- child sleep 10
+  //   \-+- grandchild exit 0
+  //     \-+- greatGrandchild sleep 10
+  //       \--- greatGreatGrandchild sleep 10
+  //
+  // But becomes two process trees after the grandchild exits:
+  //
+  //  -+- child sleep 10
+  //   \--- grandchild (exit 0)
+  //
+  //  -+- greatGrandchild sleep 10
+  //   \--- greatGreatGrandchild sleep 10
+
+  // Grab the pids from the instantiated process tree.
+  ASSERT_EQ(1u, tree.get().children.size());
+  ASSERT_EQ(1u, tree.get().children.front().children.size());
+  ASSERT_EQ(1u, tree.get().children.front().children.front().children.size());
+
+  pid_t child = tree.get();
+  pid_t grandchild = tree.get().children.front();
+  pid_t greatGrandchild = tree.get().children.front().children.front();
+  pid_t greatGreatGrandchild =
+    tree.get().children.front().children.front().children.front();
+
+  // Now wait for the grandchild to exit splitting the process tree.
+  Duration elapsed = Duration::zero();
+  while (true) {
+    Result<os::Process> process = os::process(grandchild);
+
+    ASSERT_FALSE(process.isError());
+
+    if (process.isNone() || process.get().zombie) {
+      break;
+    }
+
+    if (elapsed > Seconds(10)) {
+      FAIL() << "Granchild process '" << process.get().pid << "' "
+             << "(" << process.get().command << ") did not terminate";
+    }
+
+    os::sleep(Milliseconds(5));
+    elapsed += Milliseconds(5);
+  }
+
+  // Kill the process tree and follow sessions and groups to make sure
+  // we cross the broken link due to the grandchild.
+  Try<list<ProcessTree> > trees =
+    os::killtree(child, SIGKILL, true, true);
+
+  ASSERT_SOME(trees);
+
+  EXPECT_EQ(2u, trees.get().size()) << stringify(trees.get());
+
+  foreach (const ProcessTree& tree, trees.get()) {
+    if (tree.process.pid == child) {
+      // The 'grandchild' _might_ still be in the tree, just zombied,
+      // unless the 'child' reaps the 'grandchild', which may happen
+      // if the shell "sticks around" (i.e., some invocations of 'sh
+      // -c' will 'exec' the command which will likely not do any
+      // reaping, but in other cases an invocation of 'sh -c' will not
+      // 'exec' the command, for example when the command is a
+      // sequence of commands separated by ';').
+      EXPECT_FALSE(tree.contains(greatGrandchild)) << tree;
+      EXPECT_FALSE(tree.contains(greatGreatGrandchild)) << tree;
+    } else if (tree.process.pid == greatGrandchild) {
+      EXPECT_TRUE(tree.contains(greatGreatGrandchild)) << tree;
+    } else {
+      FAIL()
+        << "Not expecting a process tree rooted at "
+        << tree.process.pid << "\n" << tree;
+    }
+  }
+
+  // All processes should be reaped since we've killed everything.
+  // The direct child must be reaped by us below.
+  elapsed = Duration::zero();
+  while (true) {
+    Result<os::Process> _child = os::process(child);
+    ASSERT_SOME(_child);
+
+    if (os::process(greatGreatGrandchild).isNone() &&
+        os::process(greatGrandchild).isNone() &&
+        os::process(grandchild).isNone() &&
+        _child.get().zombie) {
+      break;
+    }
+
+    if (elapsed > Seconds(10)) {
+      FAIL() << "Processes were not reaped after killtree invocation";
+    }
+
+    os::sleep(Milliseconds(5));
+    elapsed += Milliseconds(5);
+  }
+
+  // Expect the pids to be wiped!
+  EXPECT_NONE(os::process(greatGreatGrandchild));
+  EXPECT_NONE(os::process(greatGrandchild));
+  EXPECT_NONE(os::process(grandchild));
+  EXPECT_SOME(os::process(child));
+  EXPECT_TRUE(os::process(child).get().zombie);
+
+  // We have to reap the child for running the tests in repetition.
+  ASSERT_EQ(child, waitpid(child, NULL, 0));
+}
+
+
+TEST_F(OsTest, KilltreeNoRoot)
+{
+  Try<ProcessTree> tree =
+    Fork(&dosetsid,       // Child.
+         Fork(None(),     // Grandchild.
+              Fork(None(),
+                   Exec("sleep 100")),
+              Exec("sleep 100")),
+         Exec("exit 0"))();
+  ASSERT_SOME(tree);
+
+  // The process tree we instantiate initially looks like this:
+  //
+  // -+- child exit 0             [new session and process group leader]
+  //  \-+- grandchild sleep 100
+  //   \-+- great grandchild sleep 100
+  //
+  // But becomes the following tree after the child exits:
+  //
+  // -+- child (exited 0)
+  //  \-+- grandchild sleep 100
+  //   \-+- great grandchild sleep 100
+  //
+  // And gets reparented when we reap the child:
+  //
+  // -+- new parent
+  //  \-+- grandchild sleep 100
+  //   \-+- great grandchild sleep 100
+
+  // Grab the pids from the instantiated process tree.
+  ASSERT_EQ(1u, tree.get().children.size());
+  ASSERT_EQ(1u, tree.get().children.front().children.size());
+
+  pid_t child = tree.get();
+  pid_t grandchild = tree.get().children.front();
+  pid_t greatGrandchild = tree.get().children.front().children.front();
+
+  // Wait for the child to exit.
+  Duration elapsed = Duration::zero();
+  while (true) {
+    Result<os::Process> process = os::process(child);
+    ASSERT_FALSE(process.isError());
+
+    if (process.get().zombie) {
+      break;
+    }
+
+    if (elapsed > Seconds(1)) {
+      FAIL() << "Child process " << stringify(child) << " did not terminate";
+    }
+
+    os::sleep(Milliseconds(5));
+    elapsed += Milliseconds(5);
+  }
+
+  // Ensure we reap our child now.
+  EXPECT_SOME(os::process(child));
+  EXPECT_TRUE(os::process(child).get().zombie);
+  ASSERT_EQ(child, waitpid(child, NULL, 0));
+
+  // Check the grandchild and great grandchild are still running.
+  ASSERT_TRUE(os::exists(grandchild));
+  ASSERT_TRUE(os::exists(greatGrandchild));
+
+  // Check the subtree has been reparented: the parent is no longer
+  // child (the root of the tree), and that the process is not a
+  // zombie. This is done because some systems run a secondary init
+  // process for the user (init --user) that does not have pid 1,
+  // meaning we can't just check that the parent pid == 1.
+  Result<os::Process> _grandchild = os::process(grandchild);
+  ASSERT_SOME(_grandchild);
+  ASSERT_NE(child, _grandchild.get().parent);
+  ASSERT_FALSE(_grandchild.get().zombie);
+
+  // Check to see if we're in a jail on FreeBSD in case we've been
+  // reparented to pid 1
+#if __FreeBSD__
+  if (!isJailed()) {
+#endif
+  // Check that grandchild's parent is also not a zombie.
+  Result<os::Process> currentParent = os::process(_grandchild.get().parent);
+  ASSERT_SOME(currentParent);
+  ASSERT_FALSE(currentParent.get().zombie);
+#ifdef __FreeBSD__
+  }
+#endif
+
+
+  // Kill the process tree. Even though the root process has exited,
+  // we specify to follow sessions and groups which should kill the
+  // grandchild and greatgrandchild.
+  Try<list<ProcessTree>> trees = os::killtree(child, SIGKILL, true, true);
+
+  ASSERT_SOME(trees);
+  EXPECT_FALSE(trees.get().empty());
+
+  // All processes should be reparented and reaped by init.
+  elapsed = Duration::zero();
+  while (true) {
+    if (os::process(grandchild).isNone() &&
+        os::process(greatGrandchild).isNone()) {
+      break;
+    }
+
+    if (elapsed > Seconds(10)) {
+      FAIL() << "Processes were not reaped after killtree invocation";
+    }
+
+    os::sleep(Milliseconds(5));
+    elapsed += Milliseconds(5);
+  }
+
+  EXPECT_NONE(os::process(grandchild));
+  EXPECT_NONE(os::process(greatGrandchild));
+}
+
+
+TEST_F(OsTest, ProcessExists)
+{
+  // Check we exist.
+  EXPECT_TRUE(os::exists(::getpid()));
+
+  // In a FreeBSD jail, pid 1 may not exist.
+#if !defined(__FreeBSD__)
+  // Check init/launchd/systemd exists.
+  // NOTE: This should return true even if we don't have permission to signal
+  // the pid.
+  EXPECT_TRUE(os::exists(1));
+#endif
+
+  // Check existence of a child process through its lifecycle: running,
+  // zombied, reaped.
+  pid_t pid = ::fork();
+  ASSERT_NE(-1, pid);
+
+  if (pid == 0) {
+    // In child process.
+    while (true) { sleep(1); }
+
+    ABORT("Child should not reach this statement");
+  }
+
+  // In parent.
+  EXPECT_TRUE(os::exists(pid));
+
+  ASSERT_EQ(0, kill(pid, SIGKILL));
+
+  // Wait until the process is a zombie.
+  Duration elapsed = Duration::zero();
+  while (true) {
+    Result<os::Process> process = os::process(pid);
+    ASSERT_SOME(process);
+
+    if (process.get().zombie) {
+      break;
+    }
+
+    ASSERT_LT(elapsed, Milliseconds(100));
+
+    os::sleep(Milliseconds(5));
+    elapsed += Milliseconds(5);
+  };
+
+  // The process should still 'exist', even if it's a zombie.
+  EXPECT_TRUE(os::exists(pid));
+
+  // Reap the zombie and confirm the process no longer exists.
+  int status;
+
+  EXPECT_EQ(pid, ::waitpid(pid, &status, 0));
+  EXPECT_TRUE(WIFSIGNALED(status));
+  EXPECT_EQ(SIGKILL, WTERMSIG(status));
+
+  EXPECT_FALSE(os::exists(pid));
+}
+
+
+TEST_F(OsTest, User)
+{
+  Try<string> user_ = os::shell("id -un");
+  EXPECT_SOME(user_);
+
+  Result<string> user = os::user();
+  ASSERT_SOME_EQ(strings::trim(user_.get()), user);
+
+  Try<string> uid_ = os::shell("id -u");
+  EXPECT_SOME(uid_);
+  Try<uid_t> uid = numify<uid_t>(strings::trim(uid_.get()));
+  ASSERT_SOME(uid);
+  EXPECT_SOME_EQ(uid.get(), os::getuid(user.get()));
+
+  Try<string> gid_ = os::shell("id -g");
+  EXPECT_SOME(gid_);
+  Try<gid_t> gid = numify<gid_t>(strings::trim(gid_.get()));
+  ASSERT_SOME(gid);
+  EXPECT_SOME_EQ(gid.get(), os::getgid(user.get()));
+
+  EXPECT_NONE(os::getuid(UUID::random().toString()));
+  EXPECT_NONE(os::getgid(UUID::random().toString()));
+
+  EXPECT_SOME(os::su(user.get()));
+  EXPECT_ERROR(os::su(UUID::random().toString()));
+}
+
+
+// Test setting/resetting/appending to LD_LIBRARY_PATH environment
+// variable (DYLD_LIBRARY_PATH on OS X).
+TEST_F(OsTest, Libraries)
+{
+  const string path1 = "/tmp/path1";
+  const string path2 = "/tmp/path1";
+  string ldLibraryPath;
+  const string originalLibraryPath = os::libraries::paths();
+
+  // Test setPaths.
+  os::libraries::setPaths(path1);
+  EXPECT_EQ(os::libraries::paths(), path1);
+
+  // Test appendPaths.
+  // 1. With empty LD_LIBRARY_PATH.
+  // 1a. Set LD_LIBRARY_PATH to an empty string.
+  os::libraries::setPaths("");
+  ldLibraryPath = os::libraries::paths();
+  EXPECT_EQ(ldLibraryPath, "");
+
+  // 1b. Now test appendPaths.
+  os::libraries::appendPaths(path1);
+  EXPECT_EQ(os::libraries::paths(), path1);
+
+  // 2. With non-empty LD_LIBRARY_PATH.
+  // 2a. Set LD_LIBRARY_PATH to some non-empty value.
+  os::libraries::setPaths(path2);
+  ldLibraryPath = os::libraries::paths();
+  EXPECT_EQ(ldLibraryPath, path2);
+
+  // 2b. Now test appendPaths.
+  os::libraries::appendPaths(path1);
+  EXPECT_EQ(os::libraries::paths(), path2 + ":" + path1);
+
+  // Reset LD_LIBRARY_PATH.
+  os::libraries::setPaths(originalLibraryPath);
+  EXPECT_EQ(os::libraries::paths(), originalLibraryPath);
+}
+
+
+TEST_F(OsTest, Shell)
+{
+  Try<string> result = os::shell("echo %s", "hello world");
+  EXPECT_SOME_EQ("hello world\n", result);
+
+  result = os::shell("foobar");
+  EXPECT_ERROR(result);
+
+  // The `|| true`` necessary so that os::shell() sees a success
+  // exit code and returns stdout (which we have piped stderr to).
+  result = os::shell("LC_ALL=C ls /tmp/foobar889076 2>&1 || true");
+  ASSERT_SOME(result);
+  EXPECT_TRUE(strings::contains(result.get(), "No such file or directory"));
+
+  // Testing a more ambitious command that mutates the filesystem.
+  const string path = "/tmp/os_tests.txt";
+  result = os::shell("touch %s", path.c_str());
+  EXPECT_SOME_EQ("", result);
+  EXPECT_TRUE(os::exists(path));
+
+  // Let's clean up, and ensure this worked too.
+  result = os::shell("rm %s", path.c_str());
+  EXPECT_SOME_EQ("", result);
+  EXPECT_FALSE(os::exists("/tmp/os_tests.txt"));
+}
+
+
+TEST_F(OsTest, Mknod)
+{
+  // mknod requires root permission.
+  Result<string> user = os::user();
+  ASSERT_SOME(user);
+
+  if (user.get() != "root") {
+    return;
+  }
+
+  const string device = "null";
+
+  const string existing = path::join("/dev", device);
+  ASSERT_TRUE(os::exists(existing));
+
+  Try<mode_t> mode = os::stat::mode(existing);
+  ASSERT_SOME(mode);
+
+  Try<dev_t> rdev = os::stat::rdev(existing);
+  ASSERT_SOME(rdev);
+
+  const string another = path::join(os::getcwd(), device);
+  ASSERT_FALSE(os::exists(another));
+
+  EXPECT_SOME(os::mknod(another, mode.get(), rdev.get()));
+
+  EXPECT_SOME(os::rm(another));
+}
+
+
+TEST_F(OsTest, Realpath)
+{
+  // Create a file.
+  const Try<string> _testFile = os::mktemp();
+  ASSERT_SOME(_testFile);
+  ASSERT_SOME(os::touch(_testFile.get()));
+  const string testFile = _testFile.get();
+
+  // Create a symlink pointing to a file.
+  const string testLink = UUID::random().toString();
+  ASSERT_SOME(fs::symlink(testFile, testLink));
+
+  // Validate the symlink.
+  const Try<ino_t> fileInode = os::stat::inode(testFile);
+  ASSERT_SOME(fileInode);
+  const Try<ino_t> linkInode = os::stat::inode(testLink);
+  ASSERT_SOME(linkInode);
+  ASSERT_EQ(fileInode.get(), linkInode.get());
+
+  // Verify that the symlink resolves correctly.
+  Result<string> resolved = os::realpath(testLink);
+  ASSERT_SOME(resolved);
+  EXPECT_TRUE(strings::contains(resolved.get(), testFile));
+
+  // Verify that the file itself resolves correctly.
+  resolved = os::realpath(testFile);
+  ASSERT_SOME(resolved);
+  EXPECT_TRUE(strings::contains(resolved.get(), testFile));
+
+  // Remove the file and the symlink.
+  os::rm(testFile);
+  os::rm(testLink);
+}
+
+
+TEST_F(OsTest, Which)
+{
+  // TODO(jieyu): Test PATH search ordering and file execution bit.
+  Option<string> which = os::which("ls");
+  ASSERT_SOME(which);
+
+  which = os::which("bar");
+  EXPECT_NONE(which);
+}

http://git-wip-us.apache.org/repos/asf/mesos/blob/c235f7c1/3rdparty/stout/tests/path_tests.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/stout/tests/path_tests.cpp b/3rdparty/stout/tests/path_tests.cpp
new file mode 100644
index 0000000..6dff5e7
--- /dev/null
+++ b/3rdparty/stout/tests/path_tests.cpp
@@ -0,0 +1,192 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//  http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <stout/path.hpp>
+
+#include <stout/os/exists.hpp>
+#include <stout/os/getcwd.hpp>
+#include <stout/os/open.hpp>
+#include <stout/os/rm.hpp>
+#include <stout/os/touch.hpp>
+
+#include <stout/tests/utils.hpp>
+
+using std::string;
+using std::vector;
+
+
+// Test many corner cases of Path::basename.
+TEST(PathTest, Basename)
+{
+  // Empty path check.
+  EXPECT_EQ(".", Path("").basename());
+
+  // Check common path patterns.
+  EXPECT_EQ("/", Path("/").basename());
+  EXPECT_EQ(".", Path(".").basename());
+  EXPECT_EQ("..", Path("..").basename());
+
+  EXPECT_EQ("a", Path("a").basename());
+  EXPECT_EQ("b", Path("a/b").basename());
+  EXPECT_EQ("c", Path("a/b/c").basename());
+
+  // Check leading slashes get cleaned up properly.
+  EXPECT_EQ("a", Path("/a").basename());
+  EXPECT_EQ("a", Path("//a").basename());
+  EXPECT_EQ("a", Path("/a/").basename());
+  EXPECT_EQ("c", Path("/a/b/c").basename());
+  EXPECT_EQ("b", Path("/a/b").basename());
+  EXPECT_EQ("b", Path("//a//b").basename());
+
+  // Check trailing slashes get cleaned up properly.
+  EXPECT_EQ("a", Path("a/").basename());
+  EXPECT_EQ("c", Path("/a/b/c//").basename());
+  EXPECT_EQ("c", Path("/a/b/c///").basename());
+  EXPECT_EQ("/", Path("//").basename());
+  EXPECT_EQ("/", Path("///").basename());
+}
+
+
+// Test many corner cases of Path::dirname.
+TEST(PathTest, Dirname)
+{
+  // Empty path check.
+  EXPECT_EQ(".", Path("").dirname());
+
+  // Check common path patterns.
+  EXPECT_EQ("/", Path("/").dirname());
+  EXPECT_EQ(".", Path(".").dirname());
+  EXPECT_EQ(".", Path("..").dirname());
+
+  EXPECT_EQ(".", Path("a").dirname());
+  EXPECT_EQ("a", Path("a/b").dirname());
+  EXPECT_EQ("a/b", Path("a/b/c/").dirname());
+
+  // Check leading slashes get cleaned up properly.
+  EXPECT_EQ("/", Path("/a").dirname());
+  EXPECT_EQ("/", Path("//a").dirname());
+  EXPECT_EQ("/", Path("/a/").dirname());
+  EXPECT_EQ("/a", Path("/a/b").dirname());
+  EXPECT_EQ("//a", Path("//a//b").dirname());
+  EXPECT_EQ("/a/b", Path("/a/b/c").dirname());
+
+  // Check intermittent slashes get handled just like ::dirname does.
+  EXPECT_EQ("/a//b", Path("/a//b//c//").dirname());
+  EXPECT_EQ("//a/b", Path("//a/b//c").dirname());
+
+  // Check trailing slashes get cleaned up properly.
+  EXPECT_EQ(".", Path("a/").dirname());
+  EXPECT_EQ("a/b", Path("a/b/c").dirname());
+  EXPECT_EQ("/a/b", Path("/a/b/c/").dirname());
+  EXPECT_EQ("/a/b", Path("/a/b/c//").dirname());
+  EXPECT_EQ("/a/b", Path("/a/b/c///").dirname());
+  EXPECT_EQ("/", Path("//").dirname());
+  EXPECT_EQ("/", Path("///").dirname());
+}
+
+
+TEST(PathTest, Extension)
+{
+  EXPECT_NONE(Path(".").extension());
+  EXPECT_NONE(Path("..").extension());
+
+  EXPECT_NONE(Path("a").extension());
+  EXPECT_NONE(Path("/a").extension());
+  EXPECT_NONE(Path("/").extension());
+
+  EXPECT_NONE(Path("/a.b/c").extension());
+
+  EXPECT_SOME_EQ(".txt", Path("a.txt").extension());
+  EXPECT_SOME_EQ(".txt", Path("/a/b.txt").extension());
+  EXPECT_SOME_EQ(".txt", Path("/a.b/c.txt").extension());
+
+  EXPECT_SOME_EQ(".gz", Path("a.tar.gz").extension());
+  EXPECT_SOME_EQ(".gz", Path("/a.tar.gz").extension());
+
+  EXPECT_SOME_EQ(".bashrc", Path(".bashrc").extension());
+  EXPECT_SOME_EQ(".bashrc", Path("/.bashrc").extension());
+}
+
+
+TEST(PathTest, Join)
+{
+  EXPECT_EQ("a/b/c", path::join("a", "b", "c"));
+  EXPECT_EQ("/a/b/c", path::join("/a", "b", "c"));
+
+  EXPECT_EQ("", path::join(vector<string>()));
+  EXPECT_EQ("a/b/c", path::join(vector<string>({"a", "b", "c"})));
+
+  // TODO(cmaloney): This should join to ""
+  EXPECT_EQ("/", path::join(vector<string>({"", "", ""})));
+
+  // Interesting corner cases around being the first, middle, last.
+  EXPECT_EQ("/asdf", path::join("/", "asdf"));
+  EXPECT_EQ("/", path::join("", "/", ""));
+  EXPECT_EQ("ab/", path::join("ab/", "", "/"));
+  EXPECT_EQ("/ab", path::join("/", "/", "ab"));
+  EXPECT_EQ("ab/", path::join("ab", "/", "/"));
+  EXPECT_EQ("/ab", path::join("/", "", "/ab"));
+
+  // Check trailing and leading slashes get cleaned up.
+  EXPECT_EQ("a/b/c/", path::join("a/", "b/", "c/"));
+  EXPECT_EQ("/a/b/c", path::join("/a", "/b", "/c"));
+  EXPECT_EQ("/a/b/c/", path::join("/a/", "/b/", "/c/"));
+  EXPECT_EQ("a/b/c/", path::join("a/", "/b/", "/c/"));
+}
+
+
+TEST(PathTest, Absolute)
+{
+  // Check absolute paths.
+  EXPECT_TRUE(path::absolute("/"));
+  EXPECT_TRUE(path::absolute("/foo"));
+  EXPECT_TRUE(path::absolute("/foo/bar"));
+  EXPECT_TRUE(path::absolute("/foo/bar/../baz"));
+
+  // Check relative paths.
+  EXPECT_FALSE(path::absolute(""));
+  EXPECT_FALSE(path::absolute("."));
+  EXPECT_FALSE(path::absolute(".."));
+  EXPECT_FALSE(path::absolute("../"));
+  EXPECT_FALSE(path::absolute("./foo"));
+  EXPECT_FALSE(path::absolute("../foo"));
+}
+
+
+class PathFileTest : public TemporaryDirectoryTest {};
+
+
+TEST_F(PathFileTest, ImplicitConversion)
+{
+  // Should be implicitly converted to string for the various os::_ calls.
+  const Path testfile(path::join(os::getcwd(), "file.txt"));
+
+  // Create the test file.
+  ASSERT_SOME(os::touch(testfile));
+  ASSERT_TRUE(os::exists(testfile));
+
+  // Open and close the file.
+  Try<int> fd = os::open(
+      testfile,
+      O_RDONLY,
+      S_IRUSR | S_IRGRP | S_IROTH);
+  ASSERT_SOME(fd);
+  close(fd.get());
+
+  // Delete the file.
+  EXPECT_SOME(os::rm(testfile));
+}

http://git-wip-us.apache.org/repos/asf/mesos/blob/c235f7c1/3rdparty/stout/tests/proc_tests.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/stout/tests/proc_tests.cpp b/3rdparty/stout/tests/proc_tests.cpp
new file mode 100644
index 0000000..17b9093
--- /dev/null
+++ b/3rdparty/stout/tests/proc_tests.cpp
@@ -0,0 +1,154 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+#include <unistd.h> // For getpid, getppid.
+
+#include <condition_variable>
+#include <iostream>
+#include <list>
+#include <mutex>
+#include <set>
+#include <string>
+#include <thread>
+
+#include <gmock/gmock.h>
+
+#include <stout/abort.hpp>
+#include <stout/gtest.hpp>
+#include <stout/numify.hpp>
+#include <stout/os.hpp>
+#include <stout/proc.hpp>
+#include <stout/synchronized.hpp>
+#include <stout/try.hpp>
+
+using proc::CPU;
+using proc::SystemStatus;
+using proc::ProcessStatus;
+
+using std::set;
+using std::string;
+
+
+TEST(ProcTest, Pids)
+{
+  Try<set<pid_t> > pids = proc::pids();
+
+  ASSERT_SOME(pids);
+  EXPECT_NE(0u, pids.get().size());
+  EXPECT_EQ(1u, pids.get().count(getpid()));
+  EXPECT_EQ(1u, pids.get().count(1));
+}
+
+
+TEST(ProcTest, Cpus)
+{
+  Try<std::list<CPU> > cpus = proc::cpus();
+
+  ASSERT_SOME(cpus);
+  EXPECT_LE(1u, cpus.get().size());
+}
+
+
+TEST(ProcTest, SystemStatus)
+{
+  Try<SystemStatus> status = proc::status();
+
+  ASSERT_SOME(status);
+  EXPECT_NE(0u, status.get().btime);
+}
+
+
+TEST(ProcTest, ProcessStatus)
+{
+  Result<ProcessStatus> status = proc::status(getpid());
+
+  ASSERT_SOME(status);
+  EXPECT_EQ(getpid(), status.get().pid);
+  EXPECT_EQ(getppid(), status.get().ppid);
+}
+
+
+// NOTE: This test assumes there is a single thread running for the test.
+TEST(ProcTest, SingleThread)
+{
+  // Check we have the expected number of threads.
+  Try<set<pid_t> > threads = proc::threads(::getpid());
+
+  ASSERT_SOME(threads);
+  EXPECT_EQ(1u, threads.get().size());
+  EXPECT_EQ(1u, threads.get().count(::getpid()));
+}
+
+
+// NOTE: This test assumes there is only a single thread running for the test.
+TEST(ProcTest, MultipleThreads)
+{
+  const size_t numThreads = 5;
+
+  std::thread* runningThreads[numThreads];
+
+  std::mutex mutex;
+  std::condition_variable cond;
+  bool stop = false;
+
+  // Create additional threads.
+  for (size_t i = 0; i < numThreads; i++) {
+    runningThreads[i] = new std::thread([&mutex, &cond, &stop]() {
+      // Wait until the main thread tells us to exit.
+      synchronized (mutex) {
+        while (!stop) {
+          synchronized_wait(&cond, &mutex);
+        }
+      }
+    });
+  }
+
+  // Check we have the expected number of threads.
+  Try<set<pid_t> > threads = proc::threads(::getpid());
+
+  ASSERT_SOME(threads);
+  EXPECT_EQ(1u + numThreads, threads.get().size());
+  EXPECT_EQ(1u, threads.get().count(::getpid()));
+
+  // Terminate the additional threads.
+  synchronized (mutex) {
+    stop = true;
+    cond.notify_all();
+  }
+
+  for (size_t i = 0; i < numThreads; i++) {
+    runningThreads[i]->join();
+    delete runningThreads[i];
+  }
+
+  // There is some delay before /proc updates after the threads have
+  // terminated. We wait until this occurs before completing the test to ensure
+  // a call to proc::threads in a subsequent test will not return these
+  // threads, e.g., if tests are shuffled and ProcTest.SingleThread occurs
+  // after this test.
+  Duration elapsed = Duration::zero();
+  while (true) {
+    threads = proc::threads(::getpid());
+    ASSERT_SOME(threads);
+
+    if (threads.get().size() == 1) {
+      break;
+    }
+
+    if (elapsed > Seconds(1)) {
+      FAIL() << "Failed to wait for /proc to update for terminated threads";
+    }
+
+    os::sleep(Milliseconds(5));
+    elapsed += Milliseconds(5);
+  }
+}

http://git-wip-us.apache.org/repos/asf/mesos/blob/c235f7c1/3rdparty/stout/tests/protobuf_tests.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/stout/tests/protobuf_tests.cpp b/3rdparty/stout/tests/protobuf_tests.cpp
new file mode 100644
index 0000000..8877e89
--- /dev/null
+++ b/3rdparty/stout/tests/protobuf_tests.cpp
@@ -0,0 +1,603 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+#include <gtest/gtest.h>
+
+#include <gmock/gmock.h>
+
+#include <algorithm>
+#include <string>
+
+#include <stout/gtest.hpp>
+#include <stout/json.hpp>
+#include <stout/jsonify.hpp>
+#include <stout/protobuf.hpp>
+#include <stout/stringify.hpp>
+#include <stout/strings.hpp>
+#include <stout/uuid.hpp>
+
+#include "protobuf_tests.pb.h"
+
+using std::string;
+
+using google::protobuf::RepeatedPtrField;
+
+namespace tests {
+
+// Trivial equality operators to enable gtest macros.
+bool operator==(const SimpleMessage& left, const SimpleMessage& right)
+{
+  if (left.id() != right.id() ||
+      left.numbers().size() != right.numbers().size()) {
+    return false;
+  }
+
+  return std::equal(
+      left.numbers().begin(), left.numbers().end(), right.numbers().begin());
+}
+
+
+bool operator!=(const SimpleMessage& left, const SimpleMessage& right)
+{
+  return !(left == right);
+}
+
+} // namespace tests {
+
+
+TEST(ProtobufTest, JSON)
+{
+  tests::Message message;
+  message.set_b(true);
+  message.set_str("string");
+  message.set_bytes("bytes");
+  message.set_int32(-1);
+  message.set_int64(-1);
+  message.set_uint32(1);
+  message.set_uint64(1);
+  message.set_sint32(-1);
+  message.set_sint64(-1);
+  message.set_f(1.0);
+  message.set_d(1.0);
+  message.set_e(tests::ONE);
+  message.mutable_nested()->set_str("nested");
+  message.add_repeated_bool(true);
+  message.add_repeated_string("repeated_string");
+  message.add_repeated_bytes("repeated_bytes");
+  message.add_repeated_int32(-2);
+  message.add_repeated_int64(-2);
+  message.add_repeated_uint32(2);
+  message.add_repeated_uint64(2);
+  message.add_repeated_sint32(-2);
+  message.add_repeated_sint64(-2);
+  message.add_repeated_float(1.0);
+  message.add_repeated_double(1.0);
+  message.add_repeated_double(2.0);
+  message.add_repeated_enum(tests::TWO);
+  message.add_repeated_nested()->set_str("repeated_nested");
+
+  // TODO(bmahler): To dynamically generate a protobuf message,
+  // see the commented-out code below.
+  //
+  //  DescriptorProto proto;
+  //
+  //  proto.set_name("Message");
+  //
+  //  FieldDescriptorProto* field = proto.add_field();
+  //  field->set_name("str");
+  //  field->set_type(FieldDescriptorProto::TYPE_STRING);
+  //
+  //  const Descriptor* descriptor = proto.descriptor();
+  //
+  //  DynamicMessageFactory factory;
+  //  Message* message = factory.GetPrototype(descriptor);
+  //
+  //  Reflection* message.getReflection();
+
+  // The keys are in alphabetical order.
+  string expected = strings::remove(
+      "{"
+      "  \"b\": true,"
+      "  \"bytes\": \"Ynl0ZXM=\","
+      "  \"d\": 1.0,"
+      "  \"e\": \"ONE\","
+      "  \"f\": 1.0,"
+      "  \"int32\": -1,"
+      "  \"int64\": -1,"
+      "  \"nested\": { \"str\": \"nested\"},"
+      "  \"optional_default\": 42.0,"
+      "  \"repeated_bool\": [true],"
+      "  \"repeated_bytes\": [\"cmVwZWF0ZWRfYnl0ZXM=\"],"
+      "  \"repeated_double\": [1.0, 2.0],"
+      "  \"repeated_enum\": [\"TWO\"],"
+      "  \"repeated_float\": [1.0],"
+      "  \"repeated_int32\": [-2],"
+      "  \"repeated_int64\": [-2],"
+      "  \"repeated_nested\": [ { \"str\": \"repeated_nested\" } ],"
+      "  \"repeated_sint32\": [-2],"
+      "  \"repeated_sint64\": [-2],"
+      "  \"repeated_string\": [\"repeated_string\"],"
+      "  \"repeated_uint32\": [2],"
+      "  \"repeated_uint64\": [2],"
+      "  \"sint32\": -1,"
+      "  \"sint64\": -1,"
+      "  \"str\": \"string\","
+      "  \"uint32\": 1,"
+      "  \"uint64\": 1"
+      "}",
+      " ");
+
+  JSON::Object object = JSON::protobuf(message);
+
+  EXPECT_EQ(expected, stringify(object));
+
+  // Test parsing too.
+  Try<tests::Message> parse = protobuf::parse<tests::Message>(object);
+  ASSERT_SOME(parse);
+
+  EXPECT_EQ(object, JSON::protobuf(parse.get()));
+
+  // Modify the message to test (de-)serialization of random bytes generated
+  // by UUID.
+  message.set_bytes(UUID::random().toBytes());
+
+  object = JSON::protobuf(message);
+
+  // Test parsing too.
+  parse = protobuf::parse<tests::Message>(object);
+  ASSERT_SOME(parse);
+
+  EXPECT_EQ(object, JSON::protobuf(parse.get()));
+
+  // Now convert JSON to string and parse it back as JSON.
+  ASSERT_SOME_EQ(object, JSON::parse(stringify(object)));
+}
+
+
+TEST(ProtobufTest, JSONArray)
+{
+  tests::SimpleMessage message1;
+  message1.set_id("message1");
+  message1.add_numbers(1);
+  message1.add_numbers(2);
+
+  // Messages with different IDs are not equal.
+  tests::SimpleMessage message2;
+  message2.set_id("message2");
+  message2.add_numbers(1);
+  message2.add_numbers(2);
+
+  // The keys are in alphabetical order.
+  string expected = strings::remove(
+      "["
+      "  {"
+      "    \"id\": \"message1\","
+      "    \"numbers\": [1, 2]"
+      "  },"
+      "  {"
+      "    \"id\": \"message2\","
+      "    \"numbers\": [1, 2]"
+      "  }"
+      "]",
+      " ");
+
+  tests::ArrayMessage arrayMessage;
+  arrayMessage.add_values()->CopyFrom(message1);
+  arrayMessage.add_values()->CopyFrom(message2);
+
+  JSON::Array array = JSON::protobuf(arrayMessage.values());
+
+  EXPECT_EQ(expected, stringify(array));
+}
+
+
+// Tests that integer precision is maintained between
+// JSON <-> Protobuf conversions.
+TEST(ProtobufTest, JsonLargeIntegers)
+{
+  // These numbers are equal or close to the integer limits.
+  tests::Message message;
+  message.set_int32(-2147483647);
+  message.set_int64(-9223372036854775807);
+  message.set_uint32(4294967295U);
+  message.set_uint64(9223372036854775807);
+  message.set_sint32(-1234567890);
+  message.set_sint64(-1234567890123456789);
+  message.add_repeated_int32(-2000000000);
+  message.add_repeated_int64(-9000000000000000000);
+  message.add_repeated_uint32(3000000000U);
+  message.add_repeated_uint64(7000000000000000000);
+  message.add_repeated_sint32(-1000000000);
+  message.add_repeated_sint64(-8000000000000000000);
+
+  // Parts of the protobuf that are required.  Copied from the above test.
+  message.set_b(true);
+  message.set_str("string");
+  message.set_bytes("bytes");
+  message.set_f(1.0);
+  message.set_d(1.0);
+  message.set_e(tests::ONE);
+  message.mutable_nested()->set_str("nested");
+
+  // The keys are in alphabetical order.
+  string expected = strings::remove(
+      "{"
+      "  \"b\": true,"
+      "  \"bytes\": \"Ynl0ZXM=\","
+      "  \"d\": 1.0,"
+      "  \"e\": \"ONE\","
+      "  \"f\": 1.0,"
+      "  \"int32\": -2147483647,"
+      "  \"int64\": -9223372036854775807,"
+      "  \"nested\": {\"str\": \"nested\"},"
+      "  \"optional_default\": 42.0,"
+      "  \"repeated_int32\": [-2000000000],"
+      "  \"repeated_int64\": [-9000000000000000000],"
+      "  \"repeated_sint32\": [-1000000000],"
+      "  \"repeated_sint64\": [-8000000000000000000],"
+      "  \"repeated_uint32\": [3000000000],"
+      "  \"repeated_uint64\": [7000000000000000000],"
+      "  \"sint32\": -1234567890,"
+      "  \"sint64\": -1234567890123456789,"
+      "  \"str\": \"string\","
+      "  \"uint32\": 4294967295,"
+      "  \"uint64\": 9223372036854775807"
+      "}",
+      " ");
+
+  // Check JSON -> String.
+  JSON::Object object = JSON::protobuf(message);
+  EXPECT_EQ(expected, stringify(object));
+
+  // Check JSON -> Protobuf.
+  Try<tests::Message> parse = protobuf::parse<tests::Message>(object);
+  ASSERT_SOME(parse);
+
+  // Check Protobuf -> JSON.
+  EXPECT_EQ(object, JSON::protobuf(parse.get()));
+
+  // Check String -> JSON.
+  Try<JSON::Object> json = JSON::parse<JSON::Object>(expected);
+  EXPECT_SOME_EQ(object, json);
+}
+
+
+TEST(ProtobufTest, SimpleMessageEquals)
+{
+  tests::SimpleMessage message1;
+  message1.set_id("message1");
+  message1.add_numbers(1);
+  message1.add_numbers(2);
+
+  // Obviously, a message should equal to itself.
+  EXPECT_EQ(message1, message1);
+
+  // Messages with different IDs are not equal.
+  tests::SimpleMessage message2;
+  message2.set_id("message2");
+  message2.add_numbers(1);
+  message2.add_numbers(2);
+
+  EXPECT_NE(message1, message2);
+
+  // Messages with not identical collection of numbers are not equal.
+  tests::SimpleMessage message3;
+  message3.set_id("message1");
+  message3.add_numbers(1);
+
+  EXPECT_NE(message1, message3);
+
+  tests::SimpleMessage message4;
+  message4.set_id("message1");
+  message4.add_numbers(2);
+  message4.add_numbers(1);
+
+  EXPECT_NE(message1, message4);
+
+  // Different messages with the same ID and collection of numbers should
+  // be equal. Their JSON counterparts should be equal as well.
+  tests::SimpleMessage message5;
+  message5.set_id("message1");
+  message5.add_numbers(1);
+  message5.add_numbers(2);
+
+  EXPECT_EQ(message1, message5);
+  EXPECT_EQ(JSON::protobuf(message1), JSON::protobuf(message5));
+}
+
+
+TEST(ProtobufTest, ParseJSONArray)
+{
+  tests::SimpleMessage message;
+  message.set_id("message1");
+  message.add_numbers(1);
+  message.add_numbers(2);
+
+  // Convert protobuf message to a JSON object.
+  JSON::Object object = JSON::protobuf(message);
+
+  // Populate JSON array with JSON objects, conversion JSON::Object ->
+  // JSON::Value is implicit.
+  JSON::Array array;
+  array.values.push_back(object);
+  array.values.push_back(object);
+
+  // Parse JSON array into a collection of protobuf messages.
+  auto parse =
+    protobuf::parse<RepeatedPtrField<tests::SimpleMessage>>(array);
+  ASSERT_SOME(parse);
+  auto repeated = parse.get();
+
+  // Make sure the parsed message equals to the original one.
+  EXPECT_EQ(message, repeated.Get(0));
+  EXPECT_EQ(message, repeated.Get(1));
+}
+
+
+TEST(ProtobufTest, ParseJSONNull)
+{
+  tests::Nested nested;
+  nested.set_str("value");
+
+  // Test message with optional field set to 'null'.
+  string message =
+    "{"
+    "  \"str\": \"value\","
+    "  \"optional_str\": null"
+    "}";
+
+  Try<JSON::Object> json = JSON::parse<JSON::Object>(message);
+  ASSERT_SOME(json);
+
+  Try<tests::Nested> parse = protobuf::parse<tests::Nested>(json.get());
+  ASSERT_SOME(parse);
+
+  EXPECT_EQ(parse->SerializeAsString(), nested.SerializeAsString());
+
+  // Test message with repeated field set to 'null'.
+  message =
+    "{"
+    "  \"str\": \"value\","
+    "  \"repeated_str\": null"
+    "}";
+
+  json = JSON::parse<JSON::Object>(message);
+  ASSERT_SOME(json);
+
+  parse = protobuf::parse<tests::Nested>(json.get());
+  ASSERT_SOME(parse);
+
+  EXPECT_EQ(parse->SerializeAsString(), nested.SerializeAsString());
+
+  // Test message with required field set to 'null'.
+  message =
+    "{"
+    "  \"str\": null"
+    "}";
+
+  json = JSON::parse<JSON::Object>(message);
+  ASSERT_SOME(json);
+
+  EXPECT_ERROR(protobuf::parse<tests::Nested>(json.get()));
+}
+
+
+TEST(ProtobufTest, ParseJSONNestedError)
+{
+  // Here we trigger an error parsing the 'nested' message.
+  string message =
+    "{"
+    "  \"b\": true,"
+    "  \"str\": \"string\","
+    "  \"bytes\": \"Ynl0ZXM=\","
+    "  \"f\": 1.0,"
+    "  \"d\": 1.0,"
+    "  \"e\": \"ONE\","
+    "  \"nested\": {"
+    "      \"str\": 1.0" // Error due to int for string type.
+    "  }"
+    "}";
+
+  Try<JSON::Object> json = JSON::parse<JSON::Object>(message);
+  ASSERT_SOME(json);
+
+  Try<tests::Message> parse = protobuf::parse<tests::Message>(json.get());
+  ASSERT_ERROR(parse);
+
+  EXPECT_TRUE(strings::contains(
+      parse.error(), "Not expecting a JSON number for field"));
+}
+
+
+TEST(ProtobufTest, Jsonify)
+{
+  tests::Message message;
+  message.set_b(true);
+  message.set_str("string");
+  message.set_bytes("bytes");
+  message.set_int32(-1);
+  message.set_int64(-1);
+  message.set_uint32(1);
+  message.set_uint64(1);
+  message.set_sint32(-1);
+  message.set_sint64(-1);
+  message.set_f(1.0);
+  message.set_d(1.0);
+  message.set_e(tests::ONE);
+  message.mutable_nested()->set_str("nested");
+  message.add_repeated_bool(true);
+  message.add_repeated_string("repeated_string");
+  message.add_repeated_bytes("repeated_bytes");
+  message.add_repeated_int32(-2);
+  message.add_repeated_int64(-2);
+  message.add_repeated_uint32(2);
+  message.add_repeated_uint64(2);
+  message.add_repeated_sint32(-2);
+  message.add_repeated_sint64(-2);
+  message.add_repeated_float(1.0);
+  message.add_repeated_double(1.0);
+  message.add_repeated_double(2.0);
+  message.add_repeated_enum(tests::TWO);
+  message.add_repeated_nested()->set_str("repeated_nested");
+
+  // TODO(bmahler): To dynamically generate a protobuf message,
+  // see the commented-out code below.
+//  DescriptorProto proto;
+//
+//  proto.set_name("Message");
+//
+//  FieldDescriptorProto* field = proto.add_field();
+//  field->set_name("str");
+//  field->set_type(FieldDescriptorProto::TYPE_STRING);
+//
+//  const Descriptor* descriptor = proto.descriptor();
+//
+//  DynamicMessageFactory factory;
+//  Message* message = factory.GetPrototype(descriptor);
+//
+//  Reflection* message.getReflection();
+
+  // The keys are in alphabetical order.
+  string expected = strings::remove(
+      "{"
+      "  \"b\": true,"
+      "  \"str\": \"string\","
+      "  \"bytes\": \"Ynl0ZXM=\","
+      "  \"int32\": -1,"
+      "  \"int64\": -1,"
+      "  \"uint32\": 1,"
+      "  \"uint64\": 1,"
+      "  \"sint32\": -1,"
+      "  \"sint64\": -1,"
+      "  \"f\": 1.0,"
+      "  \"d\": 1.0,"
+      "  \"e\": \"ONE\","
+      "  \"nested\": { \"str\": \"nested\"},"
+      "  \"repeated_bool\": [true],"
+      "  \"repeated_string\": [\"repeated_string\"],"
+      "  \"repeated_bytes\": [\"cmVwZWF0ZWRfYnl0ZXM=\"],"
+      "  \"repeated_int32\": [-2],"
+      "  \"repeated_int64\": [-2],"
+      "  \"repeated_uint32\": [2],"
+      "  \"repeated_uint64\": [2],"
+      "  \"repeated_sint32\": [-2],"
+      "  \"repeated_sint64\": [-2],"
+      "  \"repeated_float\": [1.0],"
+      "  \"repeated_double\": [1.0, 2.0],"
+      "  \"repeated_enum\": [\"TWO\"],"
+      "  \"repeated_nested\": [ { \"str\": \"repeated_nested\" } ],"
+      "  \"optional_default\": 42.0"
+      "}",
+      " ");
+
+  EXPECT_EQ(expected, string(jsonify(JSON::Protobuf(message))));
+}
+
+
+TEST(ProtobufTest, JsonifyArray)
+{
+  tests::SimpleMessage message1;
+  message1.set_id("message1");
+  message1.add_numbers(1);
+  message1.add_numbers(2);
+
+  // Messages with different IDs are not equal.
+  tests::SimpleMessage message2;
+  message2.set_id("message2");
+  message2.add_numbers(1);
+  message2.add_numbers(2);
+
+  // The keys are in alphabetical order.
+  string expected = strings::remove(
+      "["
+      "  {"
+      "    \"id\": \"message1\","
+      "    \"numbers\": [1, 2]"
+      "  },"
+      "  {"
+      "    \"id\": \"message2\","
+      "    \"numbers\": [1, 2]"
+      "  }"
+      "]",
+      " ");
+
+  tests::ArrayMessage arrayMessage;
+  arrayMessage.add_values()->CopyFrom(message1);
+  arrayMessage.add_values()->CopyFrom(message2);
+
+  string actual = jsonify([&arrayMessage](JSON::ArrayWriter* writer) {
+    foreach (const tests::SimpleMessage& message, arrayMessage.values()) {
+      writer->element(JSON::Protobuf(message));
+    }
+  });
+
+  EXPECT_EQ(expected, actual);
+}
+
+
+// Tests that integer precision is maintained between
+// JSON <-> Protobuf conversions.
+TEST(ProtobufTest, JsonifyLargeIntegers)
+{
+  // These numbers are equal or close to the integer limits.
+  tests::Message message;
+  message.set_int32(-2147483647);
+  message.set_int64(-9223372036854775807);
+  message.set_uint32(4294967295U);
+  message.set_uint64(9223372036854775807);
+  message.set_sint32(-1234567890);
+  message.set_sint64(-1234567890123456789);
+  message.add_repeated_int32(-2000000000);
+  message.add_repeated_int64(-9000000000000000000);
+  message.add_repeated_uint32(3000000000U);
+  message.add_repeated_uint64(7000000000000000000);
+  message.add_repeated_sint32(-1000000000);
+  message.add_repeated_sint64(-8000000000000000000);
+
+  // Parts of the protobuf that are required.  Copied from the above test.
+  message.set_b(true);
+  message.set_str("string");
+  message.set_bytes("bytes");
+  message.set_f(1.0);
+  message.set_d(1.0);
+  message.set_e(tests::ONE);
+  message.mutable_nested()->set_str("nested");
+
+  // The keys are in alphabetical order.
+  string expected = strings::remove(
+      "{"
+      "  \"b\": true,"
+      "  \"str\": \"string\","
+      "  \"bytes\": \"Ynl0ZXM=\","
+      "  \"int32\": -2147483647,"
+      "  \"int64\": -9223372036854775807,"
+      "  \"uint32\": 4294967295,"
+      "  \"uint64\": 9223372036854775807,"
+      "  \"sint32\": -1234567890,"
+      "  \"sint64\": -1234567890123456789,"
+      "  \"f\": 1.0,"
+      "  \"d\": 1.0,"
+      "  \"e\": \"ONE\","
+      "  \"nested\": {\"str\": \"nested\"},"
+      "  \"repeated_int32\": [-2000000000],"
+      "  \"repeated_int64\": [-9000000000000000000],"
+      "  \"repeated_uint32\": [3000000000],"
+      "  \"repeated_uint64\": [7000000000000000000],"
+      "  \"repeated_sint32\": [-1000000000],"
+      "  \"repeated_sint64\": [-8000000000000000000],"
+      "  \"optional_default\": 42.0"
+      "}",
+      " ");
+
+  // Check JSON -> String.
+  EXPECT_EQ(expected, string(jsonify(JSON::Protobuf(message))));
+}


Mime
View raw message