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