You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by mp...@apache.org on 2016/04/10 01:48:34 UTC

[1/3] mesos git commit: Transition `filesystem_tests.cpp` to use `path::join`.

Repository: mesos
Updated Branches:
  refs/heads/master 8c6a55d10 -> 8d9eb5fb9


Transition `filesystem_tests.cpp` to use `path::join`.

This commit will fix a test that fails due to inconsistent usage of path
delimiting characters.

Historically in Mesos, it has been common to join paths by adding
strings. For example: `somePath + "/yourFile"`. The '/' character in
particular is contentious, and can cause problems if you're not
consistent in your usage. For example, if `somePath` already uses '\' as
a path separator, then joining the string `"/yourFile"` introduces
inconsistency in the path delimiter usage, resulting in undesirable
behavior.

Review: https://reviews.apache.org/r/45009/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/36214b8d
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/36214b8d
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/36214b8d

Branch: refs/heads/master
Commit: 36214b8d5637a30b4467a1c1e0b1f973621fbefd
Parents: 8c6a55d
Author: Alex Clemmer <cl...@gmail.com>
Authored: Sat Apr 9 16:25:13 2016 -0700
Committer: Michael Park <mp...@apache.org>
Committed: Sat Apr 9 16:25:13 2016 -0700

----------------------------------------------------------------------
 .../stout/tests/os/filesystem_tests.cpp         | 42 ++++++++++----------
 1 file changed, 21 insertions(+), 21 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/36214b8d/3rdparty/libprocess/3rdparty/stout/tests/os/filesystem_tests.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/3rdparty/stout/tests/os/filesystem_tests.cpp b/3rdparty/libprocess/3rdparty/stout/tests/os/filesystem_tests.cpp
index ab6f595..c93b827 100644
--- a/3rdparty/libprocess/3rdparty/stout/tests/os/filesystem_tests.cpp
+++ b/3rdparty/libprocess/3rdparty/stout/tests/os/filesystem_tests.cpp
@@ -57,13 +57,13 @@ class FsTest : public TemporaryDirectoryTest {};
 TEST_F(FsTest, Find)
 {
   const string testdir = path::join(os::getcwd(), UUID::random().toString());
-  const string subdir = testdir + "/test1";
+  const string subdir = path::join(testdir, "test1");
   ASSERT_SOME(os::mkdir(subdir)); // Create the directories.
 
   // Now write some files.
-  const string file1 = testdir + "/file1.txt";
-  const string file2 = subdir + "/file2.txt";
-  const string file3 = subdir + "/file3.jpg";
+  const string file1 = path::join(testdir, "file1.txt");
+  const string file2 = path::join(subdir, "file2.txt");
+  const string file3 = path::join(subdir, "file3.jpg");
 
   ASSERT_SOME(os::touch(file1));
   ASSERT_SOME(os::touch(file2));
@@ -105,9 +105,9 @@ TEST_F(FsTest, Mkdir)
   hashset<string> expectedListing = EMPTY;
   EXPECT_EQ(expectedListing, listfiles(tmpdir));
 
-  os::mkdir(tmpdir + "/a/b/c");
-  os::mkdir(tmpdir + "/a/b/d");
-  os::mkdir(tmpdir + "/e/f");
+  ASSERT_SOME(os::mkdir(path::join(tmpdir, "a", "b", "c")));
+  ASSERT_SOME(os::mkdir(path::join(tmpdir, "a", "b", "d")));
+  ASSERT_SOME(os::mkdir(path::join(tmpdir, "e", "f")));
 
   expectedListing = EMPTY;
   expectedListing.insert("a");
@@ -116,22 +116,22 @@ TEST_F(FsTest, Mkdir)
 
   expectedListing = EMPTY;
   expectedListing.insert("b");
-  EXPECT_EQ(expectedListing, listfiles(tmpdir + "/a"));
+  EXPECT_EQ(expectedListing, listfiles(path::join(tmpdir, "a")));
 
   expectedListing = EMPTY;
   expectedListing.insert("c");
   expectedListing.insert("d");
-  EXPECT_EQ(expectedListing, listfiles(tmpdir + "/a/b"));
+  EXPECT_EQ(expectedListing, listfiles(path::join(tmpdir, "a", "b")));
 
   expectedListing = EMPTY;
-  EXPECT_EQ(expectedListing, listfiles(tmpdir + "/a/b/c"));
-  EXPECT_EQ(expectedListing, listfiles(tmpdir + "/a/b/d"));
+  EXPECT_EQ(expectedListing, listfiles(path::join(tmpdir, "a", "b", "c")));
+  EXPECT_EQ(expectedListing, listfiles(path::join(tmpdir, "a", "b", "d")));
 
   expectedListing.insert("f");
-  EXPECT_EQ(expectedListing, listfiles(tmpdir + "/e"));
+  EXPECT_EQ(expectedListing, listfiles(path::join(tmpdir, "e")));
 
   expectedListing = EMPTY;
-  EXPECT_EQ(expectedListing, listfiles(tmpdir + "/e/f"));
+  EXPECT_EQ(expectedListing, listfiles(path::join(tmpdir, "e", "f")));
 }
 
 
