Flutter Engine
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 
16 static 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 
35 static 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 
46 TEST(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 
56 TEST(FileTest, ScopedTempDirIsValid) {
58  ASSERT_TRUE(dir.fd().is_valid());
59 }
60 
61 TEST(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 
72 TEST(FileTest, CanTruncateAndWrite) {
74  ASSERT_TRUE(dir.fd().is_valid());
75 
76  std::string contents = "some contents here";
77 
78  {
79  auto fd = fml::OpenFile(dir.fd(), "some.txt", true,
81  ASSERT_TRUE(fd.is_valid());
82 
83  ASSERT_TRUE(fml::TruncateFile(fd, contents.size()));
84 
86  ASSERT_EQ(mapping.GetSize(), contents.size());
87  ASSERT_NE(mapping.GetMutableMapping(), nullptr);
88 
89  ::memcpy(mapping.GetMutableMapping(), contents.data(), contents.size());
90  }
91 
92  {
93  auto fd =
94  fml::OpenFile(dir.fd(), "some.txt", false, fml::FilePermission::kRead);
95  ASSERT_TRUE(fd.is_valid());
96 
97  fml::FileMapping mapping(fd);
98  ASSERT_EQ(mapping.GetSize(), contents.size());
99 
100  ASSERT_EQ(0,
101  ::memcmp(mapping.GetMapping(), contents.data(), contents.size()));
102  }
103 
104  fml::UnlinkFile(dir.fd(), "some.txt");
105 }
106 
107 TEST(FileTest, CreateDirectoryStructure) {
109 
110  std::string contents = "These are my contents";
111  {
112  auto sub = fml::CreateDirectory(dir.fd(), {"a", "b", "c"},
114  ASSERT_TRUE(sub.is_valid());
115  auto file = fml::OpenFile(sub, "my_contents", true,
117  ASSERT_TRUE(file.is_valid());
118  ASSERT_TRUE(WriteStringToFile(file, contents));
119  }
120 
121  const char* file_path = "a/b/c/my_contents";
122 
123  {
124  auto contents_file =
125  fml::OpenFile(dir.fd(), file_path, false, fml::FilePermission::kRead);
126  ASSERT_EQ(ReadStringFromFile(contents_file), contents);
127  }
128 
129  // Cleanup.
130  ASSERT_TRUE(fml::UnlinkFile(dir.fd(), file_path));
131  ASSERT_TRUE(fml::UnlinkDirectory(dir.fd(), "a/b/c"));
132  ASSERT_TRUE(fml::UnlinkDirectory(dir.fd(), "a/b"));
133  ASSERT_TRUE(fml::UnlinkDirectory(dir.fd(), "a"));
134 }
135 
136 TEST(FileTest, VisitFilesCanBeCalledTwice) {
138 
139  {
140  auto file = fml::OpenFile(dir.fd(), "my_contents", true,
142  ASSERT_TRUE(file.is_valid());
143  }
144 
145  int count;
146  fml::FileVisitor count_visitor = [&count](const fml::UniqueFD& directory,
147  const std::string& filename) {
148  count += 1;
149  return true;
150  };
151  count = 0;
152  fml::VisitFiles(dir.fd(), count_visitor);
153  ASSERT_EQ(count, 1);
154 
155  // Without `rewinddir` in `VisitFiles`, the following check would fail.
156  count = 0;
157  fml::VisitFiles(dir.fd(), count_visitor);
158  ASSERT_EQ(count, 1);
159 
160  ASSERT_TRUE(fml::UnlinkFile(dir.fd(), "my_contents"));
161 }
162 
163 TEST(FileTest, CanListFilesRecursively) {
165 
166  {
167  auto c = fml::CreateDirectory(dir.fd(), {"a", "b", "c"},
169  ASSERT_TRUE(c.is_valid());
170  auto file1 =
172  auto file2 =
175  ASSERT_TRUE(d.is_valid());
176  auto file3 =
178  ASSERT_TRUE(file1.is_valid());
179  ASSERT_TRUE(file2.is_valid());
180  ASSERT_TRUE(file3.is_valid());
181  }
182 
183  std::set<std::string> names;
184  fml::FileVisitor visitor = [&names](const fml::UniqueFD& directory,
185  const std::string& filename) {
186  names.insert(filename);
187  return true;
188  };
189 
190  fml::VisitFilesRecursively(dir.fd(), visitor);
191  ASSERT_EQ(names, std::set<std::string>(
192  {"a", "b", "c", "d", "file1", "file2", "file3"}));
193 
194  // Cleanup.
195  ASSERT_TRUE(fml::UnlinkFile(dir.fd(), "a/b/c/d/file3"));
196  ASSERT_TRUE(fml::UnlinkFile(dir.fd(), "a/b/c/file1"));
197  ASSERT_TRUE(fml::UnlinkFile(dir.fd(), "a/b/c/file2"));
198  ASSERT_TRUE(fml::UnlinkDirectory(dir.fd(), "a/b/c/d"));
199  ASSERT_TRUE(fml::UnlinkDirectory(dir.fd(), "a/b/c"));
200  ASSERT_TRUE(fml::UnlinkDirectory(dir.fd(), "a/b"));
201  ASSERT_TRUE(fml::UnlinkDirectory(dir.fd(), "a"));
202 }
203 
204 TEST(FileTest, CanStopVisitEarly) {
206 
207  {
208  auto d = fml::CreateDirectory(dir.fd(), {"a", "b", "c", "d"},
210  ASSERT_TRUE(d.is_valid());
211  }
212 
213  std::set<std::string> names;
214  fml::FileVisitor visitor = [&names](const fml::UniqueFD& directory,
215  const std::string& filename) {
216  names.insert(filename);
217  return filename == "c" ? false : true; // stop if c is found
218  };
219 
220  // Check the d is not visited as we stop at c.
221  ASSERT_FALSE(fml::VisitFilesRecursively(dir.fd(), visitor));
222  ASSERT_EQ(names, std::set<std::string>({"a", "b", "c"}));
223 
224  // Cleanup.
225  ASSERT_TRUE(fml::UnlinkDirectory(dir.fd(), "a/b/c/d"));
226  ASSERT_TRUE(fml::UnlinkDirectory(dir.fd(), "a/b/c"));
227  ASSERT_TRUE(fml::UnlinkDirectory(dir.fd(), "a/b"));
228  ASSERT_TRUE(fml::UnlinkDirectory(dir.fd(), "a"));
229 }
230 
231 #if OS_WIN
232 #define AtomicWriteTest DISABLED_AtomicWriteTest
233 #else
234 #define AtomicWriteTest AtomicWriteTest
235 #endif
236 TEST(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 
256 TEST(FileTest, EmptyMappingTest) {
258 
259  {
260  auto file = fml::OpenFile(dir.fd(), "my_contents", true,
262 
263  fml::FileMapping mapping(file);
264  ASSERT_TRUE(mapping.IsValid());
265  ASSERT_EQ(mapping.GetSize(), 0ul);
266  ASSERT_EQ(mapping.GetMapping(), nullptr);
267  }
268 
269  ASSERT_TRUE(fml::UnlinkFile(dir.fd(), "my_contents"));
270 }
271 
272 TEST(FileTest, FileTestsWork) {
274  ASSERT_TRUE(dir.fd().is_valid());
275  const char* filename = "some.txt";
276  auto fd =
277  fml::OpenFile(dir.fd(), filename, true, fml::FilePermission::kWrite);
278  ASSERT_TRUE(fd.is_valid());
279  fd.reset();
280  ASSERT_TRUE(fml::FileExists(dir.fd(), filename));
281  ASSERT_TRUE(
282  fml::IsFile(fml::paths::JoinPaths({dir.path(), filename}).c_str()));
283  ASSERT_TRUE(fml::UnlinkFile(dir.fd(), filename));
284 }
285 
286 TEST(FileTest, FileTestsSupportsUnicode) {
288  ASSERT_TRUE(dir.fd().is_valid());
289  const char* filename = u8"äëïöüテスト☃";
290  auto fd =
291  fml::OpenFile(dir.fd(), filename, true, fml::FilePermission::kWrite);
292  ASSERT_TRUE(fd.is_valid());
293  fd.reset();
294  ASSERT_TRUE(fml::FileExists(dir.fd(), filename));
295  ASSERT_TRUE(
296  fml::IsFile(fml::paths::JoinPaths({dir.path(), filename}).c_str()));
297  ASSERT_TRUE(fml::UnlinkFile(dir.fd(), filename));
298 }
const std::string & path() const
Definition: file.h:146
static std::string ReadStringFromFile(const fml::UniqueFD &fd)
std::string CreateTemporaryDirectory()
Definition: file_posix.cc:23
std::function< bool(const fml::UniqueFD &directory, const std::string &filename)> FileVisitor
Definition: file.h:98
#define AtomicWriteTest
TEST(FileTest, CreateTemporaryAndUnlink)
void reset(const T &value=Traits::InvalidValue())
Definition: unique_object.h:62
bool is_valid() const
Definition: unique_object.h:89
fml::UniqueFD OpenDirectory(const char *path, bool create_if_necessary, FilePermission permission)
Definition: file_posix.cc:94
std::string JoinPaths(std::initializer_list< std::string > components)
Definition: paths.cc:14
bool VisitFiles(const fml::UniqueFD &directory, const FileVisitor &visitor)
Definition: file_posix.cc:233
const UniqueFD & fd()
Definition: file.h:147
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:180
static bool WriteStringToFile(const fml::UniqueFD &fd, const std::string &contents)
bool VisitFilesRecursively(const fml::UniqueFD &directory, const FileVisitor &visitor)
Definition: file.cc:71
const uint8_t * GetMapping() const override
size_t GetSize() const override
bool UnlinkFile(const char *path)
Definition: file_posix.cc:168
bool IsFile(const std::string &path)
Definition: file_posix.cc:143
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:64
bool TruncateFile(const fml::UniqueFD &file, size_t size)
Definition: file_posix.cc:152
bool WriteAtomically(const fml::UniqueFD &base_directory, const char *file_name, const Mapping &mapping)
Definition: file_posix.cc:188
bool UnlinkDirectory(const char *path)
Definition: file_posix.cc:160