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