@@ -146,7 +146,7 @@ TEST_F(FsTest, Touch)
 TEST_F(FsTest, Symlink)
 {
   const string temp_path = os::getcwd();
-  const string link = path::join(temp_path, "/sym.link");
+  const string link = path::join(temp_path, "sym.link");
   const string file = path::join(temp_path, UUID::random().toString());
 
   // Create file
@@ -155,7 +155,7 @@ TEST_F(FsTest, Symlink)
   ASSERT_TRUE(os::exists(file));
 
   // Create symlink
-  fs::symlink(file, link);
+  ASSERT_SOME(fs::symlink(file, link));
 
   // Test symlink
   EXPECT_TRUE(os::stat::islink(link));
@@ -168,9 +168,9 @@ TEST_F(FsTest, List)
   ASSERT_SOME(os::mkdir(testdir)); // Create the directories.
 
   // Now write some files.
-  const string file1 = testdir + "/file1.txt";
-  const string file2 = testdir + "/file2.txt";
-  const string file3 = testdir + "/file3.jpg";
+  const string file1 = path::join(testdir, "file1.txt");
+  const string file2 = path::join(testdir, "file2.txt");
+  const string file3 = path::join(testdir, "file3.jpg");
 
   ASSERT_SOME(os::touch(file1));
   ASSERT_SOME(os::touch(file2));
@@ -204,9 +204,9 @@ TEST_F(FsTest, Rename)
   ASSERT_SOME(os::mkdir(testdir)); // Create the directories.
 
   // Now write some files.
-  const string file1 = testdir + "/file1.txt";
-  const string file2 = testdir + "/file2.txt";
-  const string file3 = testdir + "/file3.jpg";
+  const string file1 = path::join(testdir, "file1.txt");
+  const string file2 = path::join(testdir, "file2.txt");
+  const string file3 = path::join(testdir, "file3.jpg");
 
   ASSERT_SOME(os::touch(file1));
   ASSERT_SOME(os::touch(file2));


[2/3] mesos git commit: Windows: Fixed bug causing `os::exists` to report invalid paths exist.

Posted by mp...@apache.org.
Windows: Fixed bug causing `os::exists` to report invalid paths exist.

Currently on Windows, `os::exists` will return true if a component of a
path does not exist. For example if you have `a/fancy/path`, and you ask
`os::exists("a/fake/path")`, the result currently reports `true`. In
other words, the Windows code path only checks for the error that a file
does not exist, and ignores the error that says the path is not valid.

This commit will fix this, and also add a test that will verify we don't
regress.

Review: https://reviews.apache.org/r/45015/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/387a62e5
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/387a62e5
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/387a62e5

Branch: refs/heads/master
Commit: 387a62e57e9021c006bbe9c780fde23edc40c8d2
Parents: 36214b8
Author: Alex Clemmer <cl...@gmail.com>
Authored: Sat Apr 9 16:25:50 2016 -0700
Committer: Michael Park <mp...@apache.org>
Committed: Sat Apr 9 16:25:50 2016 -0700

----------------------------------------------------------------------
 .../stout/include/stout/os/windows/exists.hpp   | 17 ++++++++---
 .../stout/tests/os/filesystem_tests.cpp         | 32 ++++++++++++++++++++
 2 files changed, 45 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/387a62e5/3rdparty/libprocess/3rdparty/stout/include/stout/os/windows/exists.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/os/windows/exists.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/os/windows/exists.hpp
index 9211851..423e4a8 100644
--- a/3rdparty/libprocess/3rdparty/stout/include/stout/os/windows/exists.hpp
+++ b/3rdparty/libprocess/3rdparty/stout/include/stout/os/windows/exists.hpp
@@ -31,15 +31,24 @@ inline bool exists(const std::string& path)
     return false;
   }
 
