Flutter Engine
The Flutter Engine
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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}
int count
Definition: FontMgrTest.cpp:50
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)
TEST(FileTest, CreateTemporaryAndUnlink)
static const char *const names[]
Definition: symbols.cc:24
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets dir
Definition: switches.h:145
std::string JoinPaths(std::initializer_list< std::string > components)
Definition: paths.cc:14
std::string AbsolutePath(const std::string &path)
Definition: paths_posix.cc:29
bool VisitFiles(const fml::UniqueFD &directory, const FileVisitor &visitor)
Definition: file_posix.cc:236
bool WriteAtomically(const fml::UniqueFD &base_directory, const char *file_name, const Mapping &mapping)
Definition: file_posix.cc:191
fml::UniqueFD OpenDirectory(const char *path, bool create_if_necessary, FilePermission permission)
Definition: file_posix.cc:97
bool IsFile(const std::string &path)
Definition: file_posix.cc:146
std::string CreateTemporaryDirectory()
Definition: file_posix.cc:25
bool UnlinkDirectory(const char *path)
Definition: file_posix.cc:163
bool TruncateFile(const fml::UniqueFD &file, size_t size)
Definition: file_posix.cc:155
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)
Definition: file_posix.cc:183
bool UnlinkFile(const char *path)
Definition: file_posix.cc:171
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
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63