43eb5a1516d2f41410d96e5984f234e8df9cc165
[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 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   fs::path helper(buf);
151   helper.remove_filename();
152   helper /= "file_test_lock_helper";
153
154   TemporaryFile tempFile;
155   File f(tempFile.fd());
156
157   enum LockMode { EXCLUSIVE, SHARED };
158   auto testLock = [&] (LockMode mode, bool expectedSuccess) {
159     auto ret =
160       Subprocess({helper.native(),
161                   mode == SHARED ? "-s" : "-x",
162                   tempFile.path().native()}).wait();
163     EXPECT_TRUE(ret.exited());
164     if (ret.exited()) {
165       EXPECT_EQ(expectedSuccess ? 0 : 42, ret.exitStatus());
166     }
167   };
168
169   // Make sure nothing breaks and things compile.
170   {
171     Lock lock(f);
172   }
173
174   {
175     SharedLock lock(f);
176   }
177
178   {
179     Lock lock(f, std::defer_lock);
180     EXPECT_TRUE(lock.try_lock());
181   }
182
183   {
184     SharedLock lock(f, boost::defer_lock);
185     EXPECT_TRUE(lock.try_lock());
186   }
187
188   // X blocks X
189   {
190     Lock lock(f);
191     testLock(EXCLUSIVE, false);
192   }
193
194   // X blocks S
195   {
196     Lock lock(f);
197     testLock(SHARED, false);
198   }
199
200   // S blocks X
201   {
202     SharedLock lock(f);
203     testLock(EXCLUSIVE, false);
204   }
205
206   // S does not block S
207   {
208     SharedLock lock(f);
209     testLock(SHARED, true);
210   }
211 }
212