-  // NOTE: GetFileAttributes does not support unicode natively. See also
-  // "documentation"[1] for why this is a check-if-file-exists idiom.
+  // NOTE: `GetFileAttributes` redirects to either `GetFileAttributesA`
+  // (ASCII) or `GetFileAttributesW` (for `wchar`s). It returns
+  // `INVALID_FILE_ATTRIBUTES` if the file could not be opened for any reason.
+  // Checking for one of two 'not found' error codes (`ERROR_FILE_NOT_FOUND` or
+  // `ERROR_PATH_NOT_FOUND`) is a reliable test for whether the file or
+  // directory exists. See also [1] for more information on this technique.
   //
   // [1] http://blogs.msdn.com/b/oldnewthing/archive/2007/10/23/5612082.aspx
   DWORD attributes = GetFileAttributes(absolutePath.get().c_str());
 
-  bool fileNotFound = GetLastError() == ERROR_FILE_NOT_FOUND;
+  if (attributes == INVALID_FILE_ATTRIBUTES) {
+    DWORD error = GetLastError();
+    if (error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND) {
+      return false;
+    }
+  }
 
-  return !((attributes == INVALID_FILE_ATTRIBUTES) && fileNotFound);
+  return true;
 }
 
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/387a62e5/3rdparty/libprocess/3rdparty/stout/tests/os/filesystem_tests.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/3rdparty/stout/tests/os/filesystem_tests.cpp b/3rdparty/libprocess/3rdparty/stout/tests/os/filesystem_tests.cpp
index c93b827..6262892 100644
--- a/3rdparty/libprocess/3rdparty/stout/tests/os/filesystem_tests.cpp
+++ b/3rdparty/libprocess/3rdparty/stout/tests/os/filesystem_tests.cpp
@@ -135,6 +135,38 @@ TEST_F(FsTest, Mkdir)
 }
 
 
