Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
file_unittest.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter 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 <cstring>
6#include <memory>
7#include <vector>
8
9#include "flutter/fml/build_config.h"
10#include "flutter/fml/file.h"
11#include "flutter/fml/mapping.h"
12#include "flutter/fml/paths.h"
13#include "flutter/fml/unique_fd.h"
14#include "gtest/gtest.h"
15
16static bool WriteStringToFile(const fml::UniqueFD& fd,
17 const std::string& contents) {
18 if (!fml::TruncateFile(fd, contents.size())) {
19 return false;
20 }
21
23 if (mapping.GetSize() != contents.size()) {
24 return false;
25 }
26
27 if (mapping.GetMutableMapping() == nullptr) {
28 return false;
29 }
30
31 ::memmove(mapping.GetMutableMapping(), contents.data(), contents.size());
32 return true;
33}
34
35static std::string ReadStringFromFile(const fml::UniqueFD& fd) {
36 fml::FileMapping mapping(fd);
37
38 if (mapping.GetMapping() == nullptr) {
39 return nullptr;
40 }
41
42 return {reinterpret_cast<const char*>(mapping.GetMapping()),
43 mapping.GetSize()};
44}
45
46TEST(FileTest, CreateTemporaryAndUnlink) {
47 auto dir_name = fml::CreateTemporaryDirectory();
48 ASSERT_NE(dir_name, "");
49 auto dir =
50 fml::OpenDirectory(dir_name.c_str(), false, fml::FilePermission::kRead);
51 ASSERT_TRUE(dir.is_valid());
52 dir.reset();
53 ASSERT_TRUE(fml::UnlinkDirectory(dir_name.c_str()));
54}
55
56TEST(FileTest, ScopedTempDirIsValid) {
58 ASSERT_TRUE(dir.fd().is_valid());
59}
60
61TEST(FileTest, CanOpenFileForWriting) {
63 ASSERT_TRUE(dir.fd().is_valid());
64
65 auto fd =
66 fml::OpenFile(dir.fd(), "some.txt", true, fml::FilePermission::kWrite);
67 ASSERT_TRUE(fd.is_valid());
68 fd.reset();
69 ASSERT_TRUE(fml::UnlinkFile(dir.fd(), "some.txt"));
70}
71
72TEST(FileTest, CanTruncateAndWrite) {
74 ASSERT_TRUE(dir.fd().is_valid());
75
76 std::string contents = "some contents here";
77
78 // On the first iteration, this tests writing and then reading a file that
79 // didn't exist yet. On the second iteration it tests truncating, writing,
80 // and reading a file that already existed.
81 for (int i = 0; i < 2; i++) {
82 {
83 auto fd = fml::OpenFile(dir.fd(), "some.txt", true,
85 ASSERT_TRUE(fd.is_valid());
86
87 ASSERT_TRUE(fml::TruncateFile(fd, contents.size()));
88
90 ASSERT_EQ(mapping.GetSize(), contents.size());
91 ASSERT_NE(mapping.GetMutableMapping(), nullptr);
92
93 ::memcpy(mapping.GetMutableMapping(), contents.data(), contents.size());
94 }
95
96 {
97 auto fd = fml::OpenFile(dir.fd(), "some.txt", false,
99 ASSERT_TRUE(fd.is_valid());
100
101 fml::FileMapping mapping(fd);
102 ASSERT_EQ(mapping.GetSize(), contents.size());
103
104 ASSERT_EQ(
105 0, ::memcmp(mapping.GetMapping(), contents.data(), contents.size()));
106 }
107 }
108
109 fml::UnlinkFile(dir.fd(), "some.txt");
110}
111
112TEST(FileTest, CreateDirectoryStructure) {
114
115 std::string contents = "These are my contents";
116 {
117 auto sub = fml::CreateDirectory(dir.fd(), {"a", "b", "c"},
119 ASSERT_TRUE(sub.is_valid());
120 auto file = fml::OpenFile(sub, "my_contents", true,
122 ASSERT_TRUE(file.is_valid());
123 ASSERT_TRUE(WriteStringToFile(file, contents));
124 }
125
126 const char* file_path = "a/b/c/my_contents";
127
128 {
129 auto contents_file =
130 fml::OpenFile(dir.fd(), file_path, false, fml::FilePermission::kRead);
131 ASSERT_EQ(ReadStringFromFile(contents_file), contents);
132 }
133
134 // Cleanup.
135 ASSERT_TRUE(fml::UnlinkFile(dir.fd(), file_path));
136 ASSERT_TRUE(fml::UnlinkDirectory(dir.fd(), "a/b/c"));
137 ASSERT_TRUE(fml::UnlinkDirectory(dir.fd(), "a/b"));
138 ASSERT_TRUE(fml::UnlinkDirectory(dir.fd(), "a"));
139}
140
141TEST(FileTest, VisitFilesCanBeCalledTwice) {
143
144 {
145 auto file = fml::OpenFile(dir.fd(), "my_contents", true,
147 ASSERT_TRUE(file.is_valid());
148 }
149
150 int count;
151 fml::FileVisitor count_visitor = [&count](const fml::UniqueFD& directory,
152 const std::string& filename) {
153 count += 1;
154 return true;
155 };
156 count = 0;
157 fml::VisitFiles(dir.fd(), count_visitor);
158 ASSERT_EQ(count, 1);
159
160 // Without `rewinddir` in `VisitFiles`, the following check would fail.
161 count = 0;
162 fml::VisitFiles(dir.fd(), count_visitor);
163 ASSERT_EQ(count, 1);
164
165 ASSERT_TRUE(fml::UnlinkFile(dir.fd(), "my_contents"));
166}
167
168TEST(FileTest, CanListFilesRecursively) {
170
171 {
172 auto c = fml::CreateDirectory(dir.fd(), {"a", "b", "c"},
174 ASSERT_TRUE(c.is_valid());
175 auto file1 =
177 auto file2 =
180 ASSERT_TRUE(d.is_valid());
181 auto file3 =
183 ASSERT_TRUE(file1.is_valid());
184 ASSERT_TRUE(file2.is_valid());
185 ASSERT_TRUE(file3.is_valid());
186 }
187
188 std::set<std::string> names;
189 fml::FileVisitor visitor = [&names](const fml::UniqueFD& directory,
190 const std::string& filename) {
191 names.insert(filename);
192 return true;
193 };
194
195 fml::VisitFilesRecursively(dir.fd(), visitor);
196 ASSERT_EQ(names, std::set<std::string>(
197 {"a", "b", "c", "d", "file1", "file2", "file3"}));
198
199 // Cleanup.
200 ASSERT_TRUE(fml::UnlinkFile(dir.fd(), "a/b/c/d/file3"));
201 ASSERT_TRUE(fml::UnlinkFile(dir.fd(), "a/b/c/file1"));
202 ASSERT_TRUE(fml::UnlinkFile(dir.fd(), "a/b/c/file2"));
203 ASSERT_TRUE(fml::UnlinkDirectory(dir.fd(), "a/b/c/d"));
204 ASSERT_TRUE(fml::UnlinkDirectory(dir.fd(), "a/b/c"));
205 ASSERT_TRUE(fml::UnlinkDirectory(dir.fd(), "a/b"));
206 ASSERT_TRUE(fml::UnlinkDirectory(dir.fd(), "a"));
207}
208
209TEST(FileTest, CanStopVisitEarly) {
211
212 {
213 auto d = fml::CreateDirectory(dir.fd(), {"a", "b", "c", "d"},
215 ASSERT_TRUE(d.is_valid());
216 }
217
218 std::set<std::string> names;
219 fml::FileVisitor visitor = [&names](const fml::UniqueFD& directory,
220 const std::string& filename) {
221 names.insert(filename);
222 return filename == "c" ? false : true; // stop if c is found
223 };
224
225 // Check the d is not visited as we stop at c.
226 ASSERT_FALSE(fml::VisitFilesRecursively(dir.fd(), visitor));
227 ASSERT_EQ(names, std::set<std::string>({"a", "b", "c"}));
228
229 // Cleanup.
230 ASSERT_TRUE(fml::UnlinkDirectory(dir.fd(), "a/b/c/d"));
231 ASSERT_TRUE(fml::UnlinkDirectory(dir.fd(), "a/b/c"));
232 ASSERT_TRUE(fml::UnlinkDirectory(dir.fd(), "a/b"));
233 ASSERT_TRUE(fml::UnlinkDirectory(dir.fd(), "a"));
234}
235
236TEST(FileTest, AtomicWriteTest) {
238
239 const std::string contents = "These are my contents.";
240
241 auto data = std::make_unique<fml::DataMapping>(
242 std::vector<uint8_t>{contents.begin(), contents.end()});
243
244 // Write.
245 ASSERT_TRUE(fml::WriteAtomically(dir.fd(), "precious_data", *data));
246
247 // Read and verify.
248 ASSERT_EQ(contents,
249 ReadStringFromFile(fml::OpenFile(dir.fd(), "precious_data", false,
251
252 // Cleanup.
253 ASSERT_TRUE(fml::UnlinkFile(dir.fd(), "precious_data"));
254}
255
256TEST(FileTest, IgnoreBaseDirWhenPathIsAbsolute) {
258
259 // Make an absolute path.
260 std::string filename = "filename.txt";
261 std::string full_path =
263
264 const std::string contents = "These are my contents.";
265 auto data = std::make_unique<fml::DataMapping>(
266 std::vector<uint8_t>{contents.begin(), contents.end()});
267
268 // Write.
269 ASSERT_TRUE(fml::WriteAtomically(dir.fd(), full_path.c_str(), *data));
270
271 // Test existence.
272 ASSERT_TRUE(fml::FileExists(dir.fd(), full_path.c_str()));
273
274 // Read and verify.
275 ASSERT_EQ(contents,
276 ReadStringFromFile(fml::OpenFile(dir.fd(), full_path.c_str(), false,
278
279 // Cleanup.
280 ASSERT_TRUE(fml::UnlinkFile(dir.fd(), full_path.c_str()));
281}
282
283TEST(FileTest, EmptyMappingTest) {
285
286 {
287 auto file = fml::OpenFile(dir.fd(), "my_contents", true,
289
290 fml::FileMapping mapping(file);
291 ASSERT_TRUE(mapping.IsValid());
292 ASSERT_EQ(mapping.GetSize(), 0ul);
293 ASSERT_EQ(mapping.GetMapping(), nullptr);
294 }
295
296 ASSERT_TRUE(fml::UnlinkFile(dir.fd(), "my_contents"));
297}
298
299TEST(FileTest, MappingDontNeedSafeTest) {
301
302 {
303 auto file = fml::OpenFile(dir.fd(), "my_contents", true,
305 WriteStringToFile(file, "some content");
306 }
307
308 {
309 auto file = fml::OpenFile(dir.fd(), "my_contents", false,
311 fml::FileMapping mapping(file);
312 ASSERT_TRUE(mapping.IsValid());
313 ASSERT_EQ(mapping.GetMutableMapping(), nullptr);
314 ASSERT_TRUE(mapping.IsDontNeedSafe());
315 }
316
317 {
318 auto file = fml::OpenFile(dir.fd(), "my_contents", false,
322 ASSERT_TRUE(mapping.IsValid());
323 ASSERT_NE(mapping.GetMutableMapping(), nullptr);
324 ASSERT_FALSE(mapping.IsDontNeedSafe());
325 }
326 ASSERT_TRUE(fml::UnlinkFile(dir.fd(), "my_contents"));
327}
328
329TEST(FileTest, FileTestsWork) {
331 ASSERT_TRUE(dir.fd().is_valid());
332 const char* filename = "some.txt";
333 auto fd =
334 fml::OpenFile(dir.fd(), filename, true, fml::FilePermission::kWrite);
335 ASSERT_TRUE(fd.is_valid());
336 fd.reset();
337 ASSERT_TRUE(fml::FileExists(dir.fd(), filename));
338 ASSERT_TRUE(
339 fml::IsFile(fml::paths::JoinPaths({dir.path(), filename}).c_str()));
340 ASSERT_TRUE(fml::UnlinkFile(dir.fd(), filename));
341}
342
343TEST(FileTest, FileTestsSupportsUnicode) {
345 ASSERT_TRUE(dir.fd().is_valid());
346 const char* filename = u8"äëïöüテスト☃";
347 auto fd =
348 fml::OpenFile(dir.fd(), filename, true, fml::FilePermission::kWrite);
349 ASSERT_TRUE(fd.is_valid());
350 fd.reset();
351 ASSERT_TRUE(fml::FileExists(dir.fd(), filename));
352 ASSERT_TRUE(
353 fml::IsFile(fml::paths::JoinPaths({dir.path(), filename}).c_str()));
354 ASSERT_TRUE(fml::UnlinkFile(dir.fd(), filename));
355}
#define TEST(S, s, D, expected)
int count
const SkPath & path() const
Definition path.h:117
size_t GetSize() const override
bool IsDontNeedSafe() const override
uint8_t * GetMutableMapping()
Definition mapping.cc:16
bool IsValid() const
const uint8_t * GetMapping() const override
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition main.cc:19
static bool WriteStringToFile(const fml::UniqueFD &fd, const std::string &contents)
static std::string ReadStringFromFile(const fml::UniqueFD &fd)
std::string JoinPaths(std::initializer_list< std::string > components)
Definition paths.cc:14
std::string AbsolutePath(const std::string &path)
bool VisitFiles(const fml::UniqueFD &directory, const FileVisitor &visitor)
bool WriteAtomically(const fml::UniqueFD &base_directory, const char *file_name, const Mapping &mapping)
fml::UniqueFD OpenDirectory(const char *path, bool create_if_necessary, FilePermission permission)
Definition file_posix.cc:97
bool IsFile(const std::string &path)
std::string CreateTemporaryDirectory()
Definition file_posix.cc:25
bool UnlinkDirectory(const char *path)
bool TruncateFile(const fml::UniqueFD &file, size_t size)
static fml::UniqueFD CreateDirectory(const fml::UniqueFD &base_directory, const std::vector< std::string > &components, FilePermission permission, size_t index)
Definition file.cc:12
bool FileExists(const fml::UniqueFD &base_directory, const char *path)
bool UnlinkFile(const char *path)
fml::UniqueFD OpenFile(const char *path, bool create_if_necessary, FilePermission permission)
This can open a directory on POSIX, but not on Windows.
Definition file_posix.cc:66
bool VisitFilesRecursively(const fml::UniqueFD &directory, const FileVisitor &visitor)
Definition file.cc:71
std::function< bool(const fml::UniqueFD &directory, const std::string &filename)> FileVisitor
Definition file.h:98