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