2 * Copyright 2016 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include <folly/FileUtil.h>
18 #include <folly/detail/FileUtilDetail.h>
19 #include <folly/experimental/TestUtil.h>
23 #include <glog/logging.h>
24 #include <gtest/gtest.h>
26 #include <folly/File.h>
27 #include <folly/Range.h>
28 #include <folly/String.h>
30 namespace folly { namespace test {
32 using namespace fileutil_detail;
39 Reader(off_t offset, StringPiece data, std::deque<ssize_t> spec);
42 ssize_t operator()(int fd, void* buf, size_t count);
45 ssize_t operator()(int fd, void* buf, size_t count, off_t offset);
48 ssize_t operator()(int fd, const iovec* iov, int count);
51 ssize_t operator()(int fd, const iovec* iov, int count, off_t offset);
53 const std::deque<ssize_t> spec() const { return spec_; }
60 std::deque<ssize_t> spec_;
63 Reader::Reader(off_t offset, StringPiece data, std::deque<ssize_t> spec)
66 spec_(std::move(spec)) {
69 ssize_t Reader::nextSize() {
71 throw std::runtime_error("spec empty");
73 ssize_t n = spec_.front();
79 spec_.clear(); // so we fail if called again
86 ssize_t Reader::operator()(int /* fd */, void* buf, size_t count) {
87 ssize_t n = nextSize();
91 if (size_t(n) > count) {
92 throw std::runtime_error("requested count too small");
94 memcpy(buf, data_.data(), n);
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);
104 ssize_t Reader::operator()(int /* fd */, const iovec* iov, int count) {
105 ssize_t n = nextSize();
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);
116 if (remaining != 0) {
117 throw std::runtime_error("requested total size too small");
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);
129 class FileUtilTest : public ::testing::Test {
133 Reader reader(std::deque<ssize_t> spec);
136 std::vector<std::pair<size_t, Reader>> readers_;
139 FileUtilTest::FileUtilTest()
140 : in_("1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") {
141 CHECK_EQ(62, in_.size());
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}));
155 Reader FileUtilTest::reader(std::deque<ssize_t> spec) {
156 return Reader(42, in_, std::move(spec));
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));
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));
181 explicit IovecBuffers(std::initializer_list<size_t> sizes);
182 explicit IovecBuffers(std::vector<size_t> sizes);
184 std::vector<iovec> iov() const { return iov_; } // yes, make a copy
185 std::string join() const { return folly::join("", buffers_); }
189 std::vector<std::string> buffers_;
190 std::vector<iovec> iov_;
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'));
198 for (auto& b : buffers_) {
200 iov.iov_base = &b[0];
201 iov.iov_len = b.size();
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'));
211 for (auto& b : buffers_) {
213 iov.iov_base = &b[0];
214 iov.iov_len = b.size();
219 size_t IovecBuffers::size() const {
221 for (auto& b : buffers_) {
227 TEST_F(FileUtilTest, readv) {
228 for (auto& p : readers_) {
229 IovecBuffers buf({12, 19, 31});
230 ASSERT_EQ(62, buf.size());
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));
240 TEST(FileUtilTest2, wrapv) {
241 TemporaryFile tempFile("file-util-test");
242 std::vector<size_t> sizes;
244 for (int32_t i = 0; i < 1500; ++i) {
245 sizes.push_back(i % 3 + 1);
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()));
254 TEST_F(FileUtilTest, preadv) {
255 for (auto& p : readers_) {
256 IovecBuffers buf({12, 19, 31});
257 ASSERT_EQ(62, buf.size());
259 auto iov = buf.iov();
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));
268 TEST(String, readFile) {
269 const TemporaryFile afileTemp, emptyFileTemp;
270 auto afile = afileTemp.path();
271 auto emptyFile = emptyFileTemp.path();
273 EXPECT_TRUE(writeFile(string(), emptyFile.c_str()));
274 EXPECT_TRUE(writeFile(StringPiece("bar"), afile.c_str()));
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);
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);
300 class ReadFileFd : public ::testing::Test {
302 void SetUp() override {
303 ASSERT_TRUE(writeFile(StringPiece("bar"), aFile.path().c_str()));
309 TEST_F(ReadFileFd, ReadZeroBytes) {
310 std::string contents;
311 EXPECT_TRUE(readFile(aFile.fd(), contents, 0));
312 EXPECT_EQ("", contents);
315 TEST_F(ReadFileFd, ReadPartial) {
316 std::string contents;
317 EXPECT_TRUE(readFile(aFile.fd(), contents, 2));
318 EXPECT_EQ("ba", contents);
321 TEST_F(ReadFileFd, ReadFull) {
322 std::string contents;
323 EXPECT_TRUE(readFile(aFile.fd(), contents));
324 EXPECT_EQ("bar", contents);
327 TEST_F(ReadFileFd, WriteOnlyFd) {
328 File f(aFile.path().string(), O_WRONLY);
329 std::string contents;
330 EXPECT_FALSE(readFile(f.fd(), contents));
334 TEST_F(ReadFileFd, InvalidFd) {
335 File f(aFile.path().string());
337 std::string contents;
338 EXPECT_FALSE(readFile(f.fd(), contents));