+TEST_F(FsTest, Exists)
+{
+  const hashset<string> EMPTY;
+  const string tmpdir = os::getcwd();
+
+  hashset<string> expectedListing = EMPTY;
+  ASSERT_EQ(expectedListing, listfiles(tmpdir));
+
+  // Create simple directory structure.
+  ASSERT_SOME(os::mkdir(path::join(tmpdir, "a", "b", "c")));
+
+  // Expect all the directories exist.
+  EXPECT_TRUE(os::exists(tmpdir));
+  EXPECT_TRUE(os::exists(path::join(tmpdir, "a")));
+  EXPECT_TRUE(os::exists(path::join(tmpdir, "a", "b")));
+  EXPECT_TRUE(os::exists(path::join(tmpdir, "a", "b", "c")));
+
+  // Return false if a component of the path does not exist.
+  EXPECT_FALSE(os::exists(path::join(tmpdir, "a", "fakeDir")));
+  EXPECT_FALSE(os::exists(path::join(tmpdir, "a", "fakeDir", "c")));
+
+  // Add file to directory tree.
+  ASSERT_SOME(os::touch(path::join(tmpdir, "a", "b", "c", "yourFile")));
+
+  // Assert it exists.
+  EXPECT_TRUE(os::exists(path::join(tmpdir, "a", "b", "c", "yourFile")));
+
+  // Return false if file is wrong.
+  EXPECT_FALSE(os::exists(path::join(tmpdir, "a", "b", "c", "yourFakeFile")));
+}
+
+
 TEST_F(FsTest, Touch)
 {
   const string testfile  = path::join(os::getcwd(), UUID::random().toString());


[3/3] mesos git commit: Windows: Add Windows-friendly implementation of `rm.hpp`.

Posted by mp...@apache.org.
Windows: Add Windows-friendly implementation of `rm.hpp`.

We currently depend on arcane "POSIX-like" Windows APIs like `::remove`.
This API is incompatible with NT paths -- which we will eventually have
to transition to for the Windows integration -- and has no documented
behavior interacting with subsystems like the driver subsystem, which is
required for symlinks.

This commit will move us to proper core Windows API equivalents for
`::remove`.

Review: https://reviews.apache.org/r/45022/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/8d9eb5fb
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/8d9eb5fb
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/8d9eb5fb

Branch: refs/heads/master
Commit: 8d9eb5fb90839b27a0acbcf4dcac44d01bf21f8d
Parents: 387a62e
Author: Alex Clemmer <cl...@gmail.com>
Authored: Sat Apr 9 16:26:20 2016 -0700
Committer: Michael Park <mp...@apache.org>
Committed: Sat Apr 9 16:26:20 2016 -0700

----------------------------------------------------------------------
 .../3rdparty/stout/include/Makefile.am          |  2 +
 .../stout/include/stout/os/posix/rm.hpp         | 37 ++++++++++++
 .../3rdparty/stout/include/stout/os/rm.hpp      | 26 +++------
 .../stout/include/stout/os/windows/rm.hpp       | 58 +++++++++++++++++++
 .../stout/tests/os/filesystem_tests.cpp         | 61 ++++++++++++++++++++
 5 files changed, 165 insertions(+), 19 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/8d9eb5fb/3rdparty/libprocess/3rdparty/stout/include/Makefile.am
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/3rdparty/stout/include/Makefile.am b/3rdparty/libprocess/3rdparty/stout/include/Makefile.am
index 03f46d9..23b9309 100644
--- a/3rdparty/libprocess/3rdparty/stout/include/Makefile.am
+++ b/3rdparty/libprocess/3rdparty/stout/include/Makefile.am
@@ -112,6 +112,7 @@ nobase_include_HEADERS =			\
   stout/os/posix/pstree.hpp			\
   stout/os/posix/read.hpp			\
   stout/os/posix/rename.hpp			\
+  stout/os/posix/rm.hpp				\
   stout/os/posix/rmdir.hpp			\
   stout/os/posix/sendfile.hpp			\
   stout/os/posix/shell.hpp			\
@@ -133,6 +134,7 @@ nobase_include_HEADERS =			\
   stout/os/windows/pstree.hpp			\
   stout/os/windows/read.hpp			\
   stout/os/windows/rename.hpp			\
+  stout/os/windows/rm.hpp			\
   stout/os/windows/rmdir.hpp			\
   stout/os/windows/sendfile.hpp			\
   stout/os/windows/shell.hpp			\

http://git-wip-us.apache.org/repos/asf/mesos/blob/8d9eb5fb/3rdparty/libprocess/3rdparty/stout/include/stout/os/posix/rm.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/os/posix/rm.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/os/posix/rm.hpp
new file mode 100644
index 0000000..b1ec881
--- /dev/null
+++ b/3rdparty/libprocess/3rdparty/stout/include/stout/os/posix/rm.hpp
@@ -0,0 +1,37 @@
+// 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.
+#ifndef __STOUT_OS_POSIX_RM_HPP__
+#define __STOUT_OS_POSIX_RM_HPP__
+
+#include <stdio.h>
+
+#include <string>
+
+#include <stout/error.hpp>
+#include <stout/nothing.hpp>
+#include <stout/try.hpp>
+
+
+namespace os {
+
+inline Try<Nothing> rm(const std::string& path)
+{
+  if (::remove(path.c_str()) != 0) {
+    return ErrnoError();
+  }
+
+  return Nothing();
+}
+
+} // namespace os {
+
+#endif // __STOUT_OS_POSIX_RM_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/8d9eb5fb/3rdparty/libprocess/3rdparty/stout/include/stout/os/rm.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/os/rm.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/os/rm.hpp
index 52568b3..a2d48f5 100644
--- a/3rdparty/libprocess/3rdparty/stout/include/stout/os/rm.hpp
+++ b/3rdparty/libprocess/3rdparty/stout/include/stout/os/rm.hpp
@@ -13,26 +13,14 @@
 #ifndef __STOUT_OS_RM_HPP__
 #define __STOUT_OS_RM_HPP__
 
-#include <string>
 
-#include <stdio.h>
+// For readability, we minimize the number of #ifdef blocks in the code by
+// splitting platform specifc system calls into separate directories.
+#ifdef __WINDOWS__
+#include <stout/os/windows/rm.hpp>
+#else
+#include <stout/os/posix/rm.hpp>
+#endif // __WINDOWS__
 
-#include <stout/error.hpp>
-#include <stout/nothing.hpp>
-#include <stout/try.hpp>
-
-
-namespace os {
-
-inline Try<Nothing> rm(const std::string& path)
-{
-  if (::remove(path.c_str()) != 0) {
-    return ErrnoError();
-  }
-
-  return Nothing();
-}
-
-} // namespace os {
 
 #endif // __STOUT_OS_RM_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/8d9eb5fb/3rdparty/libprocess/3rdparty/stout/include/stout/os/windows/rm.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/os/windows/rm.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/os/windows/rm.hpp
new file mode 100644
index 0000000..6460b52
--- /dev/null
+++ b/3rdparty/libprocess/3rdparty/stout/include/stout/os/windows/rm.hpp
@@ -0,0 +1,58 @@
+// 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.
+#ifndef __STOUT_OS_WINDOWS_RM_HPP__
+#define __STOUT_OS_WINDOWS_RM_HPP__
+
+#include <stdio.h>
+
+#include <string>
+
+#include <stout/error.hpp>
+#include <stout/nothing.hpp>
+#include <stout/try.hpp>
+#include <stout/windows.hpp>
+
+#include <stout/os/stat.hpp>
+
+
+namespace os {
+
+inline Try<Nothing> rm(const std::string& path)
+{
+  // This function uses either `RemoveDirectory` or `DeleteFile` to remove the
+  // actual filesystem entry. These WinAPI functions are being used instead of
+  // the POSIX `remove` because their behavior when it comes to symbolic links
+  // is well documented in MSDN[1][2]. Whenever called on a symbolic link, both
+  // `RemoveDirectory` and `DeleteFile` act on the symlink itself, rather than
+  // its target.
+  //
+  // Because `RemoveDirectory` fails if the specified path is not an empty
+  // directory, its behavior is consistent with the POSIX[3] `remove`[3] (which
+  // uses `rmdir`[4]).
+  //
+  // [1] https://msdn.microsoft.com/en-us/library/windows/desktop/aa365488(v=vs.85).aspx
+  // [2] https://msdn.microsoft.com/en-us/library/windows/desktop/aa365682(v=vs.85).aspx#deletefile_and_deletefiletransacted
+  // [3] http://pubs.opengroup.org/onlinepubs/009695399/functions/remove.html
+  // [4] http://pubs.opengroup.org/onlinepubs/009695399/functions/rmdir.html
+  const BOOL result = os::stat::isdir(path) ? ::RemoveDirectory(path.c_str())
+                                            : ::DeleteFile(path.c_str());
+
+  if (!result) {
+    return WindowsError("`os::rm` could not remove '" + path + "'");
+  }
+
+  return Nothing();
+}
+
+} // namespace os {
+
+#endif // __STOUT_OS_WINDOWS_RM_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/8d9eb5fb/3rdparty/libprocess/3rdparty/stout/tests/os/filesystem_tests.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/3rdparty/stout/tests/os/filesystem_tests.cpp b/3rdparty/libprocess/3rdparty/stout/tests/os/filesystem_tests.cpp
index 6262892..269079f 100644
--- a/3rdparty/libprocess/3rdparty/stout/tests/os/filesystem_tests.cpp
+++ b/3rdparty/libprocess/3rdparty/stout/tests/os/filesystem_tests.cpp
@@ -28,6 +28,7 @@
 #include <stout/os/mkdir.hpp>
 #include <stout/os/read.hpp>
 #include <stout/os/rename.hpp>
+#include <stout/os/rm.hpp>
 #include <stout/os/touch.hpp>
 #include <stout/os/write.hpp>
 
@@ -194,6 +195,66 @@ TEST_F(FsTest, Symlink)
 }
 
 
