6526efa2facb5e81b3a6540e8b037f3e6ba3d9a4
[folly.git] / folly / test / FileUtilTest.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/FileUtil.h>
18 #include <folly/detail/FileUtilDetail.h>
19 #include <folly/experimental/TestUtil.h>
20
21 #include <deque>
22
23 #include <glog/logging.h>
24 #include <gtest/gtest.h>
25
26 #include <folly/File.h>
27 #include <folly/Range.h>
28 #include <folly/String.h>
29
30 namespace folly { namespace test {
31
32 using namespace fileutil_detail;
33 using namespace std;
34
35 namespace {
36
37 class Reader {
38  public:
39   Reader(off_t offset, StringPiece data, std::deque<ssize_t> spec);
40
41   // write-like
42   ssize_t operator()(int fd, void* buf, size_t count);
43
44   // pwrite-like
45   ssize_t operator()(int fd, void* buf, size_t count, off_t offset);
46
47   // writev-like
48   ssize_t operator()(int fd, const iovec* iov, int count);
49
50   // pwritev-like
51   ssize_t operator()(int fd, const iovec* iov, int count, off_t offset);
52
53   const std::deque<ssize_t> spec() const { return spec_; }
54
55  private:
56   ssize_t nextSize();
57
58   off_t offset_;
59   StringPiece data_;
60   std::deque<ssize_t> spec_;
61 };
62
63 Reader::Reader(off_t offset, StringPiece data, std::deque<ssize_t> spec)
64   : offset_(offset),
65     data_(data),
66     spec_(std::move(spec)) {
67 }
68
69 ssize_t Reader::nextSize() {
70   if (spec_.empty()) {
71     throw std::runtime_error("spec empty");
72   }
73   ssize_t n = spec_.front();
74   spec_.pop_front();
75   if (n <= 0) {
76     if (n == -1) {
77       errno = EIO;
78     }
79     spec_.clear();  // so we fail if called again
80   } else {
81     offset_ += n;
82   }
83   return n;
84 }
85
86 ssize_t Reader::operator()(int /* fd */, void* buf, size_t count) {
87   ssize_t n = nextSize();
88   if (n <= 0) {
89     return n;
90   }
91   if (size_t(n) > count) {
92     throw std::runtime_error("requested count too small");
93   }
94   memcpy(buf, data_.data(), n);
95   data_.advance(n);
96   return n;
97 }
98
99 ssize_t Reader::operator()(int fd, void* buf, size_t count, off_t offset) {
100   EXPECT_EQ(offset_, offset);
101   return operator()(fd, buf, count);
102 }
103
104 ssize_t Reader::operator()(int /* fd */, const iovec* iov, int count) {
105   ssize_t n = nextSize();
106   if (n <= 0) {
107     return n;
108   }
109   ssize_t remaining = n;
110   for (; count != 0 && remaining != 0; ++iov, --count) {
111     ssize_t len = std::min(remaining, ssize_t(iov->iov_len));
112     memcpy(iov->iov_base, data_.data(), len);
113     data_.advance(len);
114     remaining -= len;
115   }
116   if (remaining != 0) {
117     throw std::runtime_error("requested total size too small");
118   }
119   return n;
120 }
121
122 ssize_t Reader::operator()(int fd, const iovec* iov, int count, off_t offset) {
123   EXPECT_EQ(offset_, offset);
124   return operator()(fd, iov, count);
125 }
126
127 }  // namespace
128
129 class FileUtilTest : public ::testing::Test {
130  protected:
131   FileUtilTest();
132
133   Reader reader(std::deque<ssize_t> spec);
134
135   std::string in_;
136   std::vector<std::pair<size_t, Reader>> readers_;
137 };
138
139 FileUtilTest::FileUtilTest()
140   : in_("1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") {
141   CHECK_EQ(62, in_.size());
142
143   readers_.emplace_back(0, reader({0}));
144   readers_.emplace_back(62, reader({62}));
145   readers_.emplace_back(62, reader({62, -1}));  // error after end (not called)
146   readers_.emplace_back(61, reader({61, 0}));
147   readers_.emplace_back(-1, reader({61, -1}));  // error before end
148   readers_.emplace_back(62, reader({31, 31}));
149   readers_.emplace_back(62, reader({1, 10, 20, 10, 1, 20}));
150   readers_.emplace_back(61, reader({1, 10, 20, 10, 20, 0}));
151   readers_.emplace_back(41, reader({1, 10, 20, 10, 0}));
152   readers_.emplace_back(-1, reader({1, 10, 20, 10, 20, -1}));
153 }
154
155 Reader FileUtilTest::reader(std::deque<ssize_t> spec) {
156   return Reader(42, in_, std::move(spec));
157 }
158
159 TEST_F(FileUtilTest, read) {
160   for (auto& p : readers_) {
161     std::string out(in_.size(), '\0');
162     EXPECT_EQ(p.first, wrapFull(p.second, 0, &out[0], out.size()));
163     if (p.first != (decltype(p.first))(-1)) {
164       EXPECT_EQ(in_.substr(0, p.first), out.substr(0, p.first));
165     }
166   }
167 }
168
169 TEST_F(FileUtilTest, pread) {
170   for (auto& p : readers_) {
171     std::string out(in_.size(), '\0');
172     EXPECT_EQ(p.first, wrapFull(p.second, 0, &out[0], out.size(), off_t(42)));
173     if (p.first != (decltype(p.first))(-1)) {
174       EXPECT_EQ(in_.substr(0, p.first), out.substr(0, p.first));
175     }
176   }
177 }
178
179 class IovecBuffers {
180  public:
181   explicit IovecBuffers(std::initializer_list<size_t> sizes);
182   explicit IovecBuffers(std::vector<size_t> sizes);
183
184   std::vector<iovec> iov() const { return iov_; }  // yes, make a copy
185   std::string join() const { return folly::join("", buffers_); }
186   size_t size() const;
187
188  private:
189   std::vector<std::string> buffers_;
190   std::vector<iovec> iov_;
191 };
192
193 IovecBuffers::IovecBuffers(std::initializer_list<size_t> sizes) {
194   iov_.reserve(sizes.size());
195   for (auto& s : sizes) {
196     buffers_.push_back(std::string(s, '\0'));
197   }
198   for (auto& b : buffers_) {
199     iovec iov;
200     iov.iov_base = &b[0];
201     iov.iov_len = b.size();
202     iov_.push_back(iov);
203   }
204 }
205
206 IovecBuffers::IovecBuffers(std::vector<size_t> sizes) {
207   iov_.reserve(sizes.size());
208   for (auto s : sizes) {
209     buffers_.push_back(std::string(s, '\0'));
210   }
211   for (auto& b : buffers_) {
212     iovec iov;
213     iov.iov_base = &b[0];
214     iov.iov_len = b.size();
215     iov_.push_back(iov);
216   }
217 }
218
219 size_t IovecBuffers::size() const {
220   size_t s = 0;
221   for (auto& b : buffers_) {
222     s += b.size();
223   }
224   return s;
225 }
226
227 TEST_F(FileUtilTest, readv) {
228   for (auto& p : readers_) {
229     IovecBuffers buf({12, 19, 31});
230     ASSERT_EQ(62, buf.size());
231
232     auto iov = buf.iov();
233     EXPECT_EQ(p.first, wrapvFull(p.second, 0, iov.data(), iov.size()));
234     if (p.first != (decltype(p.first))(-1)) {
235       EXPECT_EQ(in_.substr(0, p.first), buf.join().substr(0, p.first));
236     }
237   }
238 }
239
240 TEST(FileUtilTest2, wrapv) {
241   TemporaryFile tempFile("file-util-test");
242   std::vector<size_t> sizes;
243   size_t sum = 0;
244   for (int32_t i = 0; i < 1500; ++i) {
245     sizes.push_back(i % 3 + 1);
246     sum += sizes.back();
247   }
248   IovecBuffers buf(sizes);
249   ASSERT_EQ(sum, buf.size());
250   auto iov = buf.iov();
251   EXPECT_EQ(sum, wrapvFull(writev, tempFile.fd(), iov.data(), iov.size()));
252 }
253
254 TEST_F(FileUtilTest, preadv) {
255   for (auto& p : readers_) {
256     IovecBuffers buf({12, 19, 31});
257     ASSERT_EQ(62, buf.size());
258
259     auto iov = buf.iov();
260     EXPECT_EQ(p.first,
261               wrapvFull(p.second, 0, iov.data(), iov.size(), off_t(42)));
262     if (p.first != (decltype(p.first))(-1)) {
263       EXPECT_EQ(in_.substr(0, p.first), buf.join().substr(0, p.first));
264     }
265   }
266 }
267
268 TEST(String, readFile) {
269   const TemporaryFile afileTemp, emptyFileTemp;
270   auto afile = afileTemp.path().string();
271   auto emptyFile = emptyFileTemp.path().string();
272
273   EXPECT_TRUE(writeFile(string(), emptyFile.c_str()));
274   EXPECT_TRUE(writeFile(StringPiece("bar"), afile.c_str()));
275
276   {
277     string contents;
278     EXPECT_TRUE(readFile(emptyFile.c_str(), contents));
279     EXPECT_EQ(contents, "");
280     EXPECT_TRUE(readFile(afile.c_str(), contents, 0));
281     EXPECT_EQ("", contents);
282     EXPECT_TRUE(readFile(afile.c_str(), contents, 2));
283     EXPECT_EQ("ba", contents);
284     EXPECT_TRUE(readFile(afile.c_str(), contents));
285     EXPECT_EQ("bar", contents);
286   }
287   {
288     vector<unsigned char> contents;
289     EXPECT_TRUE(readFile(emptyFile.c_str(), contents));
290     EXPECT_EQ(vector<unsigned char>(), contents);
291     EXPECT_TRUE(readFile(afile.c_str(), contents, 0));
292     EXPECT_EQ(vector<unsigned char>(), contents);
293     EXPECT_TRUE(readFile(afile.c_str(), contents, 2));
294     EXPECT_EQ(vector<unsigned char>({'b', 'a'}), contents);
295     EXPECT_TRUE(readFile(afile.c_str(), contents));
296     EXPECT_EQ(vector<unsigned char>({'b', 'a', 'r'}), contents);
297   }
298 }
299
300 class ReadFileFd : public ::testing::Test {
301  protected:
302   void SetUp() override {
303     ASSERT_TRUE(writeFile(StringPiece("bar"), aFile.path().string().c_str()));
304   }
305
306   TemporaryFile aFile;
307 };
308
309 TEST_F(ReadFileFd, ReadZeroBytes) {
310   std::string contents;
311   EXPECT_TRUE(readFile(aFile.fd(), contents, 0));
312   EXPECT_EQ("", contents);
313 }
314
315 TEST_F(ReadFileFd, ReadPartial) {
316   std::string contents;
317   EXPECT_TRUE(readFile(aFile.fd(), contents, 2));
318   EXPECT_EQ("ba", contents);
319 }
320
321 TEST_F(ReadFileFd, ReadFull) {
322   std::string contents;
323   EXPECT_TRUE(readFile(aFile.fd(), contents));
324   EXPECT_EQ("bar", contents);
325 }
326
327 TEST_F(ReadFileFd, WriteOnlyFd) {
328   File f(aFile.path().string(), O_WRONLY);
329   std::string contents;
330   EXPECT_FALSE(readFile(f.fd(), contents));
331   PLOG(INFO);
332 }
333
334 TEST_F(ReadFileFd, InvalidFd) {
335   File f(aFile.path().string());
336   f.close();
337   std::string contents;
338   msvcSuppressAbortOnInvalidParams([&] {
339     EXPECT_FALSE(readFile(f.fd(), contents));
340   });
341   PLOG(INFO);
342 }
343 }}  // namespaces