flock locks in folly::File, FileUtil, Exception.h fixes and tests
[folly.git] / folly / test / FileTest.cpp
1 /*
2  * Copyright 2013 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 #define EXPECT_CONTAINS(haystack, needle) \
95   EXPECT_NE(::std::string::npos, ::folly::StringPiece(haystack).find(needle)) \
96     << "Haystack: '" << haystack << "'\nNeedle: '" << needle << "'";
97
98 TEST(File, UsefulError) {
99   try {
100     File("does_not_exist.txt", 0, 0666);
101   } catch (const std::runtime_error& e) {
102     EXPECT_CONTAINS(e.what(), "does_not_exist.txt");
103     EXPECT_CONTAINS(e.what(), "0666");
104   }
105 }
106
107 TEST(File, Truthy) {
108   File temp = File::temporary();
109
110   EXPECT_TRUE(bool(temp));
111
112   if (temp) {
113     ;
114   } else {
115     EXPECT_FALSE(true);
116   }
117
118   if (File file = File::temporary()) {
119     ;
120   } else {
121     EXPECT_FALSE(true);
122   }
123
124   EXPECT_FALSE(bool(File()));
125   if (File()) {
126     EXPECT_TRUE(false);
127   }
128   if (File notOpened = File()) {
129     EXPECT_TRUE(false);
130   }
131 }
132
133 TEST(File, Locks) {
134   typedef std::unique_lock<File> Lock;
135   typedef boost::shared_lock<File> SharedLock;
136
137   // Find out where we are.
138   static constexpr size_t pathLength = 2048;
139   char buf[pathLength + 1];
140   int r = readlink("/proc/self/exe", buf, pathLength);
141   CHECK_ERR(r);
142   buf[r] = '\0';
143
144   fs::path helper(buf);
145   helper.remove_filename();
146   helper /= "file_test_lock_helper";
147
148   TemporaryFile tempFile;
149   File f(tempFile.fd());
150
151   enum LockMode { EXCLUSIVE, SHARED };
152   auto testLock = [&] (LockMode mode, bool expectedSuccess) {
153     auto ret =
154       Subprocess({helper.native(),
155                   mode == SHARED ? "-s" : "-x",
156                   tempFile.path().native()}).wait();
157     EXPECT_TRUE(ret.exited());
158     if (ret.exited()) {
159       EXPECT_EQ(expectedSuccess ? 0 : 42, ret.exitStatus());
160     }
161   };
162
163   // Make sure nothing breaks and things compile.
164   {
165     Lock lock(f);
166   }
167
168   {
169     SharedLock lock(f);
170   }
171
172   {
173     Lock lock(f, std::defer_lock);
174     EXPECT_TRUE(lock.try_lock());
175   }
176
177   {
178     SharedLock lock(f, boost::defer_lock);
179     EXPECT_TRUE(lock.try_lock());
180   }
181
182   // X blocks X
183   {
184     Lock lock(f);
185     testLock(EXCLUSIVE, false);
186   }
187
188   // X blocks S
189   {
190     Lock lock(f);
191     testLock(SHARED, false);
192   }
193
194   // S blocks X
195   {
196     SharedLock lock(f);
197     testLock(EXCLUSIVE, false);
198   }
199
200   // S does not block S
201   {
202     SharedLock lock(f);
203     testLock(SHARED, true);
204   }
205 }
206