1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/files/memory_mapped_file.h"
6
7 #include <stddef.h>
8 #include <stdint.h>
9
10 #include <utility>
11
12 #include "base/files/file_path.h"
13 #include "base/files/file_util.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 #include "testing/platform_test.h"
16
17 namespace base {
18
19 namespace {
20
21 // Create a temporary buffer and fill it with a watermark sequence.
CreateTestBuffer(size_t size,size_t offset)22 std::unique_ptr<uint8_t[]> CreateTestBuffer(size_t size, size_t offset) {
23 std::unique_ptr<uint8_t[]> buf(new uint8_t[size]);
24 for (size_t i = 0; i < size; ++i)
25 buf.get()[i] = static_cast<uint8_t>((offset + i) % 253);
26 return buf;
27 }
28
29 // Check that the watermark sequence is consistent with the |offset| provided.
CheckBufferContents(const uint8_t * data,size_t size,size_t offset)30 bool CheckBufferContents(const uint8_t* data, size_t size, size_t offset) {
31 std::unique_ptr<uint8_t[]> test_data(CreateTestBuffer(size, offset));
32 return memcmp(test_data.get(), data, size) == 0;
33 }
34
35 class MemoryMappedFileTest : public PlatformTest {
36 protected:
SetUp()37 void SetUp() override {
38 PlatformTest::SetUp();
39 CreateTemporaryFile(&temp_file_path_);
40 }
41
TearDown()42 void TearDown() override { EXPECT_TRUE(DeleteFile(temp_file_path_, false)); }
43
CreateTemporaryTestFile(size_t size)44 void CreateTemporaryTestFile(size_t size) {
45 File file(temp_file_path_,
46 File::FLAG_CREATE_ALWAYS | File::FLAG_READ | File::FLAG_WRITE);
47 EXPECT_TRUE(file.IsValid());
48
49 std::unique_ptr<uint8_t[]> test_data(CreateTestBuffer(size, 0));
50 size_t bytes_written =
51 file.Write(0, reinterpret_cast<char*>(test_data.get()), size);
52 EXPECT_EQ(size, bytes_written);
53 file.Close();
54 }
55
temp_file_path() const56 const FilePath temp_file_path() const { return temp_file_path_; }
57
58 private:
59 FilePath temp_file_path_;
60 };
61
TEST_F(MemoryMappedFileTest,MapWholeFileByPath)62 TEST_F(MemoryMappedFileTest, MapWholeFileByPath) {
63 const size_t kFileSize = 68 * 1024;
64 CreateTemporaryTestFile(kFileSize);
65 MemoryMappedFile map;
66 map.Initialize(temp_file_path());
67 ASSERT_EQ(kFileSize, map.length());
68 ASSERT_TRUE(map.data() != nullptr);
69 EXPECT_TRUE(map.IsValid());
70 ASSERT_TRUE(CheckBufferContents(map.data(), kFileSize, 0));
71 }
72
TEST_F(MemoryMappedFileTest,MapWholeFileByFD)73 TEST_F(MemoryMappedFileTest, MapWholeFileByFD) {
74 const size_t kFileSize = 68 * 1024;
75 CreateTemporaryTestFile(kFileSize);
76 MemoryMappedFile map;
77 map.Initialize(File(temp_file_path(), File::FLAG_OPEN | File::FLAG_READ));
78 ASSERT_EQ(kFileSize, map.length());
79 ASSERT_TRUE(map.data() != nullptr);
80 EXPECT_TRUE(map.IsValid());
81 ASSERT_TRUE(CheckBufferContents(map.data(), kFileSize, 0));
82 }
83
TEST_F(MemoryMappedFileTest,MapSmallFile)84 TEST_F(MemoryMappedFileTest, MapSmallFile) {
85 const size_t kFileSize = 127;
86 CreateTemporaryTestFile(kFileSize);
87 MemoryMappedFile map;
88 map.Initialize(temp_file_path());
89 ASSERT_EQ(kFileSize, map.length());
90 ASSERT_TRUE(map.data() != nullptr);
91 EXPECT_TRUE(map.IsValid());
92 ASSERT_TRUE(CheckBufferContents(map.data(), kFileSize, 0));
93 }
94
TEST_F(MemoryMappedFileTest,MapWholeFileUsingRegion)95 TEST_F(MemoryMappedFileTest, MapWholeFileUsingRegion) {
96 const size_t kFileSize = 157 * 1024;
97 CreateTemporaryTestFile(kFileSize);
98 MemoryMappedFile map;
99
100 File file(temp_file_path(), File::FLAG_OPEN | File::FLAG_READ);
101 map.Initialize(std::move(file), MemoryMappedFile::Region::kWholeFile);
102 ASSERT_EQ(kFileSize, map.length());
103 ASSERT_TRUE(map.data() != nullptr);
104 EXPECT_TRUE(map.IsValid());
105 ASSERT_TRUE(CheckBufferContents(map.data(), kFileSize, 0));
106 }
107
TEST_F(MemoryMappedFileTest,MapPartialRegionAtBeginning)108 TEST_F(MemoryMappedFileTest, MapPartialRegionAtBeginning) {
109 const size_t kFileSize = 157 * 1024;
110 const size_t kPartialSize = 4 * 1024 + 32;
111 CreateTemporaryTestFile(kFileSize);
112 MemoryMappedFile map;
113
114 File file(temp_file_path(), File::FLAG_OPEN | File::FLAG_READ);
115 MemoryMappedFile::Region region = {0, kPartialSize};
116 map.Initialize(std::move(file), region);
117 ASSERT_EQ(kPartialSize, map.length());
118 ASSERT_TRUE(map.data() != nullptr);
119 EXPECT_TRUE(map.IsValid());
120 ASSERT_TRUE(CheckBufferContents(map.data(), kPartialSize, 0));
121 }
122
TEST_F(MemoryMappedFileTest,MapPartialRegionAtEnd)123 TEST_F(MemoryMappedFileTest, MapPartialRegionAtEnd) {
124 const size_t kFileSize = 157 * 1024;
125 const size_t kPartialSize = 5 * 1024 - 32;
126 const size_t kOffset = kFileSize - kPartialSize;
127 CreateTemporaryTestFile(kFileSize);
128 MemoryMappedFile map;
129
130 File file(temp_file_path(), File::FLAG_OPEN | File::FLAG_READ);
131 MemoryMappedFile::Region region = {kOffset, kPartialSize};
132 map.Initialize(std::move(file), region);
133 ASSERT_EQ(kPartialSize, map.length());
134 ASSERT_TRUE(map.data() != nullptr);
135 EXPECT_TRUE(map.IsValid());
136 ASSERT_TRUE(CheckBufferContents(map.data(), kPartialSize, kOffset));
137 }
138
TEST_F(MemoryMappedFileTest,MapSmallPartialRegionInTheMiddle)139 TEST_F(MemoryMappedFileTest, MapSmallPartialRegionInTheMiddle) {
140 const size_t kFileSize = 157 * 1024;
141 const size_t kOffset = 1024 * 5 + 32;
142 const size_t kPartialSize = 8;
143
144 CreateTemporaryTestFile(kFileSize);
145 MemoryMappedFile map;
146
147 File file(temp_file_path(), File::FLAG_OPEN | File::FLAG_READ);
148 MemoryMappedFile::Region region = {kOffset, kPartialSize};
149 map.Initialize(std::move(file), region);
150 ASSERT_EQ(kPartialSize, map.length());
151 ASSERT_TRUE(map.data() != nullptr);
152 EXPECT_TRUE(map.IsValid());
153 ASSERT_TRUE(CheckBufferContents(map.data(), kPartialSize, kOffset));
154 }
155
TEST_F(MemoryMappedFileTest,MapLargePartialRegionInTheMiddle)156 TEST_F(MemoryMappedFileTest, MapLargePartialRegionInTheMiddle) {
157 const size_t kFileSize = 157 * 1024;
158 const size_t kOffset = 1024 * 5 + 32;
159 const size_t kPartialSize = 16 * 1024 - 32;
160
161 CreateTemporaryTestFile(kFileSize);
162 MemoryMappedFile map;
163
164 File file(temp_file_path(), File::FLAG_OPEN | File::FLAG_READ);
165 MemoryMappedFile::Region region = {kOffset, kPartialSize};
166 map.Initialize(std::move(file), region);
167 ASSERT_EQ(kPartialSize, map.length());
168 ASSERT_TRUE(map.data() != nullptr);
169 EXPECT_TRUE(map.IsValid());
170 ASSERT_TRUE(CheckBufferContents(map.data(), kPartialSize, kOffset));
171 }
172
TEST_F(MemoryMappedFileTest,WriteableFile)173 TEST_F(MemoryMappedFileTest, WriteableFile) {
174 const size_t kFileSize = 127;
175 CreateTemporaryTestFile(kFileSize);
176
177 {
178 MemoryMappedFile map;
179 map.Initialize(temp_file_path(), MemoryMappedFile::READ_WRITE);
180 ASSERT_EQ(kFileSize, map.length());
181 ASSERT_TRUE(map.data() != nullptr);
182 EXPECT_TRUE(map.IsValid());
183 ASSERT_TRUE(CheckBufferContents(map.data(), kFileSize, 0));
184
185 uint8_t* bytes = map.data();
186 bytes[0] = 'B';
187 bytes[1] = 'a';
188 bytes[2] = 'r';
189 bytes[kFileSize - 1] = '!';
190 EXPECT_FALSE(CheckBufferContents(map.data(), kFileSize, 0));
191 EXPECT_TRUE(CheckBufferContents(map.data() + 3, kFileSize - 4, 3));
192 }
193
194 int64_t file_size;
195 ASSERT_TRUE(GetFileSize(temp_file_path(), &file_size));
196 EXPECT_EQ(static_cast<int64_t>(kFileSize), file_size);
197
198 std::string contents;
199 ASSERT_TRUE(ReadFileToString(temp_file_path(), &contents));
200 EXPECT_EQ("Bar", contents.substr(0, 3));
201 EXPECT_EQ("!", contents.substr(kFileSize - 1, 1));
202 }
203
TEST_F(MemoryMappedFileTest,ExtendableFile)204 TEST_F(MemoryMappedFileTest, ExtendableFile) {
205 const size_t kFileSize = 127;
206 const size_t kFileExtend = 100;
207 CreateTemporaryTestFile(kFileSize);
208
209 {
210 File file(temp_file_path(),
211 File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WRITE);
212 MemoryMappedFile::Region region = {0, kFileSize + kFileExtend};
213 MemoryMappedFile map;
214 map.Initialize(std::move(file), region,
215 MemoryMappedFile::READ_WRITE_EXTEND);
216 EXPECT_EQ(kFileSize + kFileExtend, map.length());
217 ASSERT_TRUE(map.data() != nullptr);
218 EXPECT_TRUE(map.IsValid());
219 ASSERT_TRUE(CheckBufferContents(map.data(), kFileSize, 0));
220
221 uint8_t* bytes = map.data();
222 EXPECT_EQ(0, bytes[kFileSize + 0]);
223 EXPECT_EQ(0, bytes[kFileSize + 1]);
224 EXPECT_EQ(0, bytes[kFileSize + 2]);
225 bytes[kFileSize + 0] = 'B';
226 bytes[kFileSize + 1] = 'A';
227 bytes[kFileSize + 2] = 'Z';
228 EXPECT_TRUE(CheckBufferContents(map.data(), kFileSize, 0));
229 }
230
231 int64_t file_size;
232 ASSERT_TRUE(GetFileSize(temp_file_path(), &file_size));
233 EXPECT_LE(static_cast<int64_t>(kFileSize + 3), file_size);
234 EXPECT_GE(static_cast<int64_t>(kFileSize + kFileExtend), file_size);
235
236 std::string contents;
237 ASSERT_TRUE(ReadFileToString(temp_file_path(), &contents));
238 EXPECT_EQ("BAZ", contents.substr(kFileSize, 3));
239 }
240
241 } // namespace
242
243 } // namespace base
244