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/Range.h>
27 #include <folly/String.h>
29 namespace folly { namespace test {
31 using namespace fileutil_detail;
38 Reader(off_t offset, StringPiece data, std::deque<ssize_t> spec);
41 ssize_t operator()(int fd, void* buf, size_t count);
44 ssize_t operator()(int fd, void* buf, size_t count, off_t offset);
47 ssize_t operator()(int fd, const iovec* iov, int count);
50 ssize_t operator()(int fd, const iovec* iov, int count, off_t offset);
52 const std::deque<ssize_t> spec() const { return spec_; }
59 std::deque<ssize_t> spec_;
62 Reader::Reader(off_t offset, StringPiece data, std::deque<ssize_t> spec)
65 spec_(std::move(spec)) {
68 ssize_t Reader::nextSize() {
70 throw std::runtime_error("spec empty");
72 ssize_t n = spec_.front();
78 spec_.clear(); // so we fail if called again
85 ssize_t Reader::operator()(int /* fd */, void* buf, size_t count) {
86 ssize_t n = nextSize();
90 if (size_t(n) > count) {
91 throw std::runtime_error("requested count too small");
93 memcpy(buf, data_.data(), n);
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);
103 ssize_t Reader::operator()(int /* fd */, const iovec* iov, int count) {
104 ssize_t n = nextSize();
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);
115 if (remaining != 0) {
116 throw std::runtime_error("requested total size too small");
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);
128 class FileUtilTest : public ::testing::Test {
132 Reader reader(std::deque<ssize_t> spec);
135 std::vector<std::pair<size_t, Reader>> readers_;
138 FileUtilTest::FileUtilTest()
139 : in_("1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") {
140 CHECK_EQ(62, in_.size());
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}));
154 Reader FileUtilTest::reader(std::deque<ssize_t> spec) {
155 return Reader(42, in_, std::move(spec));
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 != (typeof(p.first))(-1)) {
163 EXPECT_EQ(in_.substr(0, p.first), out.substr(0, p.first));
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 != (typeof(p.first))(-1)) {
173 EXPECT_EQ(in_.substr(0, p.first), out.substr(0, p.first));
180 explicit IovecBuffers(std::initializer_list<size_t> sizes);
181 explicit IovecBuffers(std::vector<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 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'));
210 for (auto& b : buffers_) {
212 iov.iov_base = &b[0];
213 iov.iov_len = b.size();
218 size_t IovecBuffers::size() const {
220 for (auto& b : buffers_) {
226 TEST_F(FileUtilTest, readv) {
227 for (auto& p : readers_) {
228 IovecBuffers buf({12, 19, 31});
229 ASSERT_EQ(62, buf.size());
231 auto iov = buf.iov();
232 EXPECT_EQ(p.first, wrapvFull(p.second, 0, iov.data(), iov.size()));
233 if (p.first != (typeof(p.first))(-1)) {
234 EXPECT_EQ(in_.substr(0, p.first), buf.join().substr(0, p.first));
239 TEST(FileUtilTest2, wrapv) {
240 TemporaryFile tempFile("file-util-test");
241 std::vector<size_t> sizes;
243 for (int32_t i = 0; i < 1500; ++i) {
244 sizes.push_back(i % 3 + 1);
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()));
253 #if FOLLY_HAVE_PREADV
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 != (typeof(p.first))(-1)) {
263 EXPECT_EQ(in_.substr(0, p.first), buf.join().substr(0, p.first));
269 TEST(String, readFile) {
270 srand(time(nullptr));
271 const string tmpPrefix = to<string>("/tmp/folly-file-util-test-",
272 getpid(), "-", rand(), "-");
273 const string afile = tmpPrefix + "myfile";
274 const string emptyFile = tmpPrefix + "myfile2";
277 unlink(afile.c_str());
278 unlink(emptyFile.c_str());
281 EXPECT_TRUE(writeFile(string(), emptyFile.c_str()));
282 EXPECT_TRUE(writeFile(StringPiece("bar"), afile.c_str()));
286 EXPECT_TRUE(readFile(emptyFile.c_str(), contents));
287 EXPECT_EQ(contents, "");
288 EXPECT_TRUE(readFile(afile.c_str(), contents, 0));
289 EXPECT_EQ("", contents);
290 EXPECT_TRUE(readFile(afile.c_str(), contents, 2));
291 EXPECT_EQ("ba", contents);
292 EXPECT_TRUE(readFile(afile.c_str(), contents));
293 EXPECT_EQ("bar", contents);
296 vector<unsigned char> contents;
297 EXPECT_TRUE(readFile(emptyFile.c_str(), contents));
298 EXPECT_EQ(vector<unsigned char>(), contents);
299 EXPECT_TRUE(readFile(afile.c_str(), contents, 0));
300 EXPECT_EQ(vector<unsigned char>(), contents);
301 EXPECT_TRUE(readFile(afile.c_str(), contents, 2));
302 EXPECT_EQ(vector<unsigned char>({'b', 'a'}), contents);
303 EXPECT_TRUE(readFile(afile.c_str(), contents));
304 EXPECT_EQ(vector<unsigned char>({'b', 'a', 'r'}), contents);