CodeMod: Drop FOLLY_OVERRIDE and FOLLY_FINAL
[folly.git] / folly / test / FileTest.cpp
index 4f47f0fec2b2e6328e9d29941acdb657165b8258..3a120c54c68637dc8986002337ccabe7a4bd7ccd 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2013 Facebook, Inc.
+ * Copyright 2015 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * limitations under the License.
  */
 
-#include "folly/File.h"
+#include <folly/File.h>
 
+#include <mutex>
+
+#include <boost/thread/locks.hpp>
 #include <glog/logging.h>
 #include <gtest/gtest.h>
 
-#include "folly/Benchmark.h"
-#include "folly/String.h"
+#include <folly/Benchmark.h>
+#include <folly/String.h>
+#include <folly/Subprocess.h>
+#include <folly/experimental/io/FsUtil.h>
+#include <folly/experimental/TestUtil.h>
 
 using namespace folly;
+using namespace folly::test;
 
 namespace {
 void expectWouldBlock(ssize_t r) {
@@ -48,6 +55,15 @@ TEST(File, Simple) {
   }
 }
 
+TEST(File, SimpleStringPiece) {
+  char buf = 'x';
+  File f(StringPiece("/etc/hosts"));
+  EXPECT_NE(-1, f.fd());
+  EXPECT_EQ(1, ::read(f.fd(), &buf, 1));
+  f.close();
+  EXPECT_EQ(-1, f.fd());
+}
+
 TEST(File, OwnsFd) {
   // Wrap a file descriptor, make sure that ownsFd works
   // We'll test that the file descriptor is closed by closing the writing
@@ -84,6 +100,12 @@ TEST(File, OwnsFd) {
   ::close(p[0]);
 }
 
+TEST(File, Release) {
+  File in(STDOUT_FILENO, false);
+  CHECK_EQ(STDOUT_FILENO, in.release());
+  CHECK_EQ(-1, in.release());
+}
+
 #define EXPECT_CONTAINS(haystack, needle) \
   EXPECT_NE(::std::string::npos, ::folly::StringPiece(haystack).find(needle)) \
     << "Haystack: '" << haystack << "'\nNeedle: '" << needle << "'";
@@ -122,3 +144,89 @@ TEST(File, Truthy) {
     EXPECT_TRUE(false);
   }
 }
+
+TEST(File, Locks) {
+  typedef std::unique_lock<File> Lock;
+  typedef boost::shared_lock<File> SharedLock;
+
+  // Find out where we are.
+  static constexpr size_t pathLength = 2048;
+  char buf[pathLength + 1];
+  int r = readlink("/proc/self/exe", buf, pathLength);
+  CHECK_ERR(r);
+  buf[r] = '\0';
+
+  // NOTE(agallagher): Our various internal build systems layout built
+  // binaries differently, so the two layouts below.
+  fs::path me(buf);
+  auto helper_basename = "file_test_lock_helper";
+  fs::path helper;
+  if (fs::exists(me.parent_path() / helper_basename)) {
+    helper = me.parent_path() / helper_basename;
+  } else if (fs::exists(
+      me.parent_path().parent_path() / helper_basename / helper_basename)) {
+    helper = me.parent_path().parent_path()
+      / helper_basename / helper_basename;
+  } else {
+    throw std::runtime_error(
+      folly::to<std::string>("cannot find helper ", helper_basename));
+  }
+
+  TemporaryFile tempFile;
+  File f(tempFile.fd());
+
+  enum LockMode { EXCLUSIVE, SHARED };
+  auto testLock = [&] (LockMode mode, bool expectedSuccess) {
+    auto ret =
+      Subprocess({helper.native(),
+                  mode == SHARED ? "-s" : "-x",
+                  tempFile.path().native()}).wait();
+    EXPECT_TRUE(ret.exited());
+    if (ret.exited()) {
+      EXPECT_EQ(expectedSuccess ? 0 : 42, ret.exitStatus());
+    }
+  };
+
+  // Make sure nothing breaks and things compile.
+  {
+    Lock lock(f);
+  }
+
+  {
+    SharedLock lock(f);
+  }
+
+  {
+    Lock lock(f, std::defer_lock);
+    EXPECT_TRUE(lock.try_lock());
+  }
+
+  {
+    SharedLock lock(f, boost::defer_lock);
+    EXPECT_TRUE(lock.try_lock());
+  }
+
+  // X blocks X
+  {
+    Lock lock(f);
+    testLock(EXCLUSIVE, false);
+  }
+
+  // X blocks S
+  {
+    Lock lock(f);
+    testLock(SHARED, false);
+  }
+
+  // S blocks X
+  {
+    SharedLock lock(f);
+    testLock(EXCLUSIVE, false);
+  }
+
+  // S does not block S
+  {
+    SharedLock lock(f);
+    testLock(SHARED, true);
+  }
+}