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 <gflags/gflags.h>
25 #include <gtest/gtest.h>
27 #include <folly/Benchmark.h>
28 #include <folly/Range.h>
29 #include <folly/String.h>
31 namespace folly { namespace test {
33 using namespace fileutil_detail;
40 Reader(off_t offset, StringPiece data, std::deque<ssize_t> spec);
43 ssize_t operator()(int fd, void* buf, size_t count);
46 ssize_t operator()(int fd, void* buf, size_t count, off_t offset);
49 ssize_t operator()(int fd, const iovec* iov, int count);
52 ssize_t operator()(int fd, const iovec* iov, int count, off_t offset);
54 const std::deque<ssize_t> spec() const { return spec_; }
61 std::deque<ssize_t> spec_;
64 Reader::Reader(off_t offset, StringPiece data, std::deque<ssize_t> spec)
67 spec_(std::move(spec)) {
70 ssize_t Reader::nextSize() {
72 throw std::runtime_error("spec empty");
74 ssize_t n = spec_.front();
80 spec_.clear(); // so we fail if called again
87 ssize_t Reader::operator()(int /* fd */, void* buf, size_t count) {
88 ssize_t n = nextSize();
92 if (size_t(n) > count) {
93 throw std::runtime_error("requested count too small");
95 memcpy(buf, data_.data(), n);
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);
105 ssize_t Reader::operator()(int /* fd */, const iovec* iov, int count) {
106 ssize_t n = nextSize();
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);
117 if (remaining != 0) {
118 throw std::runtime_error("requested total size too small");
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);
130 class FileUtilTest : public ::testing::Test {
134 Reader reader(std::deque<ssize_t> spec);
137 std::vector<std::pair<size_t, Reader>> readers_;
140 FileUtilTest::FileUtilTest()
141 : in_("1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") {
142 CHECK_EQ(62, in_.size());
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}));
156 Reader FileUtilTest::reader(std::deque<ssize_t> spec) {
157 return Reader(42, in_, std::move(spec));
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));
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));
182 explicit IovecBuffers(std::initializer_list<size_t> sizes);
183 explicit IovecBuffers(std::vector<size_t> sizes);
185 std::vector<iovec> iov() const { return iov_; } // yes, make a copy
186 std::string join() const { return folly::join("", buffers_); }
190 std::vector<std::string> buffers_;
191 std::vector<iovec> iov_;
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'));
199 for (auto& b : buffers_) {
201 iov.iov_base = &b[0];
202 iov.iov_len = b.size();
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'));
212 for (auto& b : buffers_) {
214 iov.iov_base = &b[0];
215 iov.iov_len = b.size();
220 size_t IovecBuffers::size() const {
222 for (auto& b : buffers_) {
228 TEST_F(FileUtilTest, readv) {
229 for (auto& p : readers_) {
230 IovecBuffers buf({12, 19, 31});
231 ASSERT_EQ(62, buf.size());
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));
241 TEST(FileUtilTest2, wrapv) {
242 TemporaryFile tempFile("file-util-test");
243 std::vector<size_t> sizes;
245 for (int32_t i = 0; i < 1500; ++i) {
246 sizes.push_back(i % 3 + 1);
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()));
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());
261 auto iov = buf.iov();
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));
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";
279 unlink(afile.c_str());
280 unlink(emptyFile.c_str());
283 EXPECT_TRUE(writeFile(string(), emptyFile.c_str()));
284 EXPECT_TRUE(writeFile(StringPiece("bar"), afile.c_str()));
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);
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);
312 int main(int argc, char *argv[]) {
313 testing::InitGoogleTest(&argc, argv);
314 gflags::ParseCommandLineFlags(&argc, &argv, true);
315 return RUN_ALL_TESTS();