2 * Copyright 2014 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>
22 #include <glog/logging.h>
23 #include <gflags/gflags.h>
24 #include <gtest/gtest.h>
26 #include <folly/Benchmark.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();
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()));
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)));
174 EXPECT_EQ(in_.substr(0, p.first), out.substr(0, p.first));
181 explicit IovecBuffers(std::initializer_list<size_t> sizes);
183 std::vector<iovec> iov() const { return iov_; } // yes, make a copy
184 std::string join() const { return folly::join("", buffers_); }
188 std::vector<std::string> buffers_;
189 std::vector<iovec> iov_;
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'));
197 for (auto& b : buffers_) {
199 iov.iov_base = &b[0];
200 iov.iov_len = b.size();
205 size_t IovecBuffers::size() const {
207 for (auto& b : buffers_) {
213 TEST_F(FileUtilTest, readv) {
214 for (auto& p : readers_) {
215 IovecBuffers buf({12, 19, 31});
216 ASSERT_EQ(62, buf.size());
218 auto iov = buf.iov();
219 EXPECT_EQ(p.first, wrapvFull(p.second, 0, iov.data(), iov.size()));
221 EXPECT_EQ(in_.substr(0, p.first), buf.join().substr(0, p.first));
226 #if FOLLY_HAVE_PREADV
227 TEST_F(FileUtilTest, preadv) {
228 for (auto& p : readers_) {
229 IovecBuffers buf({12, 19, 31});
230 ASSERT_EQ(62, buf.size());
232 auto iov = buf.iov();
234 wrapvFull(p.second, 0, iov.data(), iov.size(), off_t(42)));
236 EXPECT_EQ(in_.substr(0, p.first), buf.join().substr(0, p.first));
242 TEST(String, readFile) {
243 srand(time(nullptr));
244 const string tmpPrefix = to<string>("/tmp/folly-file-util-test-",
245 getpid(), "-", rand(), "-");
246 const string afile = tmpPrefix + "myfile";
247 const string emptyFile = tmpPrefix + "myfile2";
250 unlink(afile.c_str());
251 unlink(emptyFile.c_str());
254 auto f = fopen(emptyFile.c_str(), "wb");
255 EXPECT_NE(nullptr, f);
256 EXPECT_EQ(0, fclose(f));
257 f = fopen(afile.c_str(), "wb");
258 EXPECT_NE(nullptr, f);
259 EXPECT_EQ(3, fwrite("bar", 1, 3, f));
260 EXPECT_EQ(0, fclose(f));
264 EXPECT_TRUE(readFile(emptyFile.c_str(), contents));
265 EXPECT_EQ(contents, "");
266 EXPECT_TRUE(readFile(afile.c_str(), contents, 0));
267 EXPECT_EQ("", contents);
268 EXPECT_TRUE(readFile(afile.c_str(), contents, 2));
269 EXPECT_EQ("ba", contents);
270 EXPECT_TRUE(readFile(afile.c_str(), contents));
271 EXPECT_EQ("bar", contents);
274 vector<unsigned char> contents;
275 EXPECT_TRUE(readFile(emptyFile.c_str(), contents));
276 EXPECT_EQ(vector<unsigned char>(), contents);
277 EXPECT_TRUE(readFile(afile.c_str(), contents, 0));
278 EXPECT_EQ(vector<unsigned char>(), contents);
279 EXPECT_TRUE(readFile(afile.c_str(), contents, 2));
280 EXPECT_EQ(vector<unsigned char>({'b', 'a'}), contents);
281 EXPECT_TRUE(readFile(afile.c_str(), contents));
282 EXPECT_EQ(vector<unsigned char>({'b', 'a', 'r'}), contents);
288 int main(int argc, char *argv[]) {
289 testing::InitGoogleTest(&argc, argv);
290 gflags::ParseCommandLineFlags(&argc, &argv, true);
291 return RUN_ALL_TESTS();