File ctor should take StringPiece.
[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, SimpleStringPiece) {
59   char buf = 'x';
60   File f(StringPiece("/etc/hosts"));
61   EXPECT_NE(-1, f.fd());
62   EXPECT_EQ(1, ::read(f.fd(), &buf, 1));
63   f.close();
64   EXPECT_EQ(-1, f.fd());
65 }
66
67 TEST(File, OwnsFd) {
68   // Wrap a file descriptor, make sure that ownsFd works
69   // We'll test that the file descriptor is closed by closing the writing
70   // end of a pipe and making sure that a non-blocking read from the reading
71   // end returns 0.
72
73   char buf = 'x';
74   int p[2];
75   expectOK(::pipe(p));
76   int flags = ::fcntl(p[0], F_GETFL);
77   expectOK(flags);
78   expectOK(::fcntl(p[0], F_SETFL, flags | O_NONBLOCK));
79   expectWouldBlock(::read(p[0], &buf, 1));
80   {
81     File f(p[1]);
82     EXPECT_EQ(p[1], f.fd());
83   }
84   // Ensure that moving the file doesn't close it
85   {
86     File f(p[1]);
87     EXPECT_EQ(p[1], f.fd());
88     File f1(std::move(f));
89     EXPECT_EQ(-1, f.fd());
90     EXPECT_EQ(p[1], f1.fd());
91   }
92   expectWouldBlock(::read(p[0], &buf, 1));  // not closed
93   {
94     File f(p[1], true);
95     EXPECT_EQ(p[1], f.fd());
96   }
97   ssize_t r = ::read(p[0], &buf, 1);  // eof
98   expectOK(r);
99   EXPECT_EQ(0, r);
100   ::close(p[0]);
101 }
102
103 TEST(File, Release) {
104   File in(STDOUT_FILENO, false);
105   CHECK_EQ(STDOUT_FILENO, in.release());
106   CHECK_EQ(-1, in.release());
107 }
108
109 #define EXPECT_CONTAINS(haystack, needle) \
110   EXPECT_NE(::std::string::npos, ::folly::StringPiece(haystack).find(needle)) \
111     << "Haystack: '" << haystack << "'\nNeedle: '" << needle << "'";
112
113 TEST(File, UsefulError) {
114   try {
115     File("does_not_exist.txt", 0, 0666);
116   } catch (const std::runtime_error& e) {
117     EXPECT_CONTAINS(e.what(), "does_not_exist.txt");
118     EXPECT_CONTAINS(e.what(), "0666");
119   }
120 }
121
122 TEST(File, Truthy) {
123   File temp = File::temporary();
124
125   EXPECT_TRUE(bool(temp));
126
127   if (temp) {
128     ;
129   } else {
130     EXPECT_FALSE(true);
131   }
132
133   if (File file = File::temporary()) {
134     ;
135   } else {
136     EXPECT_FALSE(true);
137   }
138
139   EXPECT_FALSE(bool(File()));
140   if (File()) {
141     EXPECT_TRUE(false);
142   }
143   if (File notOpened = File()) {
144     EXPECT_TRUE(false);
145   }
146 }
147
148 TEST(File, Locks) {
149   typedef std::unique_lock<File> Lock;
150   typedef boost::shared_lock<File> SharedLock;
151
152   // Find out where we are.
153   static constexpr size_t pathLength = 2048;
154   char buf[pathLength + 1];
155   int r = readlink("/proc/self/exe", buf, pathLength);
156   CHECK_ERR(r);
157   buf[r] = '\0';
158
159   // NOTE(agallagher): Our various internal build systems layout built
160   // binaries differently, so the two layouts below.
161   fs::path me(buf);
162   auto helper_basename = "file_test_lock_helper";
163   fs::path helper;
164   if (fs::exists(me.parent_path() / helper_basename)) {
165     helper = me.parent_path() / helper_basename;
166   } else if (fs::exists(
167       me.parent_path().parent_path() / helper_basename / helper_basename)) {
168     helper = me.parent_path().parent_path()
169       / helper_basename / helper_basename;
170   } else {
171     throw std::runtime_error(
172       folly::to<std::string>("cannot find helper ", helper_basename));
173   }
174
175   TemporaryFile tempFile;
176   File f(tempFile.fd());
177
178   enum LockMode { EXCLUSIVE, SHARED };
179   auto testLock = [&] (LockMode mode, bool expectedSuccess) {
180     auto ret =
181       Subprocess({helper.native(),
182                   mode == SHARED ? "-s" : "-x",
183                   tempFile.path().native()}).wait();
184     EXPECT_TRUE(ret.exited());
185     if (ret.exited()) {
186       EXPECT_EQ(expectedSuccess ? 0 : 42, ret.exitStatus());
187     }
188   };
189
190   // Make sure nothing breaks and things compile.
191   {
192     Lock lock(f);
193   }
194
195   {
196     SharedLock lock(f);
197   }
198
199   {
200     Lock lock(f, std::defer_lock);
201     EXPECT_TRUE(lock.try_lock());
202   }
203
204   {
205     SharedLock lock(f, boost::defer_lock);
206     EXPECT_TRUE(lock.try_lock());
207   }
208
209   // X blocks X
210   {
211     Lock lock(f);
212     testLock(EXCLUSIVE, false);
213   }
214
215   // X blocks S
216   {
217     Lock lock(f);
218     testLock(SHARED, false);
219   }
220
221   // S blocks X
222   {
223     SharedLock lock(f);
224     testLock(EXCLUSIVE, false);
225   }
226
227   // S does not block S
228   {
229     SharedLock lock(f);
230     testLock(SHARED, true);
231   }
232 }