Copyright 2014->2015
[folly.git] / folly / test / FileTest.cpp
1 /*
2  * Copyright 2015 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <folly/File.h>
18
19 #include <mutex>
20
21 #include <boost/thread/locks.hpp>
22 #include <glog/logging.h>
23 #include <gtest/gtest.h>
24
25 #include <folly/Benchmark.h>
26 #include <folly/String.h>
27 #include <folly/Subprocess.h>
28 #include <folly/experimental/io/FsUtil.h>
29 #include <folly/experimental/TestUtil.h>
30
31 using namespace folly;
32 using namespace folly::test;
33
34 namespace {
35 void expectWouldBlock(ssize_t r) {
36   int savedErrno = errno;
37   EXPECT_EQ(-1, r);
38   EXPECT_EQ(EAGAIN, savedErrno) << errnoStr(errno);
39 }
40 void expectOK(ssize_t r) {
41   int savedErrno = errno;
42   EXPECT_LE(0, r) << ": errno=" << errnoStr(errno);
43 }
44 }  // namespace
45
46 TEST(File, Simple) {
47   // Open a file, ensure it's indeed open for reading
48   char buf = 'x';
49   {
50     File f("/etc/hosts");
51     EXPECT_NE(-1, f.fd());
52     EXPECT_EQ(1, ::read(f.fd(), &buf, 1));
53     f.close();
54     EXPECT_EQ(-1, f.fd());
55   }
56 }
57
58 TEST(File, OwnsFd) {
59   // Wrap a file descriptor, make sure that ownsFd works
60   // We'll test that the file descriptor is closed by closing the writing
61   // end of a pipe and making sure that a non-blocking read from the reading
62   // end returns 0.
63
64   char buf = 'x';
65   int p[2];
66   expectOK(::pipe(p));
67   int flags = ::fcntl(p[0], F_GETFL);
68   expectOK(flags);
69   expectOK(::fcntl(p[0], F_SETFL, flags | O_NONBLOCK));
70   expectWouldBlock(::read(p[0], &buf, 1));
71   {
72     File f(p[1]);
73     EXPECT_EQ(p[1], f.fd());
74   }
75   // Ensure that moving the file doesn't close it
76   {
77     File f(p[1]);
78     EXPECT_EQ(p[1], f.fd());
79     File f1(std::move(f));
80     EXPECT_EQ(-1, f.fd());
81     EXPECT_EQ(p[1], f1.fd());
82   }
83   expectWouldBlock(::read(p[0], &buf, 1));  // not closed
84   {
85     File f(p[1], true);
86     EXPECT_EQ(p[1], f.fd());
87   }
88   ssize_t r = ::read(p[0], &buf, 1);  // eof
89   expectOK(r);
90   EXPECT_EQ(0, r);
91   ::close(p[0]);
92 }
93
94 TEST(File, Release) {
95   File in(STDOUT_FILENO, false);
96   CHECK_EQ(STDOUT_FILENO, in.release());
97   CHECK_EQ(-1, in.release());
98 }
99
100 #define EXPECT_CONTAINS(haystack, needle) \
101   EXPECT_NE(::std::string::npos, ::folly::StringPiece(haystack).find(needle)) \
102     << "Haystack: '" << haystack << "'\nNeedle: '" << needle << "'";
103
104 TEST(File, UsefulError) {
105   try {
106     File("does_not_exist.txt", 0, 0666);
107   } catch (const std::runtime_error& e) {
108     EXPECT_CONTAINS(e.what(), "does_not_exist.txt");
109     EXPECT_CONTAINS(e.what(), "0666");
110   }
111 }
112
113 TEST(File, Truthy) {
114   File temp = File::temporary();
115
116   EXPECT_TRUE(bool(temp));
117
118   if (temp) {
119     ;
120   } else {
121     EXPECT_FALSE(true);
122   }
123
124   if (File file = File::temporary()) {
125     ;
126   } else {
127     EXPECT_FALSE(true);
128   }
129
130   EXPECT_FALSE(bool(File()));
131   if (File()) {
132     EXPECT_TRUE(false);
133   }
134   if (File notOpened = File()) {
135     EXPECT_TRUE(false);
136   }
137 }
138
139 TEST(File, Locks) {
140   typedef std::unique_lock<File> Lock;
141   typedef boost::shared_lock<File> SharedLock;
142
143   // Find out where we are.
144   static constexpr size_t pathLength = 2048;
145   char buf[pathLength + 1];
146   int r = readlink("/proc/self/exe", buf, pathLength);
147   CHECK_ERR(r);
148   buf[r] = '\0';
149
150   // NOTE(agallagher): Our various internal build systems layout built
151   // binaries differently, so the two layouts below.
152   fs::path me(buf);
153   auto helper_basename = "file_test_lock_helper";
154   fs::path helper;
155   if (fs::exists(me.parent_path() / helper_basename)) {
156     helper = me.parent_path() / helper_basename;
157   } else if (fs::exists(
158       me.parent_path().parent_path() / helper_basename / helper_basename)) {
159     helper = me.parent_path().parent_path()
160       / helper_basename / helper_basename;
161   } else {
162     throw std::runtime_error(
163       folly::to<std::string>("cannot find helper ", helper_basename));
164   }
165
166   TemporaryFile tempFile;
167   File f(tempFile.fd());
168
169   enum LockMode { EXCLUSIVE, SHARED };
170   auto testLock = [&] (LockMode mode, bool expectedSuccess) {
171     auto ret =
172       Subprocess({helper.native(),
173                   mode == SHARED ? "-s" : "-x",
174                   tempFile.path().native()}).wait();
175     EXPECT_TRUE(ret.exited());
176     if (ret.exited()) {
177       EXPECT_EQ(expectedSuccess ? 0 : 42, ret.exitStatus());
178     }
179   };
180
181   // Make sure nothing breaks and things compile.
182   {
183     Lock lock(f);
184   }
185
186   {
187     SharedLock lock(f);
188   }
189
190   {
191     Lock lock(f, std::defer_lock);
192     EXPECT_TRUE(lock.try_lock());
193   }
194
195   {
196     SharedLock lock(f, boost::defer_lock);
197     EXPECT_TRUE(lock.try_lock());
198   }
199
200   // X blocks X
201   {
202     Lock lock(f);
203     testLock(EXCLUSIVE, false);
204   }
205
206   // X blocks S
207   {
208     Lock lock(f);
209     testLock(SHARED, false);
210   }
211
212   // S blocks X
213   {
214     SharedLock lock(f);
215     testLock(EXCLUSIVE, false);
216   }
217
218   // S does not block S
219   {
220     SharedLock lock(f);
221     testLock(SHARED, true);
222   }
223 }