+TEST_F(FsTest, Rm)
+{
+  const string tmpdir = os::getcwd();
+
+  hashset<string> expectedListing;
+  EXPECT_EQ(expectedListing, listfiles(tmpdir));
+
+  const string file1 = path::join(tmpdir, "file1.txt");
+  const string directory1 = path::join(tmpdir, "directory1");
+
+  const string fileSymlink1 = path::join(tmpdir, "fileSymlink1");
+  const string fileToSymlink = path::join(tmpdir, "fileToSymlink.txt");
+  const string directorySymlink1 = path::join(tmpdir, "directorySymlink1");
+  const string directoryToSymlink = path::join(tmpdir, "directoryToSymlink");
+
+  // Create a file, a directory, and a symlink to a file and a directory.
+  ASSERT_SOME(os::touch(file1));
+  expectedListing.insert("file1.txt");
+
+  ASSERT_SOME(os::mkdir(directory1));
+  expectedListing.insert("directory1");
+
+  ASSERT_SOME(os::touch(fileToSymlink));
+  ASSERT_SOME(fs::symlink(fileToSymlink, fileSymlink1));
+  expectedListing.insert("fileToSymlink.txt");
+  expectedListing.insert("fileSymlink1");
+
+  ASSERT_SOME(os::mkdir(directoryToSymlink));
+  ASSERT_SOME(fs::symlink(directoryToSymlink, directorySymlink1));
+  expectedListing.insert("directoryToSymlink");
+  expectedListing.insert("directorySymlink1");
+
+  EXPECT_EQ(expectedListing, listfiles(tmpdir));
+
+  // Verify `rm` of non-empty directory fails.
+  EXPECT_ERROR(os::rm(tmpdir));
+  EXPECT_EQ(expectedListing, listfiles(tmpdir));
+
+  // Remove all, and verify.
+  EXPECT_SOME(os::rm(file1));
+  expectedListing.erase("file1.txt");
+
+  EXPECT_SOME(os::rm(directory1));
+  expectedListing.erase("directory1");
+
+  EXPECT_SOME(os::rm(fileSymlink1));
+  expectedListing.erase("fileSymlink1");
+
+  EXPECT_SOME(os::rm(directorySymlink1));
+  expectedListing.erase("directorySymlink1");
+
+  // `os::rm` doesn't act on the target, therefore we must verify they each
+  // still exist.
+  EXPECT_EQ(expectedListing, listfiles(tmpdir));
+
+  // Verify that we error out for paths that don't exist.
+  EXPECT_ERROR(os::rm("fakeFile"));
+}
+
+
 TEST_F(FsTest, List)
 {
   const string testdir = path::join(os::getcwd(), UUID::random().toString());